diff -urN -X bkexcl linux-2.4.22/Documentation/Configure.help linuxppc-2.4/Documentation/Configure.help --- linux-2.4.22/Documentation/Configure.help 2003-08-26 14:24:57.000000000 +1000 +++ linuxppc-2.4/Documentation/Configure.help 2003-08-26 11:57:10.000000000 +1000 @@ -109,6 +109,15 @@ like MGA monitors that you are very unlikely to see on today's systems. +Prompt for advanced kernel configuration options +CONFIG_ADVANCED_OPTIONS + This option will enable prompting for a variety of advanced kernel + configuration options. These options can cause the kernel to not + work if they are set incorrectly, but can be used to optimize certain + aspects of kernel memory management. + + Unless you know what you are doing you *should not* enable this option. + Symmetric Multi-Processing support CONFIG_SMP This enables support for systems with more than one CPU. If you have @@ -5194,6 +5203,16 @@ If you plan to use the LCD display with your SA-1100 system, say Y here. +IBM Beech LCD Controller Support +CONFIG_FB_IBMLCDC + This is a framebuffer device for the IBM 405LP/Beech LCD Controller. + See for information on framebuffer + devices. + + If you plan to use the LCD display with your 405LP/Beech system, say + Y here. You must also enable generic framebuffer support under the + Console configuration tab. + Advanced low level driver options CONFIG_FBCON_ADVANCED The frame buffer console uses character drawing routines that are @@ -22970,6 +22989,14 @@ CONFIG_WALNUT Select Walnut if you have an IBM 405GP "Walnut" Evaluation Board. +Beech +CONFIG_BEECH + Select Beech if you have an IBM 405LP "Beech" Evaluation Board. + +Xilinx-ML300 +CONFIG_XILINX_ML300 + Select Xilinx-ML300 if you have a Xilinx ML300 evaluation board. + Workarounds for PPC601 bugs CONFIG_PPC601_SYNC_FIX Some versions of the PPC601 (the first PowerPC chip) have bugs which @@ -23085,6 +23112,12 @@ More information is available at: . +IBM Spruce Baud Clock Support +CONFIG_SPRUCE_BAUD_33M + IBM Spruce reference platforms are equipped with either a 30Mhz or + 33Mhz CPC700 input oscillator. Enable this option only if you + have the 33Mhz oscillator. + AltiVec kernel support CONFIG_ALTIVEC This option enables kernel support for the Altivec extensions to the @@ -23468,6 +23501,38 @@ /proc/sys/dev/mac_hid/mouse_button2_keycode /proc/sys/dev/mac_hid/mouse_button3_keycode +Set high memory pool address +CONFIG_HIGHMEM_START_BOOL + Unless you know what you are doing you *should not* set this option. + + It can be used to override the default PKMAP_BASE address which + is the location of the high memory pool. This can be useful in + optimizing virtual memory usage in a system. + +Set maximum low memory +CONFIG_LOWMEM_SIZE_BOOL + Unless you know what you are doing you *should not* set this option. + + It can be used to override the standard calculated value of + MAX_LOW_MEM. This can be useful in optimizing virtual memory usage + in a system. + +Set custom kernel base address +CONFIG_KERNEL_START_BOOL + Unless you know what you are doing you *should not* set this option. + + It can be used to override the standard PAGE_OFFSET/KERNELBASE + value used by the kernel. This can be useful in controlling + amount of virtual address space available to the kernel. + +Set custom user task size +CONFIG_TASK_SIZE_BOOL + Unless you know what you are doing you *should not* set this option. + + It can be used to override the standard TASK_SIZE value used + by the kernel. This can be useful in controlling amount of + virtual address space available to user tasks. + Enhanced Real Time Clock Support (/dev/rtc) CONFIG_PPC_RTC If you say Y here and create a character special file /dev/rtc with @@ -26405,6 +26470,101 @@ To use this option, you have to check that the "/proc file system support" (CONFIG_PROC_FS) is enabled, too. +Use MDIO for PHY configuration +CONFIG_USE_MDIO + This option specifies whether to use MII autoconfiguration in the + FCC Ethernet driver or not. + +PPC4xx DMA controller support +CONFIG_PPC4xx_DMA + Select this to enable support for the PPC4xx general purpose DMA + controller. + +ttyS0 device +CONFIG_UART0_TTYS0 + This option reverses the mapping between the hardware UART and software + device. Selecting UART0 gives the normal mapping of UART0=ttyS0 and + UART1=ttyS1. Selecting UART1 gives the reverse mapping of UART0=ttyS1 + and UART1=ttyS0. Most people will use UART0. + +PowerPC 405 on-chip ethernet +CONFIG_IBM_OCP_ENET + If you want to use the 405 built-in ethernet select this. + +CONFIG_IBM_OCP_ENET_ERROR_MSG + Enable this option to print verbose debug messages for troubleshooting. + +PowerPC 405 on-chip ethernet -- Number of receive buffers +CONFIG_IBM_OCP_ENET_RX_BUFF + Number of ethernet receive (read) buffers. Unless you know what you + are doing the default should be fine. + +PowerPC 405 on-chip ethernet -- Number of transmit buffers +CONFIG_IBM_OCP_ENET_TX_BUFF + Number of ethernet transmit (write) buffers. Unless you know what + you are doing the default should be fine. + +PowerPC 405 on-chip ethernet -- Amount of bytes to Reserve on a skb +CONFIG_IBM_OCP_ENET_SKB_RES + Many standard ethernet drivers need to reserve 2 bytes of data + on the skb before giving the data ptr to the hardware. This is + so the IP data will be 16-byte aligned when it goes up the stack. + This is a requirement for some processors and it can cause major + slow downs on others. The 405GP dose not have problems with the + misaligned data so the default is 0. If you need to route the + incoming ethernet packets to another device that has alignment + requirements this can help remove a data copy. A value of 2 can + help at getting 16-byte aligned IP data for another device. A + larger value can be used when routing to a IP tunnel device. + Make sure XXX_DESC_SIZE - XXX_SKB_RES >= 1514, or larger if VLANS + are used. + +PPC 405 I2C Algorithm +CONFIG_PPC405_I2C_ALGO + Enable this option to use the built-in I2C on your 405. + +PPC 405 I2C Adapter +CONFIG_PPC405_I2C_ADAP + Enable this option to use the built-in I2C on your 405. + +BM PPC 405 Watchdog Timer +CONFIG_PPC405_WDT + Enable this option to use the built-in watch dog timer on your 405. + +Xilinx Virtex-II Pro on-chip ethernet +CONFIG_XILINX_ENET + If you want to use the Xilinx built-in ethernet select this. + +Xilinx Virtex-II Pro TFT display frame buffer +CONFIG_FB_XILINX + If you want to use the Xilinx built-in TFT display select this. + +Rotate display +CONFIG_FB_XILINX_ROTATE + Say Y here to rotate the TFT output 180 degrees. + +Xilinx Virtex-II Pro I2C +CONFIG_I2C_XILINX + If you want to use the Xilinx built-in I2C select this. + +Xilinx Virtex-II Pro GPIO +CONFIG_XILINX_GPIO + If you want to use the Xilinx built-in GPIO select this. + +Xilinx Virtex-II Pro System ACE +CONFIG_XILINX_SYSACE + Xilinx has a piece of hardware called the System ACE. It interfaces + the processor, the JTAG chain and a CompactFlash. When the system + is reset, System ACE reads a file from the CompactFlash that is used + to initialize the system via the JTAG interface. After the system is + up, the System ACE can be used to give the processor access to the + CompactFlash and JTAG chain. If you want Linux to use the + CompactFlash as a block device, select this. + +Xilinx Virtex-II Pro Touchscreen +CONFIG_XILINX_TS + If you want to use the Xilinx built-in touchscreen select this. + /proc/efi/vars support CONFIG_EFI_VARS If you say Y here, you are able to get EFI (Extensible Firmware diff -urN -X bkexcl linux-2.4.22/Documentation/powerpc/405LP-sleep.txt linuxppc-2.4/Documentation/powerpc/405LP-sleep.txt --- linux-2.4.22/Documentation/powerpc/405LP-sleep.txt 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/Documentation/powerpc/405LP-sleep.txt 2003-06-02 14:37:41.000000000 +1000 @@ -0,0 +1,237 @@ +Hollis Blanchard, IBM Linux Technology Center +6 Jan 2003 + +Bishop Brock, IBM Low-Power Computing Research Center +Revised May, 2003 + +Linux Sleep on the PowerPC 405LP +-------------------------------- + +The IBM 405LP embedded CPU supports three different sleep modes, controlled by +a unit called "APM0". These three hardware modes have been used to specify +six different types of sleep. Not all hardware platforms will necessarily +support all modes of sleep. + +APM has four wakeup interrupt lines which it cascades onto the UIC as irq +24. These "wakeups" function similar to other interrupt sources when the 405LP +is executing normally. When the 405LP enters a sleep mode, however, the only +things that can wake it are these four wakeup lines, and the APM (master) reset +signal. One wakeup line is connected to the RTC interrupt line, allowing wakeup +when an RTC alarm goes off. The other three wakeups are more-or-less +programmable and board-dependent. On both Beech and Arctic2 one of the wakeups +is connected to a physical pushbutton to provide a soft power switch. + +Sleep Modes +----------- + +1. In "clock-suspend" mode the output of the PLL is stopped. No units are +powered down, and so no state is lost. SDRAM must held in self refresh to avoid +losing SDRAM contents. On wakeup, the PLL continues and processing resumes +exactly where it left off. + +2. In "power-down" mode, APM signals the power supply to drop power to the CPU +and integrated peripherals. The Linux implementation subdivides this hardware +mode into four different types of power-down sleep. In simple power-down +mode, all system state is lost, and the system simply reboots when +awakened or reset. Linux also supports a "failsafe-power-down" mode similar to +power-down, except that the APM wakeup mechanism is disabled before going to +sleep. This mode requires a master reset to restart and reboot the system. + +The power-down state can also be used to implement laptop-like suspend modes. +In these modes all important state, including GPRs, SPRs, and DCRs must be +saved by software before entering the sleep mode, and restored after wakeup. +In "suspend" mode SDRAM is not powered off, so state can be saved there as long +as SDRAM is put into self-refresh mode prior to the sleep. In "hibernate" mode +the SDRAM contents are copied to non-volatile storage, and then the entire +system can be powered down. [ This mode has not yet been fully implemented. ] + +When a wakeup from power-down occurs, the chip (CPU and integrated peripherals) +is reset, returning control to the firmware. The firmware must determine +whether this is a wakeup from a normal power-down, leading to a reboot, or +wakeup from a suspend or hibernate, requiring restoration of the system +context. The mechanism used to communicate the sleep mode to the firmware is +discussed further below. + +3. The "standby" mode is a specialized clock-suspend mode unique to the 405LP. +In "standby" mode, APM first freezes the PLL. It then transfers all chip state +(excluding CPU core state and peripheral arrays such as the LCD palette) over +the i2c bus to a nonvolatile storage device (such as an EEPROM). Once that +process is complete, APM signals the power supply to drop power to the CPU and +integrated peripherals. To the external system, standby mode appears to be a +clock stop mode, so , for example, SDRAM must be held in self refresh. + +When a wakeup occurs, APM transfers the saved chip state back over the i2c bus. +The CPU core is reset but held frozen. APM then releases the PLL and processing +continues from the reset vector, returning control to the firmware. The +firmware must determine that this is a wakeup from a standby state by by polling +the APM0_CFG and APM0_SR registers, and taking the appropriate action. + +Firmware Interaction with Linux +------------------------------- + +Note that because the CPU is reset on wakeup in both power-down and standby +modes, the firmware *must* be made APM-aware. Otherwise, a wakeup will simply +reset the chip, losing all state. + +The clock-suspend mode does not require firmware interaction. Linux handles +the clock-stop, and the clock is restarted exactly at the point where Linux +stopped the system. + +On wake from standby, suspend or hibernate, the firmware must know how to +transfer control back to Linux (which is still resident in RAM). The following +structure is used: + +struct wakeup_info { + void (*wakeup_func_phys)(u32 apm0_cfg, u32 apm0_sr); /* physical addr */ + u32 magic; + ... +} + +Firmware is aware only of the first two fields. Linux creates and populates +this structure in memory, and writes the pointer to a well-known location +(currently 0xfc physical) for the firmware to read. (Unfortunately there are +no "scratch" registers provided by the APM unit, requiring used of a +predetermined absolute physical address.) The magic number is intended to be a +simple validator ensuring that the contents of SDRAM have not been corrupted. + +Currently Linux also saves one other value in the wakeup_info structure: the +(physical) stack pointer. + +The contents of the APM0_CFG[SM] field are sufficient to identify a standby +sleep. In order to disambiguate power-down, sleep and hibernate, Linux writes +a flag value in the RTC0_CEN DCR (RTC century register) before initiating the +sleep. If firmware detects the resumption from a power-down sleep, it takes +action based on the flag value it finds in RTC0_CEN. + +Linux Sleep Procedure +--------------------- + +This is a high-level summary; the code is well-documented. Note that entry into +all sleep modes is completely handled by Linux. + +For clock-suspend mode: + + The power-management callbacks of all devices registered with LDM are + invoked. + + Since this is a simple clock-suspend, no CPU or DCR state is saved, and + the clocks are simply stopped after placing SDRAM in self-refresh. + +For power-down and failsafe-power-down mode: + + Since this is a simple power-down, no CPU or DCR state is saved, and + APM is commanded to power-down the system. + +For suspend and standby modes: + + The power-management callbacks of all devices registered with LDM are + invoked. + + For suspend mode, Linux marks the mode for the firmware by a flag in + RTC0_CEN. + + One page is allocated in which to save some CPU registers and selected + DCRs required by the mode. For suspend mode, some system-level DCRs + not associated with a specific device driver (e.g., EBC, UIC) are + saved. In standby mode all registers external to the CPU are saved and + restored by the hardware mechanism, and only the APM_CFG and APM0_SR + registers and the SDRAM_CFG register will be touched by the firmware on + wakeup. + + The non-volatile GPRs, all 64 TLB entries, and quite a few SPRs like LR, + MSR, and EVPR are saved to the stack in an assembly routine. The registers + saved here are those that must be restored before returning into C code on + wakeup; i.e. they cannot be saved and restored in C. + + SDRAM is placed in self-refresh before initiating power-down. + + +The final code is touched into the I-cache so that SDRAM can be put into +self-refresh. The sleep command is written to the APM unit, and then the +processor spins in a bdnz loop until either: +1. the APM unit freezes the processor +2. the CTR register decrements to zero, indicating sleep did not take place. +This could happen if the wakeup were already asserted at the time the sleep +command was issued. + +Firmware/Linux Wakeup Procedure +------------------------------- + +If the firmware detects the resumption from a standby sleep, the firmware +should only bring the SDRAM out of self-refresh, and then restart Linux as +explained below. If firmware detects the resumption from a suspend sleep, the +firmware should perform the normal board setup (including establishing the +default operating point) up to re-enabling the SDRAM controller, and then +resume Linux as explained below. In any other case the firmware reboots the +system. + +To restart Linux, after validating the magic number the firmware jumps to the +address specified by wakeup_info->wakeup_func_phys . The processor *must* be +in untranslated mode (MSR:IR/DR = 0) at this point. + +Back in Linux, the stack pointer is immediately restored from +wakeup_info->wakeup_sp_phys so that SPRs can be loaded and restored from the +stack. Finally, the non-volatile GPRs are restored, and control returns via an +rfi to the C function executed before sleep, returning to translated mode +(MSR:IR/DR = 1). The C function continues to restore non-essential registers +which were not handled in assembly. + +The RTC remains active during sleep, but the timebase does not, and obviously +jiffies has not been incremented. To restore the timebase, the pre-sleep RTC +time is subtracted from the current RTC time to get the difference in seconds, +and then the timebase is incremented by + +(seconds elapsed) * HZ * tb_ticks_per_jiffy + + +User Interfaces +--------------- + +One of the interfaces to the sleep code is via sysctl (and its /proc +interface). The following /proc items are present under /proc/sys/sleep : + suspend: (write-only) + triggers sleep + mode: (string) + contains one of "clock-suspend", "power-down", + "failsafe-power-down", "suspend", "hibernate" or "standby", + indicating the sleep mode to be used. + pm_alarm: (string) + See below. + cdiv: (integer, for debugging only) + allows the user to override the "cdiv" field of APM0_CFG, which + controls the speed at which CPU state is transferred over the i2c bus. + watchdog: (integer, for debugging only) + allows user to disable the APM watchdog. Defaults to 1 (meaning the + watchdog is enabled). + + +The pm_alarm entry can be used to program a wakeup using the RTC alarm +mechanism. If enabled at system shutdown, this alarm will wake up the +processor from any of the sleep modes except failsafe-power-down. To program an +alarm, write an 8-character string of the form hh:mm:ss (using a 24-hour clock) +to /proc/sys/sleep/pm_alarm. To indicate a don't-care value use an illegal +value, e.g., 99. For example, writing "09:30:99" sets the alarm for 9:30 AM. +Write the string "clear" to clear the alarm. Reading the /proc file will +return the current alarm time followed by either "clear", "armed" or "expired". +This alarm mechanism only guarantees that the system will be awake when the +alarm goes off. NB: The RTC and system clocks may not be in sync; see 'man +hwclock'. + +A generic button driver for a soft power switch connected to an APM wakeup +input is also provided. Pressing and releasing the button will initiate sleep +using the current 'mode'. If the button is pressed and held continuously for 5 +seconds the failsafe-power-down mode is entered. Pressing the button on a +sleeping system will wake it up (unless it was forced off using the +failsafe-power-down mode). + + +Other Issues +------------ + +Standby mode can only be safely entered and exited in a +well-constructed PLL bypass operating point that must be supplied by +board support code. If this state is not defined then "clocksuspend" +mode is substituted for "standby". We assume that the EEPROM is +properly programmed for standby mode. + + diff -urN -X bkexcl linux-2.4.22/arch/ppc/8xx_io/commproc.c linuxppc-2.4/arch/ppc/8xx_io/commproc.c --- linux-2.4.22/arch/ppc/8xx_io/commproc.c 2003-02-15 08:07:09.000000000 +1100 +++ linuxppc-2.4/arch/ppc/8xx_io/commproc.c 2003-03-21 08:08:33.000000000 +1100 @@ -53,7 +53,8 @@ }; static struct cpm_action cpm_vecs[CPMVEC_NR]; static void cpm_interrupt(int irq, void * dev, struct pt_regs * regs); -static void cpm_error_interrupt(int irq, void *, struct pt_regs * regs); +static void cpm_error_interrupt(int irq, void *dev, struct pt_regs *regs); +static void alloc_host_memory(void); /* Define a table of names to identify CPM interrupt handlers in * /proc/interrupts. @@ -104,7 +105,7 @@ }; void -m8xx_cpm_reset(uint host_page_addr) +m8xx_cpm_reset() { volatile immap_t *imp; volatile cpm8xx_t *commproc; @@ -130,7 +131,7 @@ * this is what we realy want for some applications, but the * manual recommends it. * Bit 25, FAM can also be set to use FEC aggressive mode (860T). - */ + */ imp->im_siu_conf.sc_sdcr = 1; /* Reclaim the DP memory for our use. @@ -138,25 +139,23 @@ dp_alloc_base = CPM_DATAONLY_BASE; dp_alloc_top = dp_alloc_base + CPM_DATAONLY_SIZE; - /* Set the host page for allocation. + /* Tell everyone where the comm processor resides. */ - host_buffer = host_page_addr; /* Host virtual page address */ - host_end = host_page_addr + PAGE_SIZE; + cpmp = (cpm8xx_t *)commproc; +} - /* We need to get this page early, so I have to do it the - * hard way. - */ - if (get_pteptr(&init_mm, host_page_addr, &pte)) { - pte_val(*pte) |= _PAGE_NO_CACHE; - flush_tlb_page(init_mm.mmap, host_buffer); - } - else { - panic("Huh? No CPM host page?"); - } +/* We used to do this earlier, but have to postpone as long as possible + * to ensure the kernel VM is now running. + */ +static void +alloc_host_memory() +{ + uint physaddr; - /* Tell everyone where the comm processor resides. + /* Set the host page for allocation. */ - cpmp = (cpm8xx_t *)commproc; + host_buffer = (uint)consistent_alloc(GFP_KERNEL, PAGE_SIZE, &physaddr); + host_end = host_buffer + PAGE_SIZE; } /* This is called during init_IRQ. We used to do it above, but this @@ -321,6 +320,9 @@ { uint retloc; + if (host_buffer == 0) + alloc_host_memory(); + if ((host_buffer + size) >= host_end) return(0); diff -urN -X bkexcl linux-2.4.22/arch/ppc/8xx_io/enet.c linuxppc-2.4/arch/ppc/8xx_io/enet.c --- linux-2.4.22/arch/ppc/8xx_io/enet.c 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/8xx_io/enet.c 2003-07-25 02:06:49.000000000 +1000 @@ -137,6 +137,11 @@ cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ cbd_t *dirty_tx; /* The ring entries to be free()ed. */ scc_t *sccp; + + /* Virtual addresses for the receive buffers because we can't + * do a __va() on them anymore. + */ + unsigned char *rx_vaddr[RX_RING_SIZE]; struct net_device_stats stats; uint tx_free; spinlock_t lock; @@ -498,7 +503,7 @@ skb->dev = dev; skb_put(skb,pkt_len-4); /* Make room */ eth_copy_and_sum(skb, - (unsigned char *)__va(bdp->cbd_bufaddr), + cep->rx_vaddr[bdp - cep->rx_bd_base], pkt_len-4, 0); skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); @@ -632,10 +637,9 @@ { struct net_device *dev; struct scc_enet_private *cep; - int i, j; - unsigned char *eap; - unsigned long mem_addr; - pte_t *pte; + int i, j, k; + unsigned char *eap, *ba; + dma_addr_t mem_addr; bd_t *bd; volatile cbd_t *bdp; volatile cpm8xx_t *cp; @@ -826,24 +830,21 @@ bdp->cbd_sc |= BD_SC_WRAP; bdp = cep->rx_bd_base; + k = 0; for (i=0; icbd_sc = BD_ENET_RX_EMPTY | BD_ENET_RX_INTR; - bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_bufaddr = mem_addr; + cep->rx_vaddr[k++] = ba; mem_addr += CPM_ENET_RX_FRSIZE; + ba += CPM_ENET_RX_FRSIZE; bdp++; } } diff -urN -X bkexcl linux-2.4.22/arch/ppc/8xx_io/fec.c linuxppc-2.4/arch/ppc/8xx_io/fec.c --- linux-2.4.22/arch/ppc/8xx_io/fec.c 2003-07-30 23:33:24.000000000 +1000 +++ linuxppc-2.4/arch/ppc/8xx_io/fec.c 2003-08-05 09:59:45.000000000 +1000 @@ -161,7 +161,12 @@ cbd_t *tx_bd_base; cbd_t *cur_rx, *cur_tx; /* The next free ring entry */ cbd_t *dirty_tx; /* The ring entries to be free()ed. */ - scc_t *sccp; + + /* Virtual addresses for the receive buffers because we can't + * do a __va() on them anymore. + */ + unsigned char *rx_vaddr[RX_RING_SIZE]; + struct net_device_stats stats; uint tx_free; spinlock_t lock; @@ -587,7 +592,7 @@ fep->stats.rx_packets++; pkt_len = bdp->cbd_datlen; fep->stats.rx_bytes += pkt_len; - data = (__u8*)__va(bdp->cbd_bufaddr); + data = fep->rx_vaddr[bdp - fep->rx_bd_base]; /* This does 16 byte alignment, exactly what we need. * The packet length includes FCS, but we don't want to @@ -602,9 +607,7 @@ } else { skb->dev = dev; skb_put(skb,pkt_len-4); /* Make room */ - eth_copy_and_sum(skb, - (unsigned char *)__va(bdp->cbd_bufaddr), - pkt_len-4, 0); + eth_copy_and_sum(skb, data, pkt_len-4, 0); skb->protocol=eth_type_trans(skb,dev); netif_rx(skb); } @@ -1700,10 +1703,9 @@ { struct net_device *dev; struct fec_enet_private *fep; - int i, j; - unsigned char *eap, *iap; + int i, j, k; + unsigned char *eap, *iap, *ba; unsigned long mem_addr; - pte_t *pte; volatile cbd_t *bdp; cbd_t *cbd_base; volatile immap_t *immap; @@ -1774,14 +1776,7 @@ printk("FEC initialization failed.\n"); return 1; } - mem_addr = __get_free_page(GFP_KERNEL); - cbd_base = (cbd_t *)mem_addr; - - /* Make it uncached. - */ - pte = va_to_pte(mem_addr); - pte_val(*pte) |= _PAGE_NO_CACHE; - flush_tlb_page(init_mm.mmap, mem_addr); + cbd_base = (cbd_t *)consistent_alloc(GFP_KERNEL, PAGE_SIZE, &mem_addr); /* Set receive and transmit descriptor base. */ @@ -1793,24 +1788,21 @@ /* Initialize the receive buffer descriptors. */ bdp = fep->rx_bd_base; + k = 0; for (i=0; icbd_sc = BD_ENET_RX_EMPTY; - bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_bufaddr = mem_addr; + fep->rx_vaddr[k++] = ba; mem_addr += FEC_ENET_RX_FRSIZE; + ba += FEC_ENET_RX_FRSIZE; bdp++; } } @@ -1974,8 +1966,8 @@ /* Set receive and transmit descriptor base. */ - fecp->fec_r_des_start = __pa((uint)(fep->rx_bd_base)); - fecp->fec_x_des_start = __pa((uint)(fep->tx_bd_base)); + fecp->fec_r_des_start = iopa((uint)(fep->rx_bd_base)); + fecp->fec_x_des_start = iopa((uint)(fep->tx_bd_base)); fep->dirty_tx = fep->cur_tx = fep->tx_bd_base; fep->tx_free = TX_RING_SIZE; diff -urN -X bkexcl linux-2.4.22/arch/ppc/8xx_io/uart.c linuxppc-2.4/arch/ppc/8xx_io/uart.c --- linux-2.4.22/arch/ppc/8xx_io/uart.c 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/8xx_io/uart.c 2003-07-25 02:06:49.000000000 +1000 @@ -218,6 +218,12 @@ cbd_t *rx_cur; cbd_t *tx_bd_base; cbd_t *tx_cur; + + /* Virtual addresses for the FIFOs because we can't __va() a + * physical address anymore. + */ + unsigned char *rx_va_base; + unsigned char *tx_va_base; } ser_info_t; static struct console sercons = { @@ -394,7 +400,7 @@ /* Get the number of characters and the buffer pointer. */ i = bdp->cbd_datlen; - cp = (unsigned char *)__va(bdp->cbd_bufaddr); + cp = info->rx_va_base + ((bdp - info->rx_bd_base) * RX_BUF_SIZE); status = bdp->cbd_sc; #ifdef CONFIG_KGDB @@ -1042,6 +1048,7 @@ { ser_info_t *info = (ser_info_t *)tty->driver_data; volatile cbd_t *bdp; + unsigned char *cp; if (serial_paranoia_check(info, tty->device, "rs_put_char")) return; @@ -1052,7 +1059,8 @@ bdp = info->tx_cur; while (bdp->cbd_sc & BD_SC_READY); - *((char *)__va(bdp->cbd_bufaddr)) = ch; + cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); + *cp = ch; bdp->cbd_datlen = 1; bdp->cbd_sc |= BD_SC_READY; @@ -1073,6 +1081,7 @@ int c, ret = 0; ser_info_t *info = (ser_info_t *)tty->driver_data; volatile cbd_t *bdp; + unsigned char *cp; #ifdef CONFIG_KGDB_CONSOLE /* Try to let stub handle output. Returns true if it did. */ @@ -1099,14 +1108,15 @@ break; } + cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); if (from_user) { - if (copy_from_user(__va(bdp->cbd_bufaddr), buf, c)) { + if (copy_from_user((void *)cp, buf, c)) { if (!ret) ret = -EFAULT; break; } } else { - memcpy(__va(bdp->cbd_bufaddr), buf, c); + memcpy((void *)cp, buf, c); } bdp->cbd_datlen = c; @@ -1181,6 +1191,7 @@ static void rs_8xx_send_xchar(struct tty_struct *tty, char ch) { volatile cbd_t *bdp; + unsigned char *cp; ser_info_t *info = (ser_info_t *)tty->driver_data; @@ -1190,7 +1201,8 @@ bdp = info->tx_cur; while (bdp->cbd_sc & BD_SC_READY); - *((char *)__va(bdp->cbd_bufaddr)) = ch; + cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); + *cp = ch; bdp->cbd_datlen = 1; bdp->cbd_sc |= BD_SC_READY; @@ -2217,6 +2229,11 @@ #ifdef CONFIG_SERIAL_CONSOLE +/* I need this just so I can store the virtual addresses and have + * common functions for the early console printing. + */ +static ser_info_t consinfo; + /* * Print a string to the serial port trying not to disturb any possible * real use of the port... @@ -2249,6 +2266,8 @@ /* Get the address of the host memory buffer. */ bdp = bdbase = (cbd_t *)&cpmp->cp_dpmem[up->smc_tbase]; + + info = &consinfo; } /* @@ -2276,7 +2295,7 @@ if ((uint)(bdp->cbd_bufaddr) > (uint)IMAP_ADDR) cp = (u_char *)(bdp->cbd_bufaddr); else - cp = __va(bdp->cbd_bufaddr); + cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); *cp = *s; bdp->cbd_datlen = 1; @@ -2290,7 +2309,7 @@ /* if a LF, also do CR... */ if (*s == 10) { while (bdp->cbd_sc & BD_SC_READY); - cp = __va(bdp->cbd_bufaddr); + cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); *cp = 13; bdp->cbd_datlen = 1; bdp->cbd_sc |= BD_SC_READY; @@ -2358,10 +2377,13 @@ * If the port has been initialized for general use, we must * use information from the port structure. */ - if ((info = (ser_info_t *)ser->info)) + if ((info = (ser_info_t *)ser->info)) { bdp = info->rx_cur; - else + } + else { bdp = (cbd_t *)&cpmp->cp_dpmem[up->smc_rbase]; + info = &consinfo; + } /* * We need to gracefully shut down the receiver, disable @@ -2383,7 +2405,7 @@ if ((uint)(bdp->cbd_bufaddr) > (uint)IMAP_ADDR) cp = (u_char *)(bdp->cbd_bufaddr); else - cp = __va(bdp->cbd_bufaddr); + cp = info->rx_va_base + ((bdp - info->rx_bd_base) * RX_BUF_SIZE); if (obuf) { i = c = bdp->cbd_datlen; @@ -2704,6 +2726,7 @@ /* Allocate space for FIFOs in the host memory. */ mem_addr = m8xx_cpm_hostalloc(RX_NUM_FIFO * RX_BUF_SIZE); + info->rx_va_base = (unsigned char *)mem_addr; /* Set the physical address of the host memory * buffers in the buffer descriptors, and the @@ -2713,12 +2736,12 @@ info->rx_cur = info->rx_bd_base = (cbd_t *)bdp; for (j=0; j<(RX_NUM_FIFO-1); j++) { - bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_bufaddr = iopa(mem_addr); bdp->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT; mem_addr += RX_BUF_SIZE; bdp++; } - bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_bufaddr = iopa(mem_addr); bdp->cbd_sc = BD_SC_WRAP | BD_SC_EMPTY | BD_SC_INTRPT; idx = PORT_NUM(info->state->smc_scc_num); @@ -2738,6 +2761,7 @@ /* Allocate space for FIFOs in the host memory. */ mem_addr = m8xx_cpm_hostalloc(TX_NUM_FIFO * TX_BUF_SIZE); + info->tx_va_base = (unsigned char *)mem_addr; /* Set the physical address of the host memory * buffers in the buffer descriptors, and the @@ -2747,12 +2771,12 @@ info->tx_cur = info->tx_bd_base = (cbd_t *)bdp; for (j=0; j<(TX_NUM_FIFO-1); j++) { - bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_bufaddr = iopa(mem_addr); bdp->cbd_sc = BD_SC_INTRPT; mem_addr += TX_BUF_SIZE; bdp++; } - bdp->cbd_bufaddr = __pa(mem_addr); + bdp->cbd_bufaddr = iopa(mem_addr); bdp->cbd_sc = (BD_SC_WRAP | BD_SC_INTRPT); if (PORT_IS_SCC(info->state->smc_scc_num)) { @@ -2959,20 +2983,28 @@ * from dual port ram, and a character buffer area from host mem. */ + /* Allocate space for two FIFOs. We can't allocate from host + * memory yet because vm allocator isn't initialized + * during this early console init. + */ + dp_addr = m8xx_cpm_dpalloc(8); + mem_addr = (uint)(&cpmp->cp_dpmem[dp_addr]); + /* Allocate space for two buffer descriptors in the DP ram. */ dp_addr = m8xx_cpm_dpalloc(sizeof(cbd_t) * 2); - /* Allocate space for two 2 byte FIFOs in the host memory. - */ - mem_addr = m8xx_cpm_hostalloc(8); - /* Set the physical address of the host memory buffers in * the buffer descriptors. */ bdp = (cbd_t *)&cp->cp_dpmem[dp_addr]; - bdp->cbd_bufaddr = __pa(mem_addr); - (bdp+1)->cbd_bufaddr = __pa(mem_addr+4); + bdp->cbd_bufaddr = iopa(mem_addr); + (bdp+1)->cbd_bufaddr = iopa(mem_addr+4); + + consinfo.rx_va_base = mem_addr; + consinfo.rx_bd_base = bdp; + consinfo.tx_va_base = mem_addr + 4; + consinfo.tx_bd_base = bdp+1; /* For the receive, set empty and wrap. * For transmit, set wrap. diff -urN -X bkexcl linux-2.4.22/arch/ppc/Makefile linuxppc-2.4/arch/ppc/Makefile --- linux-2.4.22/arch/ppc/Makefile 2003-06-25 20:56:52.000000000 +1000 +++ linuxppc-2.4/arch/ppc/Makefile 2003-08-02 07:10:51.000000000 +1000 @@ -14,7 +14,7 @@ # # Be sure to change PAGE_OFFSET in include/asm-ppc/page.h to match -KERNELLOAD =0xc0000000 +KERNELLOAD =$(CONFIG_KERNEL_START) LINKFLAGS = -T arch/ppc/vmlinux.lds -Ttext $(KERNELLOAD) -Bstatic CPPFLAGS := $(CPPFLAGS) -I$(TOPDIR)/arch/$(ARCH) @@ -45,12 +45,16 @@ ifdef CONFIG_40x HEAD := arch/ppc/kernel/head_4xx.o else - ifdef CONFIG_8xx - HEAD := arch/ppc/kernel/head_8xx.o + ifdef CONFIG_440 + HEAD := arch/ppc/kernel/head_440.o else - HEAD := arch/ppc/kernel/head.o - ifdef CONFIG_6xx - HEAD += arch/ppc/kernel/idle_6xx.o + ifdef CONFIG_8xx + HEAD := arch/ppc/kernel/head_8xx.o + else + HEAD := arch/ppc/kernel/head.o + ifdef CONFIG_6xx + HEAD += arch/ppc/kernel/idle_6xx.o + endif endif endif endif diff -urN -X bkexcl linux-2.4.22/arch/ppc/boot/common/crt0.S linuxppc-2.4/arch/ppc/boot/common/crt0.S --- linux-2.4.22/arch/ppc/boot/common/crt0.S 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/boot/common/crt0.S 2003-07-25 02:06:49.000000000 +1000 @@ -66,7 +66,7 @@ bdnz 1b # If we are not done yet, keep clearing 2: -#ifdef CONFIG_4xx +#ifdef CONFIG_40x ## Set up the stack lis r9,_start@h # r9 = &_start (text section entry) diff -urN -X bkexcl linux-2.4.22/arch/ppc/boot/common/misc-simple.c linuxppc-2.4/arch/ppc/boot/common/misc-simple.c --- linux-2.4.22/arch/ppc/boot/common/misc-simple.c 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/boot/common/misc-simple.c 2003-07-25 02:06:49.000000000 +1000 @@ -75,14 +75,6 @@ extern void gunzip(void *, int, unsigned char *, int *); extern void serial_fixups(void); -/* Allow decompress_kernel to be hooked into. This is the default. */ -void * __attribute__ ((weak)) -load_kernel(unsigned long load_addr, int num_words, unsigned long cksum, - void *bp) -{ - return decompress_kernel(load_addr, num_words, cksum, bp); -} - struct bi_record * decompress_kernel(unsigned long load_addr, int num_words, unsigned long cksum) { @@ -271,3 +263,10 @@ return (struct bi_record *)rec_loc; } + +/* Allow decompress_kernel to be hooked into. This is the default. */ +struct bi_record * __attribute__ ((weak)) +load_kernel(unsigned long load_addr, int num_words, unsigned long cksum) +{ + return decompress_kernel(load_addr, num_words, cksum); +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/boot/common/util.S linuxppc-2.4/arch/ppc/boot/common/util.S --- linux-2.4.22/arch/ppc/boot/common/util.S 2003-02-27 08:13:38.000000000 +1100 +++ linuxppc-2.4/arch/ppc/boot/common/util.S 2003-03-28 08:48:01.000000000 +1100 @@ -160,9 +160,22 @@ blr +/* udelay (on non-601 processors) needs to know the period of the + * timebase in nanoseconds. This used to be hardcoded to be 60ns + * (period of 66MHz/4). Now a variable is used that is initialized to + * 60 for backward compatibility, but it can be overridden as necessary + * with code something like this: + * extern unsigned long timebase_period_ns; + * timebase_period_ns = 1000000000 / bd->bi_tbfreq; + */ + .data + .globl timebase_period_ns +timebase_period_ns: + .long 60 + + .text /* * Delay for a number of microseconds - * -- Use the BUS timer (assumes 66MHz) */ .globl udelay udelay: @@ -180,8 +193,13 @@ .udelay_not_601: mulli r4,r3,1000 /* nanoseconds */ - addi r4,r4,59 - li r5,60 + /* Change r4 to be the number of ticks using: + * (nanoseconds + (timebase_period_ns - 1 )) / timebase_period_ns + * timebase_period_ns defaults to 60 (16.6MHz) */ + lis r5,timebase_period_ns@h + lwz r5,timebase_period_ns@l(r5) + addi r4,r4,r5 + addi r4,r4,-1 divw r4,r4,r5 /* BUS ticks */ 1: mftbu r5 mftb r6 diff -urN -X bkexcl linux-2.4.22/arch/ppc/boot/simple/Makefile linuxppc-2.4/arch/ppc/boot/simple/Makefile --- linux-2.4.22/arch/ppc/boot/simple/Makefile 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/boot/simple/Makefile 2003-07-25 02:06:49.000000000 +1000 @@ -22,6 +22,22 @@ # Normally, we use the 'misc-simple.c' file for decompress_kernel and # whatnot. Sometimes we need to override this however. MISC := ../common/misc-simple.o +ifeq ($(CONFIG_ARCTIC2),y) +ZIMAGE := zImage-TREE +ZIMAGEINITRD := zImage.initrd-TREE +ZNETBOOT := zImage.treeboot +ZNETBOOTRD := zImage.initrd.treeboot +TFTPIMAGE := /tftpboot/zImage.embedded +MISC := misc-embedded.o +endif +ifeq ($(CONFIG_IBM_OPENBIOS),y) +ZIMAGE := zImage-TREE +ZIMAGEINITRD := zImage.initrd-TREE +ZNETBOOT := zImage.treeboot +ZNETBOOTRD := zImage.initrd.treeboot +TFTPIMAGE := /tftpboot/zImage.embedded +MISC := misc-embedded.o +endif ifeq ($(CONFIG_EMBEDDEDBOOT),y) ZIMAGE := zImage-EMBEDDED ZIMAGEINITRD := zImage.initrd-EMBEDDED @@ -33,6 +49,14 @@ ZIMAGEINITRD := zImage.initrd-SMON TFTPIMAGE := /tftpboot/zImage.gemini endif +ifeq ($(CONFIG_EBONY),y) +ZIMAGE := zImage-EBONY +ZIMAGEINITRD := zImage.initrd-EBONY +EXTRA := misc-ebony.o +ZNETBOOT := zImage.ebony +ZNETBOOTRD := zImage.initrd.ebony +TFTPIMAGE := /tftpboot/zImage.ebony +endif # kbuild-2.4 'feature', only one of these will ever by 'y' at a time. # The rest will be unset. ifeq ($(CONFIG_LOPEC)$(CONFIG_PPLUS),y) @@ -68,9 +92,12 @@ ifdef CONFIG_8xx LD_ARGS := -T ../ld.script -Ttext 0x00180000 -Bstatic endif -ifeq ($(CONFIG_8260)$(CONFIG_4xx),y) +ifeq ($(CONFIG_8260)$(CONFIG_40x),y) LD_ARGS := -T ../ld.script -Ttext 0x00400000 -Bstatic endif +ifdef CONFIG_440 +LD_ARGS := -T ../ld.script -Ttext 0x01000000 -Bstatic +endif OBJCOPY_ARGS := -O elf32-powerpc # head.o and ../common/relocate.o must be at the start. @@ -140,6 +167,12 @@ cp ../images/zImage.* $(TFTPIMAGE) endif +zImage-EBONY: zvmlinux + $(MKTREE) zvmlinux ../images/zImage.ebony 0x1000000 + +zImage.initrd-EBONY: zvmlinux.initrd + $(MKTREE) zvmlinux.initrd ../images/zImage.initrd.ebony 0x1000000 + zImage-EMBEDDED: zvmlinux mv zvmlinux ../images/zImage.embedded @@ -167,7 +200,7 @@ dd if=zvmlinux.initrd of=../images/zImage.initrd.gemini skip=64 bs=1k zImage-TREE: zvmlinux - $(MKTREE) zvmlinux ../images/zImage.$(END) $(ENTRYPOINT) + $(MKTREE) zvmlinux ../images/zImage.treeboot zImage.initrd-TREE: zvmlinux.initrd $(MKTREE) zvmlinux.initrd ../images/zImage.initrd.$(END) $(ENTRYPOINT) diff -urN -X bkexcl linux-2.4.22/arch/ppc/boot/simple/embed_config.c linuxppc-2.4/arch/ppc/boot/simple/embed_config.c --- linux-2.4.22/arch/ppc/boot/simple/embed_config.c 2003-07-30 23:33:24.000000000 +1000 +++ linuxppc-2.4/arch/ppc/boot/simple/embed_config.c 2003-08-05 09:59:45.000000000 +1000 @@ -9,6 +9,7 @@ #include #include +#include #ifdef CONFIG_8xx #include #endif @@ -16,12 +17,8 @@ #include #include #endif -#ifdef CONFIG_4xx #include -#endif -#if defined(CONFIG_405GP) || defined(CONFIG_NP405H) || defined(CONFIG_NP405L) -#include -#endif +extern unsigned long timebase_period_ns; /* For those boards that don't provide one. */ @@ -640,7 +637,7 @@ } #endif /* WILLOW */ -#ifdef CONFIG_TREEBOOT +#ifdef CONFIG_IBM_OPENBIOS /* This could possibly work for all treeboot roms. */ #define BOARD_INFO_VECTOR 0xFFFE0B50 @@ -654,16 +651,12 @@ bd_t *(*get_board_info)(void) = (bd_t *(*)(void))(*(unsigned long *)BOARD_INFO_VECTOR); #if !defined(CONFIG_STB03xxx) - volatile emac_t *emacp; - emacp = (emac_t *)EMAC0_BASE; /* assume 1st emac - armin */ - /* shut down the Ethernet controller that the boot rom * sometimes leaves running. */ - mtdcr(DCRN_MALCR, MALCR_MMSR); /* 1st reset MAL */ - while (mfdcr(DCRN_MALCR) & MALCR_MMSR) {}; /* wait for the reset */ - emacp->em0mr0 = 0x20000000; /* then reset EMAC */ - eieio(); + mtdcr(DCRN_MALCR(DCRN_MAL_BASE), MALCR_MMSR); /* 1st reset MAL */ + while (mfdcr(DCRN_MALCR(DCRN_MAL_BASE)) & MALCR_MMSR) {}; /* wait for the reset */ + out_be32((u32 *)EMAC0_BASE,0x20000000); /* then reset EMAC */ #endif bd = &bdinfo; @@ -707,13 +700,85 @@ } #endif +#ifdef CONFIG_ARCTIC2 +/* Several bootloaders have been used on the Arctic. We assume either + * SSX or PIBS */ + +#define SSX_BIOS_ADDR 0xFFFF0000 +#define SSX_BIOS_GET_BOARD_INFO 0 +#define PIBS_BOARD_INFO_VECTOR 0xFFF62004 + +struct ssx_bios_id { + unsigned int boot_branch; /* Branch to bootcode */ + char ssx_bios[8]; /* "SSX BIOS" (no \0) */ + void (*bios_entry_point)(unsigned int, bd_t *); /* Call bios_entry_point(cmd, &data) */ +}; + +extern int memcmp(const void *s1, const void *s2, size_t n); + +static void get_board_info(bd_t **bdp) +{ + struct ssx_bios_id *ssx = (struct ssx_bios_id *)SSX_BIOS_ADDR; + + /* Check for SSX signature */ + + if (memcmp(&ssx->ssx_bios, "SSX BIOS", 8) == 0) { + ssx->bios_entry_point(SSX_BIOS_GET_BOARD_INFO, *bdp); + } else { + /* It's not SSX, so assume PIBS */ + typedef void (*PFV)(bd_t *bd); + ((PFV)(*(unsigned long *)PIBS_BOARD_INFO_VECTOR))(*bdp); + } +} + +void embed_config(bd_t **bdp) +{ + *bdp = &bdinfo; + get_board_info(bdp); +#if 0 + /* Enable RefClk/4 mode for both UARTs */ + mtdcr(DCRN_CPC0_CR0, mfdcr(DCRN_CPC0_CR0) | 0x30000000); +#endif + timebase_period_ns = 1000000000 / bdinfo.bi_tbfreq; +} + +#endif + #ifdef CONFIG_EP405 +#include + void embed_config(bd_t **bdp) { + u32 chcr0; u_char *cp; bd_t *bd; + /* Different versions of the PlanetCore firmware vary in how + they set up the serial port - in particular whether they + use the internal or external serial clock for UART0. Make + sure the UART is in a known state. */ + /* FIXME: We should use the board's 11.0592MHz external serial + clock - it will be more accurate for serial rates. For + now, however the baud rates in ep405.h are for the internal + clock. */ + chcr0 = mfdcr(DCRN_CHCR0); + if ( (chcr0 & 0x1fff) != 0x103e ) { + mtdcr(DCRN_CHCR0, (chcr0 & 0xffffe000) | 0x103e); + /* The following tricks serial_init() into resetting + * the baud rate */ + writeb(0, UART0_IO_BASE + UART_LCR); + } + + /* We haven't seen actual problems with the EP405 leaving the + * EMAC running (as we have on Walnut). But the registers + * suggest it may not be left completely quiescent. Reset it + * just to be sure. */ + mtdcr(DCRN_MALCR(DCRN_MAL_BASE), MALCR_MMSR); /* 1st reset MAL */ + while (mfdcr(DCRN_MALCR(DCRN_MAL_BASE)) & MALCR_MMSR) + ; /* wait for the reset */ + out_be32((u32 *)EMAC0_BASE,0x20000000); /* then reset EMAC */ + bd = &bdinfo; *bdp = bd; #if 1 diff -urN -X bkexcl linux-2.4.22/arch/ppc/boot/simple/head.S linuxppc-2.4/arch/ppc/boot/simple/head.S --- linux-2.4.22/arch/ppc/boot/simple/head.S 2003-02-27 08:13:38.000000000 +1100 +++ linuxppc-2.4/arch/ppc/boot/simple/head.S 2003-03-28 08:48:01.000000000 +1100 @@ -35,8 +35,8 @@ .globl start start: bl start_ -#ifdef CONFIG_TREEBOOT - /* The IBM "Tree" bootrom knows that the address of the bootrom +#ifdef CONFIG_IBM_OPENBIOS + /* The IBM OpenBIOS bootroms know that the address of the bootrom * read only structure is 4 bytes after _start. */ .long 0x62726f6d # structure ID - "brom" diff -urN -X bkexcl linux-2.4.22/arch/ppc/boot/simple/misc-ebony.c linuxppc-2.4/arch/ppc/boot/simple/misc-ebony.c --- linux-2.4.22/arch/ppc/boot/simple/misc-ebony.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/boot/simple/misc-ebony.c 2003-03-28 08:48:01.000000000 +1100 @@ -0,0 +1,34 @@ +/* + * arch/ppc/simple/misc-ebony.c + * + * Misc. bootloader code for IBM Ebony reference platform + * + * based on code by Matt Porter + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include + +extern unsigned long timebase_period_ns; +extern struct bi_record *decompress_kernel(unsigned long load_addr, + int num_words, unsigned long cksum); + +struct bi_record * +load_kernel(unsigned long load_addr, int num_words, unsigned long cksum) +{ + timebase_period_ns = 3; + + mtdcr(DCRN_MALCR(DCRN_MAL_BASE), MALCR_MMSR); /* reset MAL */ + while (mfdcr(DCRN_MALCR(DCRN_MAL_BASE)) & MALCR_MMSR) {}; /* wait for reset */ + *(volatile unsigned long *)EBONY_EMAC0_MR0 = 0x20000000; /* reset EMAC */ + __asm__ __volatile__("eieio"); /* enforce ordering */ + + return decompress_kernel(load_addr, num_words, cksum); +} + diff -urN -X bkexcl linux-2.4.22/arch/ppc/boot/simple/misc-embedded.c linuxppc-2.4/arch/ppc/boot/simple/misc-embedded.c --- linux-2.4.22/arch/ppc/boot/simple/misc-embedded.c 2003-03-26 10:50:43.000000000 +1100 +++ linuxppc-2.4/arch/ppc/boot/simple/misc-embedded.c 2003-03-31 18:46:11.000000000 +1000 @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff -urN -X bkexcl linux-2.4.22/arch/ppc/config.in linuxppc-2.4/arch/ppc/config.in --- linux-2.4.22/arch/ppc/config.in 2003-08-26 14:24:58.000000000 +1000 +++ linuxppc-2.4/arch/ppc/config.in 2003-08-02 07:10:51.000000000 +1000 @@ -12,6 +12,7 @@ mainmenu_option next_comment comment 'Code maturity level options' bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +bool 'Prompt for advanced kernel configuration options' CONFIG_ADVANCED_OPTIONS endmenu mainmenu_option next_comment @@ -30,11 +31,12 @@ choice 'Processor Type' \ "6xx/7xx/74xx/8260 CONFIG_6xx \ 40x CONFIG_40x \ - POWER3 CONFIG_POWER3 \ - POWER4 CONFIG_POWER4 \ + 440 CONFIG_440 \ + POWER3 CONFIG_POWER3 \ + POWER4 CONFIG_POWER4 \ 8xx CONFIG_8xx" 6xx -if [ "$CONFIG_40x" = "y" ]; then +if [ "$CONFIG_40x" = "y" -o "$CONFIG_440" = "y" ]; then define_bool CONFIG_4xx y fi @@ -60,8 +62,38 @@ if [ "$CONFIG_40x" = "y" ]; then choice 'Machine Type' \ - "Oak CONFIG_OAK \ - Walnut CONFIG_WALNUT" Walnut + "Arctic-II CONFIG_ARCTIC2 \ + Ash CONFIG_ASH \ + Ceder CONFIG_CEDER \ + Beech CONFIG_BEECH \ + CPCI405 CONFIG_CPCI405 \ + EP405/EP405PC CONFIG_EP405 \ + 405EP-eval CONFIG_EVB405EP \ + Oak CONFIG_OAK \ + Rainier CONFIG_RAINIER \ + Redwood-4 CONFIG_REDWOOD_4 \ + Redwood-5 CONFIG_REDWOOD_5 \ + Redwood-6 CONFIG_REDWOOD_6 \ + Sycamore CONFIG_SYCAMORE \ + Tivo CONFIG_TIVO \ + Walnut CONFIG_WALNUT \ + Xilinx-ML300 CONFIG_XILINX_ML300" Walnut + + if [ "$CONFIG_EP405" = "y" ]; then + bool 'EP405PC Support' CONFIG_EP405PC + fi +fi + +if [ "$CONFIG_440" = "y" ]; then + define_bool CONFIG_BOOKE y + define_bool CONFIG_NOT_COHERENT_CACHE y + define_bool CONFIG_IBM_OCP y + define_bool CONFIG_GEN550 y + define_bool CONFIG_PIN_TLB y + define_int CONFIG_IBM_OCP_MAL_CNT 1 + + choice 'Machine Type' \ + "Ebony CONFIG_EBONY" Ebony fi if [ "$CONFIG_8xx" = "y" ]; then @@ -143,10 +175,60 @@ # It's often necessary to know the specific 4xx processor type. # Fortunately, it is impled (so far) from the board type, so we # don't need to ask more redundant questions. + if [ "$CONFIG_ASH" = "y" ]; then + define_bool CONFIG_NP405H y + define_bool CONFIG_IBM_OPENBIOS y + define_bool CONFIG_BIOS_FIXUP y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_CEDER" = "y" ]; then + define_bool CONFIG_NP405L y + define_bool CONFIG_IBM_OPENBIOS y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_CPCI405" = "y" ]; then + define_bool CONFIG_405GP y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_EP405" = "y" ]; then + define_bool CONFIG_405GP y + define_bool CONFIG_BIOS_FIXUP y + define_bool CONFIG_EMBEDDEDBOOT y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_EVB405EP" = "y" ]; then + define_bool CONFIG_405EP y + define_bool CONFIG_BIOS_FIXUP y + define_bool CONFIG_IBM_OPENBIOS y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_RAINIER" = "y" ]; then + define_bool CONFIG_NP405GS y + define_bool CONFIG_EMBEDDEDBOOT y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_XILINX_ML300" = "y" ]; then + define_bool CONFIG_VIRTEX_II_PRO y + define_bool CONFIG_EMBEDDEDBOOT y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_XILINX_OCP y + fi if [ "$CONFIG_OAK" = "y" -o "$CONFIG_TIVO" = "y" ]; then define_bool CONFIG_403GCX y define_bool CONFIG_IBM_OPENBIOS y fi + if [ "$CONFIG_REDWOOD_4" = "y" -o "$CONFIG_REDWOOD_5" = "y" \ + -o "$CONFIG_REDWOOD_6" = "y" ]; then + define_bool CONFIG_STB03xxx y + define_bool CONFIG_IBM_OPENBIOS y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi if [ "$CONFIG_WALNUT" = "y" ]; then define_bool CONFIG_405GP y define_bool CONFIG_BIOS_FIXUP y @@ -154,15 +236,53 @@ define_bool CONFIG_IBM405_ERR77 y define_bool CONFIG_IBM_OCP y fi + if [ "$CONFIG_BEECH" = "y" ]; then + define_bool CONFIG_405LP y + define_bool CONFIG_IBM_OPENBIOS y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_ARCTIC2" = "y" ]; then + define_bool CONFIG_405LP y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + if [ "$CONFIG_SYCAMORE" = "y" ]; then + define_bool CONFIG_405GPR y + define_bool CONFIG_BIOS_FIXUP y + define_bool CONFIG_IBM_OPENBIOS y + define_bool CONFIG_IBM405_ERR77 y + define_bool CONFIG_IBM_OCP y + fi + + dep_bool 'Power Management support (experimental)' CONFIG_PM $CONFIG_EXPERIMENTAL + if [ "$CONFIG_40x" = "y" ]; then choice 'TTYS0 device and default console' \ "UART0 CONFIG_UART0_TTYS0 \ UART1 CONFIG_UART0_TTYS1" UART0 fi - + + define_bool CONFIG_GEN550 y define_bool CONFIG_IBM405_ERR51 y define_bool CONFIG_NOT_COHERENT_CACHE y fi + +if [ "$CONFIG_4xx" = "y" ]; then + bool 'PPC4xx DMA controller support' CONFIG_PPC4xx_DMA + if [ "$CONFIG_PPC4xx_DMA" = "y" ]; then + if [ "$CONFIG_405GP" = "y" -o "$CONFIG_NP405L" = "y" \ + -o "$CONFIG_NP405H" = "y" -o "$CONFIG_NP405GS" = "y" \ + -o "$CONFIG_440" = "y" -o "$CONFIG_405LP" = "y" \ + -o "$CONFIG_405EP" = "y" ]; then + define_bool CONFIG_PPC4xx_EDMA y + fi + if [ "$CONFIG_STB03xxx" = "y" ]; then + define_bool CONFIG_STBXXX_DMA y + fi + fi +fi + if [ "$CONFIG_8xx" = "y" -o "$CONFIG_8260" = "y" ]; then define_bool CONFIG_EMBEDDEDBOOT y fi @@ -172,6 +292,49 @@ comment 'General setup' bool 'High memory support (experimental)' CONFIG_HIGHMEM +if [ "$CONFIG_ADVANCED_OPTIONS" = "y" ]; then + if [ "$CONFIG_HIGHMEM" = "y" ]; then + bool " Set high memory pool address" CONFIG_HIGHMEM_START_BOOL + if [ "$CONFIG_HIGHMEM_START_BOOL" = "y" ]; then + hex " Virtual start address of high memory pool" CONFIG_HIGHMEM_START 0xfe000000 + fi + bool " Set maximum low memory" CONFIG_LOWMEM_SIZE_BOOL + if [ "$CONFIG_LOWMEM_SIZE_BOOL" = "y" ]; then + hex " Maximum low memory size in bytes" CONFIG_LOWMEM_SIZE 0x20000000 + fi + fi + + bool "Set custom kernel base address" CONFIG_KERNEL_START_BOOL + if [ "$CONFIG_KERNEL_START_BOOL" = "y" ]; then + hex " Virtual address of kernel base" CONFIG_KERNEL_START 0xc0000000 + fi + bool "Set custom user task size" CONFIG_TASK_SIZE_BOOL + if [ "$CONFIG_TASK_SIZE_BOOL" = "y" ]; then + hex " Size of user task space" CONFIG_TASK_SIZE 0x80000000 + fi + if [ "$CONFIG_8xx" = "y" ]; then + bool "Pinned Kernel TLBs (860 ONLY)" CONFIG_PIN_TLB + fi + if [ "$CONFIG_40x" = "y" ]; then + bool "Pinned Kernel TLBs" CONFIG_PIN_TLB + fi +fi + +if [ "$CONFIG_HIGHMEM_START_BOOL" != "y" ]; then + define_hex CONFIG_HIGHMEM_START 0xfe000000 +fi + +if [ "$CONFIG_LOWMEM_SIZE_BOOL" != "y" ]; then + define_hex CONFIG_LOWMEM_SIZE 0x30000000 +fi + +if [ "$CONFIG_KERNEL_START_BOOL" != "y" ]; then + define_hex CONFIG_KERNEL_START 0xc0000000 +fi + +if [ "$CONFIG_TASK_SIZE_BOOL" != "y" ]; then + define_hex CONFIG_TASK_SIZE 0x80000000 +fi define_bool CONFIG_ISA n define_bool CONFIG_EISA n @@ -433,6 +596,27 @@ source arch/ppc/8260_io/Config.in fi +if [ "$CONFIG_40x" = "y" ]; then + mainmenu_option next_comment + comment 'IBM 4xx options' + if [ "$CONFIG_STB03xxx" = "y" ]; then + bool 'STB IR Keyboard' CONFIG_STB_KB + bool 'SICC Serial port' CONFIG_SERIAL_SICC + if [ "$CONFIG_SERIAL_SICC" = "y" -a "$CONFIG_UART0_TTYS1" = "y" ]; then + define_bool CONFIG_UART1_DFLT_CONSOLE y + define_bool CONFIG_SERIAL_SICC_CONSOLE y + fi + fi + + if [ "$CONFIG_BEECH" = "y" ]; then + dep_bool 'Use pushbutton U63 for suspend/resume?' CONFIG_405LP_PM_BUTTON $CONFIG_PM + fi + if [ "$CONFIG_ARCTIC2" = "y" ]; then + dep_bool 'Use power button for suspend/resume?' CONFIG_405LP_PM_BUTTON $CONFIG_PM + fi + endmenu +fi + source drivers/usb/Config.in source net/bluetooth/Config.in diff -urN -X bkexcl linux-2.4.22/arch/ppc/configs/ebony_defconfig linuxppc-2.4/arch/ppc/configs/ebony_defconfig --- linux-2.4.22/arch/ppc/configs/ebony_defconfig 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/configs/ebony_defconfig 2003-04-18 12:42:42.000000000 +1000 @@ -0,0 +1,498 @@ +# +# Automatically generated make config: don't edit +# +# CONFIG_UID16 is not set +# CONFIG_RWSEM_GENERIC_SPINLOCK is not set +CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_HAVE_DEC_LOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_ADVANCED_OPTIONS is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# Platform support +# +CONFIG_PPC=y +CONFIG_PPC32=y +# CONFIG_6xx is not set +# CONFIG_40x is not set +CONFIG_440=y +# CONFIG_POWER3 is not set +# CONFIG_POWER4 is not set +# CONFIG_8xx is not set +CONFIG_4xx=y +# CONFIG_PPC_STD_MMU is not set +CONFIG_BOOKE=y +CONFIG_NOT_COHERENT_CACHE=y +CONFIG_IBM_OCP=y +CONFIG_GEN550=y +CONFIG_PIN_TLB=y +CONFIG_IBM_OCP_MAL_CNT=1 +CONFIG_EBONY=y +# CONFIG_ALL_PPC is not set +# CONFIG_SMP is not set +# CONFIG_MATH_EMULATION is not set +# CONFIG_PPC4xx_DMA is not set + +# +# General setup +# +CONFIG_HIGHMEM=y +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_SBUS is not set +# CONFIG_MCA is not set +CONFIG_PCI=y +# CONFIG_PC_KEYBOARD is not set +CONFIG_NET=y +CONFIG_SYSCTL=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_KCORE_ELF=y +CONFIG_BINFMT_ELF=y +CONFIG_KERNEL_ELF=y +# CONFIG_BINFMT_MISC is not set +CONFIG_PCI_NAMES=y +# CONFIG_HOTPLUG is not set +# CONFIG_PCMCIA is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set +CONFIG_CMDLINE_BOOL=y +CONFIG_CMDLINE="ip=on" + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play configuration +# +# CONFIG_PNP is not set +# CONFIG_ISAPNP is not set + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_CISS_SCSI_TAPE is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_UMEM is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_BLK_STATS is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set +# CONFIG_BLK_DEV_MD is not set +# CONFIG_MD_LINEAR is not set +# CONFIG_MD_RAID0 is not set +# CONFIG_MD_RAID1 is not set +# CONFIG_MD_RAID5 is not set +# CONFIG_MD_MULTIPATH is not set +# CONFIG_BLK_DEV_LVM is not set + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set +CONFIG_FILTER=y +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_IP_NF_CONNTRACK is not set +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_IP_NF_COMPAT_IPCHAINS is not set +# CONFIG_IP_NF_COMPAT_IPFWADM is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set + +# +# Appletalk devices +# +# CONFIG_DEV_APPLETALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set + +# +# ATA/IDE/MFM/RLL support +# +# CONFIG_IDE is not set +# CONFIG_BLK_DEV_IDE_MODES is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_ETHERTAP is not set + +# +# Ethernet (10 or 100Mbit) +# +# CONFIG_NET_ETHERNET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_DL2K is not set +# CONFIG_E1000 is not set +# CONFIG_MYRI_SBUS is not set +# CONFIG_NS83820 is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_R8169 is not set +# CONFIG_SK98LIN is not set +# CONFIG_TIGON3 is not set + +# +# Backplane Networking +# +# CONFIG_NPNET is not set + +# +# On-chip net devices +# +CONFIG_IBM_OCP_ENET=y +# CONFIG_IBM_OCP_ENET_ERROR_MSG is not set +CONFIG_IBM_OCP_ENET_RX_BUFF=64 +CONFIG_IBM_OCP_ENET_TX_BUFF=8 +CONFIG_IBM_OCP_ENET_GAP=8 +CONFIG_IBM_OCP_ENET_SKB_RES=0 +CONFIG_OCP_NET=y +CONFIG_IBM_OCP_MAL_CNT=1 +CONFIG_IBM_OCP_ZMII=y +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Old CD-ROM drivers (not SCSI, not IDE) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# Console drivers +# +# CONFIG_VGA_CONSOLE is not set + +# +# Frame-buffer support +# +# CONFIG_FB is not set + +# +# Input core support +# +# CONFIG_INPUT is not set +# CONFIG_INPUT_KEYBDEV is not set +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_EVDEV is not set + +# +# Macintosh device drivers +# + +# +# Character devices +# +# CONFIG_VT is not set +CONFIG_SERIAL=y +CONFIG_SERIAL_CONSOLE=y +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_INPUT_GAMEPORT is not set + +# +# Input core support is needed for gameports +# + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_IPMI_PANIC_EVENT is not set +# CONFIG_IPMI_DEVICE_INTERFACE is not set +# CONFIG_IPMI_KCS is not set +# CONFIG_IPMI_WATCHDOG is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_SCx200_GPIO is not set +# CONFIG_AMD_PM768 is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set +# CONFIG_IBM_OCP_GPIO is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_REISERFS_CHECK is not set +# CONFIG_REISERFS_PROC_INFO is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BEFS_DEBUG is not set +# CONFIG_BFS_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_JBD_DEBUG is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_TMPFS is not set +CONFIG_RAMFS=y +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_ZISOFS is not set +# CONFIG_JFS_FS is not set +# CONFIG_JFS_DEBUG is not set +# CONFIG_JFS_STATISTICS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +CONFIG_DEVPTS_FS=y +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_EXT2_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_INTERMEZZO_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +CONFIG_ROOT_NFS=y +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +# CONFIG_NFSD_TCP is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set +# CONFIG_ZISOFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_SMB_NLS is not set +# CONFIG_NLS is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +# CONFIG_USB is not set + +# +# Bluetooth support +# +# CONFIG_BLUEZ is not set + +# +# Library routines +# +# CONFIG_ZLIB_INFLATE is not set +# CONFIG_ZLIB_DEFLATE is not set + +# +# Kernel hacking +# +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_SERIAL_TEXT_DEBUG is not set diff -urN -X bkexcl linux-2.4.22/arch/ppc/configs/walnut_defconfig linuxppc-2.4/arch/ppc/configs/walnut_defconfig --- linux-2.4.22/arch/ppc/configs/walnut_defconfig 2003-06-25 20:56:52.000000000 +1000 +++ linuxppc-2.4/arch/ppc/configs/walnut_defconfig 2003-06-25 21:30:07.000000000 +1000 @@ -1,5 +1,5 @@ # -# Automatically generated make config: don't edit +# Automatically generated by make menuconfig: don't edit # # CONFIG_UID16 is not set # CONFIG_RWSEM_GENERIC_SPINLOCK is not set @@ -10,6 +10,7 @@ # Code maturity level options # CONFIG_EXPERIMENTAL=y +# CONFIG_ADVANCED_OPTIONS is not set # # Loadable module support @@ -25,13 +26,28 @@ CONFIG_PPC32=y # CONFIG_6xx is not set CONFIG_40x=y +# CONFIG_440 is not set # CONFIG_POWER3 is not set # CONFIG_POWER4 is not set # CONFIG_8xx is not set CONFIG_4xx=y # CONFIG_PPC_STD_MMU is not set +# CONFIG_ARCTIC2 is not set +# CONFIG_ASH is not set +# CONFIG_CEDER is not set +# CONFIG_BEECH is not set +# CONFIG_CPCI405 is not set +# CONFIG_EP405 is not set +# CONFIG_EVB405EP is not set # CONFIG_OAK is not set +# CONFIG_RAINIER is not set +# CONFIG_REDWOOD_4 is not set +# CONFIG_REDWOOD_5 is not set +# CONFIG_REDWOOD_6 is not set +# CONFIG_SYCAMORE is not set +# CONFIG_TIVO is not set CONFIG_WALNUT=y +# CONFIG_XILINX_ML300 is not set # CONFIG_ALL_PPC is not set # CONFIG_SMP is not set # CONFIG_MATH_EMULATION is not set @@ -40,8 +56,14 @@ CONFIG_IBM_OPENBIOS=y CONFIG_IBM405_ERR77=y CONFIG_IBM_OCP=y +# CONFIG_PM is not set +CONFIG_UART0_TTYS0=y +# CONFIG_UART0_TTYS1 is not set +CONFIG_GEN550=y CONFIG_IBM405_ERR51=y CONFIG_NOT_COHERENT_CACHE=y +CONFIG_PPC4xx_DMA=y +CONFIG_PPC4xx_EDMA=y # # General setup @@ -51,7 +73,8 @@ # CONFIG_EISA is not set # CONFIG_SBUS is not set # CONFIG_MCA is not set -# CONFIG_PCI is not set +CONFIG_PCI=y +# CONFIG_PC_KEYBOARD is not set CONFIG_NET=y CONFIG_SYSCTL=y CONFIG_SYSVIPC=y @@ -60,6 +83,7 @@ CONFIG_BINFMT_ELF=y CONFIG_KERNEL_ELF=y # CONFIG_BINFMT_MISC is not set +# CONFIG_PCI_NAMES is not set # CONFIG_HOTPLUG is not set # CONFIG_PCMCIA is not set @@ -67,7 +91,6 @@ # Parallel port support # # CONFIG_PARPORT is not set -CONFIG_GEN_RTC=y # CONFIG_CMDLINE_BOOL is not set # @@ -136,10 +159,6 @@ # CONFIG_KHTTPD is not set # CONFIG_ATM is not set # CONFIG_VLAN_8021Q is not set - -# -# -# # CONFIG_IPX is not set # CONFIG_ATALK is not set @@ -181,6 +200,11 @@ # CONFIG_SCSI is not set # +# IEEE 1394 (FireWire) support (EXPERIMENTAL) +# +# CONFIG_IEEE1394 is not set + +# # Network device support # CONFIG_NETDEVICES=y @@ -203,6 +227,7 @@ # CONFIG_BMAC is not set # CONFIG_GMAC is not set # CONFIG_SUNLANCE is not set +# CONFIG_HAPPYMEAL is not set # CONFIG_SUNBMAC is not set # CONFIG_SUNQE is not set # CONFIG_SUNGEM is not set @@ -210,6 +235,7 @@ # CONFIG_LANCE is not set # CONFIG_NET_VENDOR_SMC is not set # CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_HP100 is not set # CONFIG_NET_ISA is not set # CONFIG_NET_PCI is not set # CONFIG_NET_POCKET is not set @@ -227,6 +253,22 @@ # CONFIG_R8169 is not set # CONFIG_SK98LIN is not set # CONFIG_TIGON3 is not set + +# +# Backplane Networking +# +# CONFIG_NPNET is not set + +# +# On-chip net devices +# +CONFIG_IBM_OCP_ENET=y +CONFIG_IBM_OCP_ENET_ERROR_MSG=y +CONFIG_IBM_OCP_ENET_RX_BUFF=64 +CONFIG_IBM_OCP_ENET_TX_BUFF=8 +CONFIG_IBM_OCP_ENET_GAP=8 +CONFIG_IBM_OCP_ENET_SKB_RES=0 +CONFIG_OCP_NET=y # CONFIG_FDDI is not set # CONFIG_HIPPI is not set # CONFIG_PLIP is not set @@ -310,6 +352,7 @@ CONFIG_I2C=y # CONFIG_I2C_ALGOBIT is not set # CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_IBM_IIC is not set # CONFIG_I2C_CHARDEV is not set # CONFIG_I2C_PROC is not set @@ -323,14 +366,6 @@ # Joysticks # # CONFIG_INPUT_GAMEPORT is not set - -# -# Input core support is needed for gameports -# - -# -# Input core support is needed for joysticks -# # CONFIG_QIC02_TAPE is not set # CONFIG_IPMI_HANDLER is not set # CONFIG_IPMI_PANIC_EVENT is not set @@ -349,6 +384,7 @@ # CONFIG_DTLK is not set # CONFIG_R3964 is not set # CONFIG_APPLICOM is not set +CONFIG_IBM_OCP_GPIO=y # # Ftape, the floppy tape device driver @@ -466,6 +502,10 @@ # CONFIG_SOUND is not set # +# IBM 4xx options +# + +# # USB support # # CONFIG_USB is not set diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/Makefile linuxppc-2.4/arch/ppc/kernel/Makefile --- linux-2.4.22/arch/ppc/kernel/Makefile 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/Makefile 2003-07-25 02:06:49.000000000 +1000 @@ -24,13 +24,16 @@ HEAD-y := head.o HEAD-$(CONFIG_6xx) += idle_6xx.o HEAD-$(CONFIG_40x) := head_4xx.o +HEAD-$(CONFIG_440) := head_440.o HEAD-$(CONFIG_8xx) := head_8xx.o all: $(HEAD-y) kernel.o O_TARGET := kernel.o -export-objs := ppc_ksyms.o time.o ocp.o ppc4xx_setup.o +export-objs := ppc_ksyms.o time.o ocp.o \ + ppc4xx_dma.o ppc4xx_sgdma.o \ + ppc4xx_stbdma.o ppc4xx_setup.o obj-y := entry.o traps.o irq.o idle.o time.o misc.o \ process.o signal.o ptrace.o align.o \ @@ -51,12 +54,25 @@ obj-$(CONFIG_KGDB) += gen550_kgdb.o gen550_dbg.o obj-$(CONFIG_SERIAL_TEXT_DEBUG) += gen550_dbg.o endif -obj-$(CONFIG_4xx) += todc_time.o + +ifeq ($(CONFIG_4xx),y) +obj-$(CONFIG_4xx) += todc_time.o idle_4xx.o obj-$(CONFIG_40x) += ppc4xx_setup.o ocp.o +ifeq ($(CONFIG_XILINX_OCP),y) +obj-$(CONFIG_40x) += xilinx_pic.o +else obj-$(CONFIG_40x) += ppc4xx_pic.o +endif +obj-$(CONFIG_440) += ppc4xx_pic.o ocp.o +obj-$(CONFIG_PPC4xx_EDMA) += ppc4xx_sgdma.o +obj-$(CONFIG_STBXXX_DMA) += ppc4xx_stbdma.o +obj-$(CONFIG_PPC4xx_DMA) += ppc4xx_dma.o ifeq ($(CONFIG_PCI),y) obj-$(CONFIG_40x) += ppc405_pci.o indirect_pci.o pci_auto.o +obj-$(CONFIG_440) += indirect_pci.o pci_auto.o +endif endif + obj-$(CONFIG_8xx) += m8xx_setup.o ppc8xx_pic.o ifeq ($(CONFIG_8xx),y) obj-$(CONFIG_PCI) += qspan_pci.o @@ -64,6 +80,7 @@ obj-y += softemu8xx.o endif endif + obj-$(CONFIG_MBX) += i8259.o obj-$(CONFIG_ALL_PPC) += prom_init.o prom.o open_pic.o \ indirect_pci.o i8259.o @@ -84,6 +101,7 @@ l2cr.o: l2cr.S ppc_defs.h head.o: head.S ppc_defs.h head_4xx.o: head_4xx.S ppc_defs.h +head_440.o: head_440.S ppc_defs.h head_8xx.o: head_8xx.S ppc_defs.h idle_6xx.o: idle_6xx.S ppc_defs.h diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/checks.c linuxppc-2.4/arch/ppc/kernel/checks.c --- linux-2.4.22/arch/ppc/kernel/checks.c 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/checks.c 2003-07-25 02:06:49.000000000 +1000 @@ -16,6 +16,8 @@ #include #include +int printf(const char *, ...); + /* * Do various before compile checks of data structures * -- Cort diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/cputable.c linuxppc-2.4/arch/ppc/kernel/cputable.c --- linux-2.4.22/arch/ppc/kernel/cputable.c 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/cputable.c 2003-07-25 02:06:49.000000000 +1000 @@ -357,19 +357,99 @@ }, { /* 405GP */ 0xffff0000, 0x40110000, "405GP", - CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB, + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_CAN_DOZE, PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, 32, 32, 0, /*__setup_cpu_405 */ }, + { /* 405GPr */ + 0xffff0000, 0x50910000, "405GPr", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_CAN_DOZE, + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + 32, 32, + 0, /*__setup_cpu_405 */ + }, + { /* 405EP */ + 0xffff0000, 0x51210000, "405EP", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_CAN_DOZE, + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + 32, 32, + 0, /*__setup_cpu_405 */ + }, { /* STB 03xxx */ 0xffff0000, 0x40130000, "STB03xxx", - CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB, + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_CAN_DOZE, + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + 32, 32, + 0, /*__setup_cpu_405 */ + }, + { /* STB 04xxx */ + 0xffff0000, 0x41810000, "STB04xxx", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_CAN_DOZE, + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + 32, 32, + 0, /*__setup_cpu_405 */ + }, + { /* NP405L */ + 0xffff0000, 0x41610000, "NP405L", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_CAN_DOZE, + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + 32, 32, + 0, /*__setup_cpu_405 */ + }, + { /* NP4GS3 */ + 0xffff0000, 0x40B10000, "NP4GS3", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB, PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, 32, 32, 0, /*__setup_cpu_405 */ }, -#endif /* CONFIG_4xx */ + { /* NP405H */ + 0xffff0000, 0x41410000, "NP405H", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_CAN_DOZE, + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + 32, 32, + 0, /*__setup_cpu_405 */ + }, + { /* STBx25xx */ + 0xffff0000, 0x51510000, "STBx25xx", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_CAN_DOZE, + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + 32, 32, + 0, /*__setup_cpu_405 */ + }, + { /* 405LP */ + 0xffff0000, 0x41F10000, "405LP", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_CAN_DOZE, + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, + 32, 32, + 0, /*__setup_cpu_405 */ + }, + { /* Xilinx Virtex-II Pro */ + 0xffff0000, 0x20010000, "Virtex-II Pro", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_USE_TB | CPU_FTR_CAN_DOZE, + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC, + 32, 32, + 0, /*__setup_cpu_405 */ + }, + +#endif /* CONFIG_40x */ +#ifdef CONFIG_440 + { /* 440GP Rev. B */ + 0xf0000fff, 0x40000440, "440GP Rev. B", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB, + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, + 32, 32, + 0, /*__setup_cpu_440 */ + }, + { /* 440GP Rev. C */ + 0xf0000fff, 0x40000481, "440GP Rev. C", + CPU_FTR_SPLIT_ID_CACHE | CPU_FTR_CAN_DOZE | CPU_FTR_USE_TB, + PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU, + 32, 32, + 0, /*__setup_cpu_440 */ + }, +#endif /* CONFIG_440 */ #if !CLASSIC_PPC { /* default match */ 0x00000000, 0x00000000, "(generic PPC)", diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/head_440.S linuxppc-2.4/arch/ppc/kernel/head_440.S --- linux-2.4.22/arch/ppc/kernel/head_440.S 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/head_440.S 2003-05-26 09:40:53.000000000 +1000 @@ -0,0 +1,1064 @@ +/* + * arch/ppc/kernel/head_440.S + * + * Kernel execution entry point code. + * + * Matt Porter + * + * Copyright 2002 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ppc_defs.h" + +/* + * Preprocessor Defines + */ + +#define STND_EXC 0 +#define CRIT_EXC 1 + +/* + * Macros + */ + +#define SET_IVOR(vector_number, vector_label) \ + li r26,vector_label@l; \ + mtspr SPRN_IVOR##vector_number,r26; \ + sync + +/* As with the other PowerPC ports, it is expected that when code + * execution begins here, the following registers contain valid, yet + * optional, information: + * + * r3 - Board info structure pointer (DRAM, frequency, MAC address, etc.) + * r4 - Starting address of the init RAM disk + * r5 - Ending address of the init RAM disk + * r6 - Start of kernel command line string (e.g. "mem=128") + * r7 - End of kernel command line string + * + */ + .text +_GLOBAL(_stext) +_GLOBAL(_start) + /* + * Reserve a word at a fixed location to store the address + * of abatron_pteptrs + */ + nop + /* To accomodate some SMP systems that overwrite the first few + * locations before cpu 0 starts, the bootloader starts us at 0xc. + */ + nop + nop +/* + * Save parameters we are passed + */ + mr r31,r3 + mr r30,r4 + mr r29,r5 + mr r28,r6 + mr r27,r7 + li r24,0 /* CPU number */ + +/* + * Set up the initial MMU state + * + * We are still executing code at the virtual address + * mappings set by the firmware for the base of RAM. + * + * We first invalidate all TLB entries but the one + * we are running from. We then load the KERNELBASE + * mappings so we can begin to use kernel addresses + * natively and so the interrupt vector locations are + * permanently pinned (necessary since Book E + * implementations always have translation enabled). + * + * TODO: Use the known TLB entry we are running from to + * determine which physical region we are located + * in. This can be used to determine where in RAM + * (on a shared CPU system) or PCI memory space + * (on a DRAMless system) we are located. + * For now, we assume a perfect world which means + * we are located at the base of DRAM (physical 0). + */ + +/* + * Search TLB for entry that we are currently using. + * Invalidate all entries but the one we are using. + */ + /* Load our current PID->MMUCR TID and MSR IS->MMUCR STS */ + mfspr r3,SPRN_MMUCR /* Get MMUCR */ + lis r4,PPC440_MMUCR_STS@h + ori r4,r4,PPC440_MMUCR_TID@l /* Create mask */ + andc r3,r3,r4 /* Clear out TID/STS bits */ + mfspr r4,SPRN_PID /* Get PID */ + or r3,r3,r4 /* Set TID bits */ + mfmsr r5 /* Get MSR */ + andi. r5,r5,MSR_IS@l /* TS=1? */ + beq wmmucr /* If not, leave STS=0 */ + oris r3,r3,PPC440_MMUCR_STS@h /* Set STS=1 */ +wmmucr: mtspr SPRN_MMUCR,r3 /* Put MMUCR */ + sync + + bl invstr /* Find our address */ +invstr: mflr r5 /* Make it accessible */ + tlbsx r23,0,r5 /* Find entry we are in */ + li r4,0 /* Start at TLB entry 0 */ + li r3,0 /* Set PAGEID inval value */ +1: cmpw r23,r4 /* Is this our entry? */ + beq skpinv /* If so, skip the inval */ + tlbwe r3,r4,PPC440_TLB_PAGEID /* If not, inval the entry */ +skpinv: addi r4,r4,1 /* Increment */ + cmpwi r4,64 /* Are we done? */ + bne 1b /* If not, repeat */ + isync /* If so, context change */ + +/* + * Configure and load pinned entries into TLB slots 62 and 63. + */ + + lis r3,KERNELBASE@h /* Load the kernel virtual address */ + ori r3,r3,KERNELBASE@l + + /* Kernel is at the base of RAM */ + li r4, 0 /* Load the kernel physical address */ + + /* Load the kernel PID = 0 */ + li r0,0 + mtspr SPRN_PID,r0 + sync + + /* Load the kernel TID = 0 */ + mfspr r5,SPRN_MMUCR + lis r6, PPC440_MMUCR_TID@h + ori r6,r6,PPC440_MMUCR_TID@l + andc r5,r5,r6 + mtspr SPRN_MMUCR,r5 + sync + + /* pageid fields */ + clrrwi r3,r3,10 /* Mask off the effective page number */ + ori r3,r3,(PPC440_TLB_VALID | PPC440_TLB_PAGESZ(PPC440_PAGESZ_16M)) + + /* xlat fields */ + clrrwi r4,r4,10 /* Mask off the real page number */ + /* ERPN is 0 for first 4GB page */ + + /* attrib fields */ + /* Added guarded bit to protect against speculative loads/stores */ + li r5,0 + ori r5,r5,(PPC440_TLB_SW | PPC440_TLB_SR | PPC440_TLB_SX | PPC440_TLB_G) + + li r0,62 /* TLB slot 62 */ + + tlbwe r3,r0,PPC440_TLB_PAGEID /* Load the pageid fields */ + tlbwe r4,r0,PPC440_TLB_XLAT /* Load the translation fields */ + tlbwe r5,r0,PPC440_TLB_ATTRIB /* Load the attrib/access fields */ + + /* Force context change */ + mfmsr r0 + mtspr SRR1, r0 + lis r0,3f@h + ori r0,r0,3f@l + mtspr SRR0,r0 + sync + rfi + + /* If necessary, invalidate original entry we used */ +3: cmpwi r23,62 + beq 4f + li r6,0 + tlbwe r6,r23,PPC440_TLB_PAGEID + sync + +4: ori r3,r3,PPC440_TLB_TS /* TS = 1 */ + + li r0,63 /* TLB slot 63 */ + + tlbwe r3,r0,PPC440_TLB_PAGEID /* Load the pageid fields */ + tlbwe r4,r0,PPC440_TLB_XLAT /* Load the translation fields */ + tlbwe r5,r0,PPC440_TLB_ATTRIB /* Load the attrib/access fields */ + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + /* + * Add temporary UART mapping for early debug. This + * mapping must be identical to that used by the early + * bootloader code since the same asm/serial.h parameters + * are used for polled operation. + */ + /* pageid fields */ + lis r3,0xe000 + ori r3,r3,(PPC440_TLB_VALID | PPC440_TLB_PAGESZ(PPC440_PAGESZ_256M)) + + /* xlat fields */ + lis r4,0x4000 /* RPN is 0x40000000 */ + ori r4,r4,0x0001 /* ERPN is 1 for second 4GB page */ + + /* attrib fields */ + li r5,0 + ori r5,r5,(PPC440_TLB_SW | PPC440_TLB_SR | PPC440_TLB_I | PPC440_TLB_G) + + li r0,60 /* TLB slot 60 */ + + tlbwe r3,r0,PPC440_TLB_PAGEID /* Load the pageid fields */ + tlbwe r4,r0,PPC440_TLB_XLAT /* Load the translation fields */ + tlbwe r5,r0,PPC440_TLB_ATTRIB /* Load the attrib/access fields */ + + ori r3,r3,PPC440_TLB_TS /* Translation state 1 */ + + li r0,61 /* TLB slot 61 */ + + tlbwe r3,r0,PPC440_TLB_PAGEID /* Load the pageid fields */ + tlbwe r4,r0,PPC440_TLB_XLAT /* Load the translation fields */ + tlbwe r5,r0,PPC440_TLB_ATTRIB /* Load the attrib/access fields */ +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ + + /* Force context change */ + isync + + /* Establish the interrupt vector offsets */ + SET_IVOR(0, CriticalInput); + SET_IVOR(1, MachineCheck); + SET_IVOR(2, DataStorage); + SET_IVOR(3, InstructionStorage); + SET_IVOR(4, ExternalInput); + SET_IVOR(5, Alignment); + SET_IVOR(6, Program); + SET_IVOR(7, FloatingPointUnavailable); + SET_IVOR(8, SystemCall); + SET_IVOR(9, AuxillaryProcessorUnavailable); + SET_IVOR(10, Decrementer); + SET_IVOR(11, FixedIntervalTimer); + SET_IVOR(12, WatchdogTimer); + SET_IVOR(13, DataTLBError); + SET_IVOR(14, InstructionTLBError); + SET_IVOR(15, Debug); + + /* Establish the interrupt vector base */ + lis r4,interrupt_base@h /* IVPR only uses the high 16-bits */ + mtspr SPRN_IVPR,r4 + + /* + * This is where the main kernel code starts. + */ + + /* ptr to current */ + lis r2,init_task_union@h + ori r2,r2,init_task_union@l + + /* ptr to current thread */ + addi r4,r2,THREAD /* init task's THREAD */ + mtspr SPRG3,r4 + li r3,0 + mtspr SPRG2,r3 /* 0 => r1 has kernel sp */ + + /* stack */ + addi r1,r2,TASK_UNION_SIZE + li r0,0 + stwu r0,-STACK_FRAME_OVERHEAD(r1) + + bl early_init + +/* + * Decide what sort of machine this is and initialize the MMU. + */ + mr r3,r31 + mr r4,r30 + mr r5,r29 + mr r6,r28 + mr r7,r27 + bl machine_init + bl MMU_init + + /* Setup PTE pointers for the Abatron bdiGDB */ + lis r6, swapper_pg_dir@h + ori r6, r6, swapper_pg_dir@l + lis r5, abatron_pteptrs@h + ori r5, r5, abatron_pteptrs@l + lis r4, KERNELBASE@h + ori r4, r4, KERNELBASE@l + stw r5, 0(r4) /* Save abatron_pteptrs at a fixed location */ + stw r6, 0(r5) + + /* Let's move on */ + lis r4,start_kernel@h + ori r4,r4,start_kernel@l + li r3,MSR_KERNEL + mtspr SRR0,r4 + mtspr SRR1,r3 + rfi /* change context and jump to start_kernel */ + +/* + * Interrupt vector entry code + * + * The Book E MMUs are always on so we don't need to handle + * interrupts in real mode as with previous PPC processors. In + * this case we handle interrupts in the kernel virtual address + * space. + * + * Interrupt vectors are dynamically placed relative to the + * interrupt prefix as determined by the address of interrupt_base. + * The interrupt vectors offsets are programmed using the labels + * for each interrupt vector entry. + * + * Interrupt vectors must be aligned on a 16 byte boundary. + * We align on a 32 byte cache line boundary for good measure. + */ + +#define COMMON_PROLOG \ +0: mtspr SPRN_SPRG0,r20; /* We need r20, move it to SPRG0 */\ + mtspr SPRN_SPRG1,r21; /* We need r21, move it to SPRG1 */\ + mfcr r20; /* We need the CR, move it to r20 */\ + mfspr r21,SPRN_SPRG2; /* Exception stack to use */\ + cmpwi cr0,r21,0; /* From user mode or RTAS? */\ + bne 1f; /* Not RTAS, branch */\ + mr r21, r1; /* Move vka in r1 to r21 */\ + subi r21,r21,INT_FRAME_SIZE; /* Allocate an exception frame */\ +1: stw r20,_CCR(r21); /* Save CR on the stack */\ + stw r22,GPR22(r21); /* Save r22 on the stack */\ + stw r23,GPR23(r21); /* r23 Save on the stack */\ + mfspr r20,SPRN_SPRG0; /* Get r20 back out of SPRG0 */\ + stw r20,GPR20(r21); /* Save r20 on the stack */\ + mfspr r22,SPRN_SPRG1; /* Get r21 back out of SPRG0 */\ + stw r22,GPR21(r21); /* Save r21 on the stack */\ + mflr r20; \ + stw r20,_LINK(r21); /* Save LR on the stack */\ + mfctr r22; \ + stw r22,_CTR(r21); /* Save CTR on the stack */\ + mfspr r20,XER; \ + stw r20,_XER(r21); /* Save XER on the stack */ + +#define COMMON_EPILOG \ + stw r0,GPR0(r21); /* Save r0 on the stack */\ + stw r1,GPR1(r21); /* Save r1 on the stack */\ + stw r2,GPR2(r21); /* Save r2 on the stack */\ + stw r1,0(r21); \ + mr r1,r21; /* Set-up new kernel stack pointer */\ + SAVE_4GPRS(3, r21); /* Save r3 through r6 on the stack */\ + SAVE_GPR(7, r21); /* Save r7 on the stack */ + +#define STND_EXCEPTION_PROLOG \ + COMMON_PROLOG; \ + mfspr r22,SPRN_SRR0; /* Faulting instruction address */\ + lis r20,MSR_WE@h; \ + mfspr r23,SPRN_SRR1; /* MSR at the time of fault */\ + andc r23,r23,r20; /* disable processor wait state */\ + COMMON_EPILOG; + +#define CRIT_EXCEPTION_PROLOG \ + COMMON_PROLOG; \ + mfspr r22,SPRN_CSRR0; /* Faulting instruction address */\ + lis r20,MSR_WE@h; \ + mfspr r23,SPRN_CSRR1; /* MSR at the time of fault */\ + andc r23,r23,r20; /* disable processor wait state */\ + COMMON_EPILOG; + +#define START_EXCEPTION(label) \ + .align 5; \ +label: + +#define FINISH_EXCEPTION(n, func) \ + bl transfer_to_handler; \ + .long func; \ + .long ret_from_except; \ + .long n + +#define STND_EXCEPTION(n, label, func) \ + START_EXCEPTION(label) \ + STND_EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + li r7,STND_EXC; \ + li r20,MSR_KERNEL; \ + FINISH_EXCEPTION(n, func) + +#define CRIT_EXCEPTION(n, label, func) \ + START_EXCEPTION(label) \ + CRIT_EXCEPTION_PROLOG; \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ + li r7,CRIT_EXC; \ + li r20,MSR_KERNEL; \ + FINISH_EXCEPTION(n, func) + +interrupt_base: + + /* Critical Input Interrupt */ + CRIT_EXCEPTION(0x100, CriticalInput,UnknownException); + + /* Machine Check Interrupt */ + /* TODO: provide bus error register status */ + CRIT_EXCEPTION(0x200, MachineCheck,MachineCheckException); + + /* Data Storage Interrupt */ + START_EXCEPTION(DataStorage) + mtspr SPRG0, r20 /* Save some working registers */ + mtspr SPRG1, r21 + mtspr SPRG4W, r22 + mtspr SPRG5W, r23 + mtspr SPRG6W, r24 + mfcr r21 + mtspr SPRG7W, r21 + + /* + * Check if it was a store fault, if not then bail + * because a user tried to access a kernel or + * read-protected page. Otherwise, get the + * offending address and handle it. + */ + mfspr r20, SPRN_ESR + andis. r20, r20, ESR_ST@h + beq 2f + + mfspr r20, SPRN_DEAR /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andis. r21, r20, 0x8000 + beq 3f + lis r21, swapper_pg_dir@h + ori r21, r21, swapper_pg_dir@l + + mfspr r22,SPRN_MMUCR /* Set TID to 0 */ + li r23,PPC440_MMUCR_TID@l + andc r22,r22,r23 + mtspr SPRN_MMUCR,r22 + + b 4f + + /* Get the PGD for the current thread */ +3: + mfspr r21,SPRG3 + lwz r21,PGDIR(r21) + + /* Load MMUCR with our PID and STS= */ + mfspr r22,SPRN_MMUCR /* Get MMUCR */ + lis r23,PPC440_MMUCR_STS@h + ori r23,r23,PPC440_MMUCR_TID@l /* Create mask */ + andc r22,r22,r23 /* Clear out TID/STS bits */ + mfspr r23,SPRN_PID /* Get PID */ + or r22,r22,r23 /* Set TID bits */ + mfspr r24,SPRN_SRR1 /* Get SRR1 */ + andi. r24,r24,MSR_IS@l /* TS=1? */ + beq 4f /* If not, leave STS=0 */ + oris r22,r22,PPC440_MMUCR_STS@h /* Set STS=1 */ + mtspr SPRN_MMUCR,r22 +4: + rlwinm r22, r20, 13, 19, 29 /* Compute pgdir/pmd offset */ + lwzx r21, r22, r21 /* Get pgd/pmd entry */ + rlwinm. r22, r21, 0, 0, 20 /* Extract pt base address */ + beq 2f /* Bail if no table */ + + rlwimi r22, r20, 23, 20, 28 /* Compute pte address */ + lwz r21, 4(r22) /* Get pte entry */ + + andi. r23, r21, _PAGE_RW /* Is it writeable? */ + beq 2f /* Bail if not */ + + /* Update 'changed'. + */ + ori r21, r21, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE + stw r21, 4(r22) /* Update Linux page table */ + + /* FIXME: Staticly setting some permissions */ + li r23, 0x003f /* Set UX,UW,UR,SX,SW,SR */ + andi. r21,r21,0xffff /* Clear MS 16 bits */ + /* FIXME: Force attributes */ + ori r21,r21, 0x0100 /* Set G */ + /* FIXME: Already set in PTE */ + rlwimi r21,r23,0,26,31 /* Insert static perms */ + + lis r23,0xffff + ori r23,r23,0x0fff /* Set U0-U3 mask */ + and r21,r21,r23 /* Clear U0-U3 */ + + /* find the TLB index that caused the fault. It has to be here. */ + tlbsx r24, 0, r20 + + tlbwe r21, r24, PPC440_TLB_ATTRIB /* Write ATTRIB */ + + /* Done...restore registers and get out of here. + */ + mfspr r21, SPRG7R + mtcr r21 + mfspr r24, SPRG6R + mfspr r23, SPRG5R + mfspr r22, SPRG4R + + mfspr r21, SPRG1 + mfspr r20, SPRG0 + PPC405_ERR77_SYNC + rfi /* Force context change */ + +2: + /* + * The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ + mfspr r21, SPRG7R + mtcr r21 + mfspr r24, SPRG6R + mfspr r23, SPRG5R + mfspr r22, SPRG4R + + mfspr r21, SPRG1 + mfspr r20, SPRG0 + b data_access + + /* Instruction Storage Interrupt */ + START_EXCEPTION(InstructionStorage) + STND_EXCEPTION_PROLOG + mfspr r5,SPRN_ESR /* Grab the ESR, save it */ + stw r5,_ESR(r21) + mr r4,r22 /* Pass SRR0 as arg2 */ + li r5,0 /* Pass zero as arg3 */ + addi r3,r1,STACK_FRAME_OVERHEAD + li r7,STND_EXC + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ + FINISH_EXCEPTION(0x400, do_page_fault)/* do_page_fault(regs, SRR0, SRR1) */ + + /* External Input Interrupt */ + START_EXCEPTION(ExternalInput) + STND_EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD + li r7,STND_EXC + li r20,MSR_KERNEL + li r4,0 + bl transfer_to_handler +_GLOBAL(do_IRQ_intercept) + .long do_IRQ + .long ret_from_intercept + .long 0x500 + + /* Alignment Interrupt */ + START_EXCEPTION(Alignment) + STND_EXCEPTION_PROLOG + mfspr r4,SPRN_DEAR /* Grab the DEAR and save it */ + stw r4,_DEAR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + li r7,STND_EXC + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ + FINISH_EXCEPTION(0x600, AlignmentException) + + /* Program Interrupt */ + START_EXCEPTION(Program) + STND_EXCEPTION_PROLOG + mfspr r4,SPRN_ESR /* Grab the ESR, save it */ + stw r4,_ESR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + li r7,STND_EXC + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ + FINISH_EXCEPTION(0x700, ProgramCheckException) + + /* Floating Point Unavailable Interrupt */ + STND_EXCEPTION(0x2010, FloatingPointUnavailable,UnknownException); + + /* System Call Interrupt */ + START_EXCEPTION(SystemCall) + STND_EXCEPTION_PROLOG + stw r3,ORIG_GPR3(r21) + li r7,STND_EXC + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ + FINISH_EXCEPTION(0xc00, DoSyscall) + + /* Auxillary Processor Unavailable */ + STND_EXCEPTION(0x2020, AuxillaryProcessorUnavailable,UnknownException); + + /* Decrementer Interrupt */ + START_EXCEPTION(Decrementer) + STND_EXCEPTION_PROLOG + lis r0,TSR_DIS@h /* Setup the DEC interrupt mask */ + mtspr SPRN_TSR,r0 /* Clear the DEC interrupt */ + addi r3,r1,STACK_FRAME_OVERHEAD + li r7,STND_EXC + li r20,MSR_KERNEL + bl transfer_to_handler +_GLOBAL(timer_interrupt_intercept) + .long timer_interrupt + .long ret_from_intercept + .long 0x1000 + + /* Fixed Internal Timer Interrupt */ + /* TODO: Add FIT support */ + STND_EXCEPTION(0x1010, FixedIntervalTimer,UnknownException); + + /* Watchdog Timer Interrupt */ + /* TODO: Add watchdog support */ + CRIT_EXCEPTION(0x1020, WatchdogTimer,UnknownException); + + /* Data TLB Error Interrupt */ + START_EXCEPTION(DataTLBError) + mtspr SPRG0, r20 /* Save some working registers */ + mtspr SPRG1, r21 + mtspr SPRG4W, r22 + mtspr SPRG5W, r23 + mtspr SPRG6W, r24 + mfcr r21 + mtspr SPRG7W, r21 + mfspr r20, SPRN_DEAR /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andis. r21, r20, 0x8000 + beq 3f + lis r21, swapper_pg_dir@h + ori r21, r21, swapper_pg_dir@l + + mfspr r22,SPRN_MMUCR /* Set TID to 0 */ + li r23,PPC440_MMUCR_TID@l + andc r22,r22,r23 + mtspr SPRN_MMUCR,r22 + + b 4f + + /* Get the PGD for the current thread */ +3: + mfspr r21,SPRG3 + lwz r21,PGDIR(r21) + + /* Load PID into MMUCR TID */ + li r23,PPC440_MMUCR_TID@l /* Create mask */ + andc r22,r22,r23 /* Clear out TID/STS bits */ + mfspr r23,SPRN_PID /* Get PID */ + or r22,r22,r23 + mtspr SPRN_MMUCR,r22 +4: + rlwinm r22, r20, 13, 19, 29 /* Compute pgdir/pmd offset */ + lwzx r21, r22, r21 /* Get pgd/pmd entry */ + rlwinm. r22, r21, 0, 0, 20 /* Extract pt base address */ + beq 2f /* Bail if no table */ + + rlwimi r22, r20, 23, 20, 28 /* Compute pte address */ + lwz r21, 4(r22) /* Get pte entry */ + andi. r23, r21, _PAGE_PRESENT /* Is the page present? */ + beq 2f /* Bail if not present */ + + ori r21, r21, _PAGE_ACCESSED + stw r21, 4(r22) + + /* Jump to common tlb load */ + b finish_tlb_load + +2: + /* The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ + mfspr r21, SPRG7R + mtcr r21 + mfspr r24, SPRG6R + mfspr r23, SPRG5R + mfspr r22, SPRG4R + mfspr r21, SPRG1 + mfspr r20, SPRG0 + b data_access + + /* Instruction TLB Error Interrupt */ + /* + * Nearly the same as above, except we get our + * information from different registers and bailout + * to a different point. + */ + START_EXCEPTION(InstructionTLBError) + mtspr SPRG0, r20 /* Save some working registers */ + mtspr SPRG1, r21 + mtspr SPRG4W, r22 + mtspr SPRG5W, r23 + mtspr SPRG6W, r24 + mfcr r21 + mtspr SPRG7W, r21 + mfspr r20, SRR0 /* Get faulting address */ + + /* If we are faulting a kernel address, we have to use the + * kernel page tables. + */ + andis. r21, r20, 0x8000 + beq 3f + lis r21, swapper_pg_dir@h + ori r21, r21, swapper_pg_dir@l + + mfspr r22,SPRN_MMUCR /* Set TID to 0 */ + li r23,PPC440_MMUCR_TID@l + andc r22,r22,r23 + mtspr SPRN_MMUCR,r22 + + b 4f + + /* Get the PGD for the current thread */ +3: + mfspr r21,SPRG3 + lwz r21,PGDIR(r21) + + /* Load PID into MMUCR TID */ + li r23,PPC440_MMUCR_TID@l /* Create mask */ + andc r22,r23,r23 /* Clear out TID/STS bits */ + mfspr r23,SPRN_PID /* Get PID */ + or r22,r22,r23 + mtspr SPRN_MMUCR,r22 + +4: + rlwinm r22, r20, 13, 19, 29 /* Compute pgdir/pmd offset */ + lwzx r21, r22, r21 /* Get pgd/pmd entry */ + rlwinm. r22, r21, 0, 0, 20 /* Extract pt base address */ + beq 2f /* Bail if no table */ + + rlwimi r22, r20, 23, 20, 28 /* Compute pte address */ + lwz r21, 4(r22) /* Get pte entry */ + andi. r23, r21, _PAGE_PRESENT /* Is the page present? */ + beq 2f /* Bail if not present */ + + ori r21, r21, _PAGE_ACCESSED + stw r21, 4(r22) + + /* Jump to common TLB load point */ + b finish_tlb_load + +2: + /* The bailout. Restore registers to pre-exception conditions + * and call the heavyweights to help us out. + */ + mfspr r21, SPRG7R + mtcr r21 + mfspr r24, SPRG6R + mfspr r23, SPRG5R + mfspr r22, SPRG4R + mfspr r21, SPRG1 + mfspr r20, SPRG0 + b InstructionStorage + +/* Check for a single step debug exception while in an exception + * handler before state has been saved. This is to catch the case + * where an instruction that we are trying to single step causes + * an exception (eg ITLB/DTLB miss) and thus the first instruction of + * the exception handler generates a single step debug exception. + * + * If we get a debug trap on the first instruction of an exception handler, + * we reset the MSR_DE in the _exception handlers_ MSR (the debug trap is + * a critical exception, so we are using SPRN_CSRR1 to manipulate the MSR). + * The exception handler was handling a non-critical interrupt, so it will + * save (and later restore) the MSR via SPRN_SRR1, which will still have + * the MSR_DE bit set. + */ + /* Debug Interrupt */ + START_EXCEPTION(Debug) + /* This first instruction was already executed by the exception + * handler and must be the first instruction of every exception + * handler. + */ + mtspr SPRN_SPRG0,r20 /* Save some working registers... */ + mtspr SPRN_SPRG1,r21 + mtspr SPRN_SPRG4W,r22 + mfcr r20 /* ..and the cr because we change it */ + + mfspr r21,SPRN_CSRR1 /* MSR at the time of fault */ + andi. r21,r21,MSR_PR + bne+ 2f /* trapped from problem state */ + + mfspr r21,SPRN_CSRR0 /* Faulting instruction address */ + lis r22, KERNELBASE@h + ori r22, r22, KERNELBASE@l + cmplw r21,r22 + blt+ 2f /* addr below exception vectors */ + + lis r22, Debug@h + ori r22, r22, Debug@l + cmplw r21,r22 + bgt+ 2f /* addr above TLB exception vectors */ + + lis r21,DBSR_IC@h /* Remove the trap status */ + mtspr SPRN_DBSR,r21 + + mfspr r21,SPRN_CSRR1 + rlwinm r21,r21,0,23,21 /* clear MSR_DE */ + mtspr SPRN_CSRR1, r21 /* restore MSR at rcfi without DE */ + + mtcrf 0xff,r20 /* restore registers */ + mfspr r22,SPRN_SPRG4R + mfspr r21,SPRN_SPRG1 + mfspr r20,SPRN_SPRG0 + + sync + rfci /* return to the exception handler */ + b . /* prevent prefetch past rfci */ + +2: + mtcrf 0xff,r20 /* restore registers */ + mfspr r22,SPRN_SPRG4R + mfspr r21,SPRN_SPRG1 + mfspr r20,SPRN_SPRG0 + + CRIT_EXCEPTION_PROLOG + addi r3,r1,STACK_FRAME_OVERHEAD + li r7,CRIT_EXC; + li r20,MSR_KERNEL + FINISH_EXCEPTION(0x2000, DebugException) + +/* + * Local functions + */ + + /* + * Data TLB exceptions will bail out to this point + * if they can't resolve the lightweight TLB fault. + */ +data_access: + STND_EXCEPTION_PROLOG + mfspr r5,SPRN_ESR /* Grab the ESR, save it, pass arg3 */ + stw r5,_ESR(r21) + mfspr r4,SPRN_DEAR /* Grab the DEAR, save it, pass arg2 */ + stw r4,_DEAR(r21) + addi r3,r1,STACK_FRAME_OVERHEAD + li r7,STND_EXC + li r20,MSR_KERNEL + rlwimi r20,r23,0,16,16 /* Copy EE bit from the saved MSR */ + FINISH_EXCEPTION(0x800, do_page_fault) /* do_page_fault(regs, ESR, DEAR) */ + +/* + * Both the instruction and data TLB miss get to this + * point to load the TLB. + * r20 - EA of fault + * r21 - LS word of the PTE + * r22 - Pointer to our 64-bit PTE + * r23 - available to use + * r24 - available to use + * MMUCR - loaded with proper value when we get here + * Upon exit, we reload everything and RFI. + */ +finish_tlb_load: + /* + * We set execute, because we don't have the granularity to + * properly set this at the page level (Linux problem). + * If shared is set, we cause a zero PID->TID load. + * Many of these bits are software only. Bits we don't set + * here we (properly should) assume have the appropriate value. + */ + + /* Load the next available TLB index */ + lis r23, tlb_4xx_index@h + ori r23, r23, tlb_4xx_index@l + lwz r24, 0(r23) + /* Increment, rollover, and store TLB index */ + addi r24, r24, 1 + cmpwi 0, r24, 61 /* reserve entries 62-63 for kernel */ + ble 7f + li r24, 0 +7: + stw r24, 0(r23) + +6: + lwz r23, 0(r22) /* Get MS word of PTE */ + rlwimi r23, r21, 0, 0 , 19 /* Insert RPN */ + tlbwe r23, r24, PPC440_TLB_XLAT /* Write XLAT */ + + /* + * Create PAGEID. This is the faulting address plus + * a set of static bits. The static bits are page + * size and valid. Bits 20 and 21 should be zero + * for a page size of 4KB. + */ + li r22, 0x0210 /* Set size and valid */ + mfspr r23, SPRN_SRR1 /* Get SRR1 */ + andi. r23, r23, MSR_IS@l + beq 7f + ori r22, r22, PPC440_TLB_TS@l /* Set TS=1 */ +7: rlwimi r20, r22, 0, 20, 31 /* Insert statics */ + tlbwe r20, r24, PPC440_TLB_PAGEID /* Write PAGEID */ + + /* FIXME: Staticly setting some permissions */ + li r23, 0x002d /* Set UX,UR,SX,SR */ + andi. r21, r21, 0xffff /* Clear MS 16 bits */ + andi. r22, r21, 0x0002 /* _PAGE_HWWRITE? */ + beq 8f + ori r23, r23, 0x0002 /* Set SW */ + /* FIXME: Force attributes */ +8: ori r21, r21, 0x0100 /* Set G */ + /* FIXME: Already set in PTE */ + rlwimi r21, r23, 0, 26, 31 /* Insert static perms */ + + lis r23,0xffff + ori r23,r23,0x0fff /* Set U0-U3 mask */ + and r21,r21,r23 /* Clear U0-U3 */ + tlbwe r21, r24, PPC440_TLB_ATTRIB /* Write ATTRIB */ + + /* Done...restore registers and get out of here. + */ + mfspr r21, SPRG7R + mtcr r21 + mfspr r24, SPRG6R + mfspr r23, SPRG5R + mfspr r22, SPRG4R + mfspr r21, SPRG1 + mfspr r20, SPRG0 + rfi /* Force context change */ + +/* + * Global functions + */ + +/* + * extern void giveup_altivec(struct task_struct *prev) + * + * The 440 core does not have an AltiVec unit. + */ +_GLOBAL(giveup_altivec) + blr + +/* + * extern void giveup_fpu(struct task_struct *prev) + * + * The 440 core does not have an FPU. + */ +_GLOBAL(giveup_fpu) + blr + +/* + * extern void abort(void) + * + * At present, this routine just applies a system reset. + */ +_GLOBAL(abort) + mfspr r13,SPRN_DBCR0 + oris r13,r13,DBCR_RST(DBCR_RST_SYSTEM)@h + mtspr SPRN_DBCR0,r13 + +_GLOBAL(set_context) + +#ifdef CONFIG_BDI_SWITCH + /* Context switch the PTE pointer for the Abatron BDI2000. + * The PGDIR is the second parameter. + */ + lis r5, abatron_pteptrs@h + ori r5, r5, abatron_pteptrs@l + stw r4, 0x4(r5) +#endif + mtspr SPRN_PID,r3 + isync /* Force context change */ + blr + +/* + * This code finishes saving the registers to the exception frame + * and jumps to the appropriate handler for the exception, turning + * on address translation. + */ +_GLOBAL(transfer_to_handler) + stw r22,_NIP(r21) /* Save the faulting IP on the stack */ + stw r23,_MSR(r21) /* Save the exception MSR on stack */ + SAVE_4GPRS(8, r21) /* Save r8 through r11 on the stack */ + SAVE_8GPRS(12, r21) /* Save r12 through r19 on the stack */ + SAVE_8GPRS(24, r21) /* Save r24 through r31 on the stack */ + andi. r23,r23,MSR_PR /* Is this from user space? */ + mfspr r23,SPRN_SPRG3 /* If from user, fix up THREAD.regs */ + beq 2f /* No, it is from the kernel; branch. */ + mfspr r24,SPRN_DBCR0 + stw r24,THREAD_DBCR0(r23) /* Save Debug Control in thread_struct */ + addi r24,r1,STACK_FRAME_OVERHEAD + stw r24,PT_REGS(r23) +2: addi r2,r23,-THREAD /* Set r2 to current thread */ + mflr r23 + lwz r24,8(r23) /* Emulate classic PPC vectors */ + stw r24,TRAP(r21) + li r22,RESULT + /* No need to put an erratum #77 workaround here + because interrupts are currently disabled */ + stwcx. r22,r22,r21 /* Clear the reservation */ + li r22,0 + stw r22,RESULT(r21) + mtspr SPRN_SPRG2,r22 /* r1 is now the kernel stack pointer */ + addi r24,r2,TASK_STRUCT_SIZE /* Check for kernel stack overflow */ + cmplw cr0,r1,r2 + cmplw cr1,r1,r24 + crand cr1,cr1,cr4 + bgt- stack_ovf /* If r2 < r1 < r2 + TASK_STRUCT_SIZE */ + lwz r24,0(r23) /* Virtual address of the handler */ + lwz r23,4(r23) /* Handler return pointer */ + cmpwi cr0,r7,STND_EXC /* What type of exception is this? */ + bne 3f /* It is a critical exception... */ + + /* Standard exception jump path + */ + + /* We have to recover r7 from the register save stack. + * It was used to indicate standard/critical exception. In + * the case of a standard exception that is the system call + * trap, it may have originally contained one of the syscall + * parameters and we have to get it back now. + */ + lwz r7,GPR7(r21) + mtspr SPRN_SRR0,r24 /* Set up the instruction pointer */ + mtspr SPRN_SRR1,r20 /* Set up the machine state register */ + mtlr r23 /* Set up the return pointer */ + SYNC + /* We shouldn't need a 405 erratum #77 workaround here, because we're not + * actually returning to the interrupted instruction yet. */ + rfi + + /* Critical exception jump path + */ + +3: mtspr SPRN_CSRR0,r24 /* Set up the instruction pointer */ + mtspr SPRN_CSRR1,r20 /* Set up the machine state register */ + mtlr r23 /* Set up the return pointer */ + SYNC + rfci + +/* On kernel stack overlow, load up an initial stack pointer and call + * StackOverflow(regs), which should NOT return. + */ + +stack_ovf: + addi r3,r1,STACK_FRAME_OVERHEAD + lis r1,init_task_union@ha + addi r1,r1,init_task_union@l + addi r1,r1,TASK_UNION_SIZE - STACK_FRAME_OVERHEAD + lis r24,StackOverflow@ha + addi r24,r24,StackOverflow@l + li r20,MSR_KERNEL + mtspr SPRN_SRR0,r24 + mtspr SPRN_SRR1,r20 + SYNC + rfi + +/* + * We put a few things here that have to be page-aligned. This stuff + * goes at the beginning of the data segment, which is page-aligned. + */ + .data +_GLOBAL(sdata) +_GLOBAL(empty_zero_page) + .space 4096 + +/* + * To support >32-bit physical addresses, we use an 8KB pgdir. + */ +_GLOBAL(swapper_pg_dir) + .space 8192 + +/* + * This space gets a copy of optional info passed to us by the bootstrap + * which is used to pass parameters into the kernel like root=/dev/sda1, etc. + */ +_GLOBAL(cmd_line) + .space 512 + +/* + * Room for two PTE pointers, usually the kernel and current user pointers + * to their respective root page table. + */ +abatron_pteptrs: + .space 8 diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/head_8xx.S linuxppc-2.4/arch/ppc/kernel/head_8xx.S --- linux-2.4.22/arch/ppc/kernel/head_8xx.S 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/head_8xx.S 2003-07-25 02:06:49.000000000 +1000 @@ -21,9 +21,9 @@ * */ +#include #include #include -#include #include #include #include @@ -817,16 +817,31 @@ * kernel initialization. This maps the first 8 MBytes of memory 1:1 * virtual to physical. Also, set the cache mode since that is defined * by TLB entries and perform any additional mapping (like of the IMMR). + * If configured to pin some TLBs, we pin the first 8 Mbytes of kernel, + * 24 Mbytes of data, and the 8M IMMR space. Anything not covered by + * these mappings is mapped by page tables. */ initial_mmu: tlbia /* Invalidate all TLB entries */ +#ifdef CONFIG_PIN_TLB + lis r8, MI_RSV4I@h + ori r8, r8, 0x1c00 +#else li r8, 0 - mtspr MI_CTR, r8 /* Set instruction control to zero */ - lis r8, MD_RESETVAL@h +#endif + mtspr MI_CTR, r8 /* Set instruction MMU control */ + +#ifdef CONFIG_PIN_TLB + lis r10, (MD_RSV4I | MD_RESETVAL)@h + ori r10, r10, 0x1c00 + mr r8, r10 +#else + lis r10, MD_RESETVAL@h +#endif #ifndef CONFIG_8xx_COPYBACK - oris r8, r8, MD_WTDEF@h + oris r10, r10, MD_WTDEF@h #endif - mtspr MD_CTR, r8 /* Set data TLB control */ + mtspr MD_CTR, r10 /* Set data TLB control */ /* Now map the lower 8 Meg into the TLBs. For this quick hack, * we can load the instruction and data TLB registers with the @@ -850,6 +865,10 @@ /* Map another 8 MByte at the IMMR to get the processor * internal registers (among other things). */ +#ifdef CONFIG_PIN_TLB + addi r10, r10, 0x0100 + mtspr MD_CTR, r10 +#endif mfspr r9, 638 /* Get current IMMR */ andis. r9, r9, 0xff80 /* Get 8Mbyte boundary */ @@ -863,6 +882,30 @@ ori r8, r8, MI_BOOTINIT|0x2 /* Inhibit cache -- Cort */ mtspr MD_RPN, r8 +#ifdef CONFIG_PIN_TLB + /* Map two more 8M kernel data pages. + */ + addi r10, r10, 0x0100 + mtspr MD_CTR, r10 + + lis r8, KERNELBASE@h /* Create vaddr for TLB */ + addis r8, r8, 0x0080 /* Add 8M */ + ori r8, r8, MI_EVALID /* Mark it valid */ + mtspr MD_EPN, r8 + li r9, MI_PS8MEG /* Set 8M byte page */ + ori r9, r9, MI_SVALID /* Make it valid */ + mtspr MD_TWC, r9 + li r11, MI_BOOTINIT /* Create RPN for address 0 */ + addis r11, r11, 0x0080 /* Add 8M */ + mtspr MD_RPN, r8 + + addis r8, r8, 0x0080 /* Add 8M */ + mtspr MD_EPN, r8 + mtspr MD_TWC, r9 + addis r11, r11, 0x0080 /* Add 8M */ + mtspr MD_RPN, r8 +#endif + /* Since the cache is enabled according to the information we * just loaded into the TLB, invalidate and enable the caches here. * We should probably check/set other modes....later. diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/idle_4xx.c linuxppc-2.4/arch/ppc/kernel/idle_4xx.c --- linux-2.4.22/arch/ppc/kernel/idle_4xx.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/idle_4xx.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,41 @@ +/* + * This is the old power_save() function from idle.c: + * + * Written by Cort Dougan (cort@cs.nmt.edu) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ +#include +#include +#include + +void power_save_4xx(void) +{ + /* Make sure the CPU has the DOZE feature set. */ + if (!(cur_cpu_spec[smp_processor_id()]->cpu_features + & CPU_FTR_CAN_DOZE)) + return; + /* + * Disable interrupts to prevent a lost wakeup + * when going to sleep. This is necessary even with + * RTLinux since we are not guaranteed an interrupt + * didn't come in and is waiting for a __sti() before + * emulating one. This way, we really do hard disable. + * + * We assume that we're sti-ed when we come in here. We + * are in the idle loop so if we're cli-ed then it's a bug + * anyway. + * -- Cort + */ + _nmask_and_or_msr(MSR_EE, 0); + if (!current->need_resched) { + /* set the POW bit in the MSR, and enable interrupts + * so we wake up sometime! */ + _nmask_and_or_msr(0, MSR_POW | MSR_EE); + } + _nmask_and_or_msr(0, MSR_EE); +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/m8xx_setup.c linuxppc-2.4/arch/ppc/kernel/m8xx_setup.c --- linux-2.4.22/arch/ppc/kernel/m8xx_setup.c 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/m8xx_setup.c 2003-07-25 02:06:49.000000000 +1000 @@ -336,7 +336,7 @@ io_block_mapping(_IO_BASE,_IO_BASE,_IO_BASE_SIZE, _PAGE_IO); #endif #endif -#ifdef CONFIG_HTDMSOUND +#if defined(CONFIG_HTDMSOUND) || defined(CONFIG_RPXTOUCH) || defined(CONFIG_FB_RPX) io_block_mapping(HIOX_CSR_ADDR, HIOX_CSR_ADDR, HIOX_CSR_SIZE, _PAGE_IO); #endif #ifdef CONFIG_FADS diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/misc.S linuxppc-2.4/arch/ppc/kernel/misc.S --- linux-2.4.22/arch/ppc/kernel/misc.S 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/misc.S 2003-07-25 02:06:49.000000000 +1000 @@ -361,6 +361,15 @@ cmpwi 0, r3, 61 /* reserve last two entries */ ble 1b isync +#elif defined(CONFIG_440) + lis r3,0 + sync +1: + tlbwe r3,r3,PPC440_TLB_PAGEID + addi r3,r3,1 + cmpwi 0,r3,61 + ble 1b + isync #else #if defined(CONFIG_SMP) mfmsr r10 @@ -406,6 +415,30 @@ tlbwe r3, r3, TLB_TAG isync 10: +#elif defined(CONFIG_440) + mfspr r4,SPRN_MMUCR /* Get MMUCR */ + lis r5,PPC440_MMUCR_STS@h + ori r5,r5,PPC440_MMUCR_TID@l /* Create mask */ + andc r4,r4,r5 /* Clear out TID/STS bits */ + mfspr r5,SPRN_PID /* Get PID */ + or r4,r4,r5 /* Set TID bits */ + mfmsr r6 /* Get MSR */ + andi. r6,r6,MSR_IS@l /* TS=1? */ + beq 11f /* If not, leave STS=0 */ + oris r4,r4,PPC440_MMUCR_STS@h /* Set STS=1 */ +11: mtspr SPRN_MMUCR, r4 /* Put MMUCR */ + + tlbsx. r3, 0, r3 + bne 10f + sync + /* There are only 64 TLB entries, so r3 < 64, + * which means bit 22, is clear. Since 22 is + * the V bit in the TLB_PAGEID, loading this + * value will invalidate the TLB entry. + */ + tlbwe r3, r3, PPC440_TLB_PAGEID + isync +10: #else #if defined(CONFIG_SMP) mfmsr r10 diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/mk_defs.c linuxppc-2.4/arch/ppc/kernel/mk_defs.c --- linux-2.4.22/arch/ppc/kernel/mk_defs.c 2003-06-25 20:56:52.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/mk_defs.c 2003-06-25 21:30:07.000000000 +1000 @@ -25,6 +25,10 @@ #include #include +#ifdef CONFIG_405LP +#include +#endif /* CONFIG_405LP */ + #define DEFINE(sym, val) \ asm volatile("\n#define\t" #sym "\t%0" : : "i" (val)) @@ -109,7 +113,7 @@ DEFINE(_XER, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, xer)); DEFINE(_DAR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dar)); DEFINE(_DSISR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, dsisr)); - /* The PowerPC 400-class processors have neither the DAR nor the DSISR + /* The PowerPC 400-class & Book-E processors have neither the DAR nor the DSISR * SPRs. Hence, we overload them to hold the similar DEAR and ESR SPRs * for such processors. */ @@ -129,5 +133,12 @@ DEFINE(CPU_SPEC_SETUP, offsetof(struct cpu_spec, cpu_setup)); DEFINE(NUM_USER_SEGMENTS, TASK_SIZE>>28); + +#ifdef CONFIG_405LP + DEFINE(WAKEUP_FUNC_OFFSET, offsetof(struct ibm405lp_wakeup_info, wakeup_func_phys)); + DEFINE(WAKEUP_MAGIC_OFFSET, offsetof(struct ibm405lp_wakeup_info, magic)); + DEFINE(WAKEUP_SP_OFFSET, offsetof(struct ibm405lp_wakeup_info, wakeup_stack_phys)); +#endif /* CONFIG_405LP */ + return 0; } diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/pci-dma.c linuxppc-2.4/arch/ppc/kernel/pci-dma.c --- linux-2.4.22/arch/ppc/kernel/pci-dma.c 2003-02-15 08:07:09.000000000 +1100 +++ linuxppc-2.4/arch/ppc/kernel/pci-dma.c 2003-03-14 20:46:44.000000000 +1100 @@ -22,11 +22,18 @@ if (hwdev == NULL || hwdev->dma_mask != 0xffffffff) gfp |= GFP_DMA; + +#ifdef CONFIG_NOT_COHERENT_CACHE + ret = consistent_alloc(gfp, size, dma_handle); +#else ret = (void *)__get_free_pages(gfp, get_order(size)); +#endif if (ret != NULL) { memset(ret, 0, size); +#ifndef CONFIG_NOT_COHERENT_CACHE *dma_handle = virt_to_bus(ret); +#endif } return ret; } @@ -34,5 +41,9 @@ void pci_free_consistent(struct pci_dev *hwdev, size_t size, void *vaddr, dma_addr_t dma_handle) { +#ifdef CONFIG_NOT_COHERENT_CACHE + consistent_free(vaddr); +#else free_pages((unsigned long)vaddr, get_order(size)); +#endif } diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/ppc-stub.c linuxppc-2.4/arch/ppc/kernel/ppc-stub.c --- linux-2.4.22/arch/ppc/kernel/ppc-stub.c 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/ppc-stub.c 2003-08-26 11:57:12.000000000 +1000 @@ -135,7 +135,7 @@ /* typedef void (*trapfunc_t)(void); */ static void kgdb_fault_handler(struct pt_regs *regs); -static void handle_exception (struct pt_regs *regs); +static int handle_exception (struct pt_regs *regs); #if 0 /* Install an exception handler for kgdb */ @@ -387,14 +387,12 @@ int kgdb_bpt(struct pt_regs *regs) { - handle_exception(regs); - return 1; + return handle_exception(regs); } int kgdb_sstep(struct pt_regs *regs) { - handle_exception(regs); - return 1; + return handle_exception(regs); } void kgdb(struct pt_regs *regs) @@ -404,16 +402,14 @@ int kgdb_iabr_match(struct pt_regs *regs) { - printk("kgdb doesn't support iabr, what?!?\n"); - handle_exception(regs); - return 1; + printk(KERN_ERR "kgdb doesn't support iabr, what?!?\n"); + return handle_exception(regs); } int kgdb_dabr_match(struct pt_regs *regs) { - printk("kgdb doesn't support dabr, what?!?\n"); - handle_exception(regs); - return 1; + printk(KERN_ERR "kgdb doesn't support dabr, what?!?\n"); + return handle_exception(regs); } /* Convert the SPARC hardware trap type code to a unix signal number. */ @@ -459,7 +455,7 @@ /* * This function does all command processing for interfacing to gdb. */ -static void +static int handle_exception (struct pt_regs *regs) { int sigval; @@ -468,14 +464,19 @@ char *ptr; unsigned int msr; + /* We don't handle user-mode breakpoints. */ + if (user_mode(regs)) + return 0; + if (debugger_fault_handler) { debugger_fault_handler(regs); panic("kgdb longjump failed!\n"); } if (kgdb_active) { - printk("interrupt while in kgdb, returning\n"); - return; + printk(KERN_ERR "interrupt while in kgdb, returning\n"); + return 0; } + kgdb_active = 1; kgdb_started = 1; @@ -677,14 +678,14 @@ kgdb_interruptible(1); unlock_kernel(); kgdb_active = 0; - return; + return 1; case 's': kgdb_flush_cache_all(); regs->msr |= MSR_SE; unlock_kernel(); kgdb_active = 0; - return; + return 1; case 'r': /* Reset (if user process..exit ???)*/ panic("kgdb reset."); diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/ppc4xx_dma.c linuxppc-2.4/arch/ppc/kernel/ppc4xx_dma.c --- linux-2.4.22/arch/ppc/kernel/ppc4xx_dma.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/ppc4xx_dma.c 2003-05-26 09:40:53.000000000 +1000 @@ -0,0 +1,852 @@ +/* + * arch/ppc/kernel/ppc4xx_dma.c + * + * IBM PPC4xx DMA engine core library + * + * Copyright 2000-2003 MontaVista Software Inc. + * + * Cleaned by Matt Porter + * + * Original code by Armin Kuster + * and Pete Popov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS]; + +int +ppc4xx_get_dma_status(void) +{ + return (mfdcr(DCRN_DMASR)); +} + +void +ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr) +{ + switch (dmanr) { + case 0: +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_DMASAH0, (u32)(src_addr >> 32)); +#endif + mtdcr(DCRN_DMASA0, (u32)src_addr); + break; + case 1: +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_DMASAH1, (u32)(src_addr >> 32)); +#endif + mtdcr(DCRN_DMASA1, (u32)src_addr); + break; + case 2: +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_DMASAH2, (u32)(src_addr >> 32)); +#endif + mtdcr(DCRN_DMASA2, (u32)src_addr); + break; + case 3: +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_DMASAH3, (u32)(src_addr >> 32)); +#endif + mtdcr(DCRN_DMASA3, (u32)src_addr); + break; + default: + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) + printk("set_src_addr: bad channel: %d\n", dmanr); + } +} + +void +ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr) +{ + switch (dmanr) { + case 0: +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_DMADAH0, (u32)(dst_addr >> 32)); +#endif + mtdcr(DCRN_DMADA0, (u32)dst_addr); + break; + case 1: +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_DMADAH1, (u32)(dst_addr >> 32)); +#endif + mtdcr(DCRN_DMADA1, (u32)dst_addr); + break; + case 2: +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_DMADAH2, (u32)(dst_addr >> 32)); +#endif + mtdcr(DCRN_DMADA2, (u32)dst_addr); + break; + case 3: +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_DMADAH3, (u32)(dst_addr >> 32)); +#endif + mtdcr(DCRN_DMADA3, (u32)dst_addr); + break; + default: + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) + printk("set_dst_addr: bad channel: %d\n", dmanr); + } +} + + +void +ppc4xx_enable_dma(unsigned int dmanr) +{ + unsigned int control; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + unsigned int status_bits[] = { DMA_CS0 | DMA_TS0 | DMA_CH0_ERR, + DMA_CS1 | DMA_TS1 | DMA_CH1_ERR, + DMA_CS2 | DMA_TS2 | DMA_CH2_ERR, + DMA_CS3 | DMA_TS3 | DMA_CH3_ERR}; + + if (p_dma_ch->in_use) { + printk("enable_dma: channel %d in use\n", dmanr); + return; + } + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("enable_dma: bad channel: %d\n", dmanr); + return; + } + + if (p_dma_ch->mode == DMA_MODE_READ) { + /* peripheral to memory */ + ppc4xx_set_src_addr(dmanr, 0); + ppc4xx_set_dst_addr(dmanr, p_dma_ch->addr); + } else if (p_dma_ch->mode == DMA_MODE_WRITE) { + /* memory to peripheral */ + ppc4xx_set_src_addr(dmanr, p_dma_ch->addr); + ppc4xx_set_dst_addr(dmanr, 0); + } + + /* for other xfer modes, the addresses are already set */ + switch (dmanr) { + case 0: + control = mfdcr(DCRN_DMACR0); + break; + case 1: + control = mfdcr(DCRN_DMACR1); + break; + case 2: + control = mfdcr(DCRN_DMACR2); + break; + case 3: + control = mfdcr(DCRN_DMACR3); + break; + default: + printk("enable_dma: bad channel: %d\n", dmanr); + } + + control &= ~(DMA_TM_MASK | DMA_TD); /* clear all mode bits */ + if (p_dma_ch->mode == DMA_MODE_MM) { + /* software initiated memory to memory */ + control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE; + } + + switch (dmanr) { + case 0: + mtdcr(DCRN_DMACR0, control); + break; + case 1: + mtdcr(DCRN_DMACR1, control); + break; + case 2: + mtdcr(DCRN_DMACR2, control); + break; + case 3: + mtdcr(DCRN_DMACR3, control); + break; + default: + printk("enable_dma: bad channel: %d\n", dmanr); + } + + /* + * Clear the CS, TS, RI bits for the channel from DMASR. This + * has been observed to happen correctly only after the mode and + * ETD/DCE bits in DMACRx are set above. Must do this before + * enabling the channel. + */ + + mtdcr(DCRN_DMASR, status_bits[dmanr]); + + /* + * For device-paced transfers, Terminal Count Enable apparently + * must be on, and this must be turned on after the mode, etc. + * bits are cleared above (at least on Redwood-6). + */ + + if ((p_dma_ch->mode == DMA_MODE_MM_DEVATDST) || + (p_dma_ch->mode == DMA_MODE_MM_DEVATSRC)) + control |= DMA_TCE_ENABLE; + + /* + * Now enable the channel. + */ + + control |= (p_dma_ch->mode | DMA_CE_ENABLE); + + switch (dmanr) { + case 0: + mtdcr(DCRN_DMACR0, control); + break; + case 1: + mtdcr(DCRN_DMACR1, control); + break; + case 2: + mtdcr(DCRN_DMACR2, control); + break; + case 3: + mtdcr(DCRN_DMACR3, control); + break; + default: + printk("enable_dma: bad channel: %d\n", dmanr); + } + + p_dma_ch->in_use = 1; +} + +void +ppc4xx_disable_dma(unsigned int dmanr) +{ + unsigned int control; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + if (!p_dma_ch->in_use) { + printk("disable_dma: channel %d not in use\n", dmanr); + return; + } + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("disable_dma: bad channel: %d\n", dmanr); + return; + } + + switch (dmanr) { + case 0: + control = mfdcr(DCRN_DMACR0); + control &= ~DMA_CE_ENABLE; + mtdcr(DCRN_DMACR0, control); + break; + case 1: + control = mfdcr(DCRN_DMACR1); + control &= ~DMA_CE_ENABLE; + mtdcr(DCRN_DMACR1, control); + break; + case 2: + control = mfdcr(DCRN_DMACR2); + control &= ~DMA_CE_ENABLE; + mtdcr(DCRN_DMACR2, control); + break; + case 3: + control = mfdcr(DCRN_DMACR3); + control &= ~DMA_CE_ENABLE; + mtdcr(DCRN_DMACR3, control); + break; + default: + printk("disable_dma: bad channel: %d\n", dmanr); + } + + p_dma_ch->in_use = 0; +} + +/* + * Sets the dma mode for single DMA transfers only. + * For scatter/gather transfers, the mode is passed to the + * alloc_dma_handle() function as one of the parameters. + * + * The mode is simply saved and used later. This allows + * the driver to call set_dma_mode() and set_dma_addr() in + * any order. + * + * Valid mode values are: + * + * DMA_MODE_READ peripheral to memory + * DMA_MODE_WRITE memory to peripheral + * DMA_MODE_MM memory to memory + * DMA_MODE_MM_DEVATSRC device-paced memory to memory, device at src + * DMA_MODE_MM_DEVATDST device-paced memory to memory, device at dst + */ +int +ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode) +{ + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("set_dma_mode: bad channel 0x%x\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + p_dma_ch->mode = mode; + + return DMA_STATUS_GOOD; +} + +/* + * Sets the DMA Count register. Note that 'count' is in bytes. + * However, the DMA Count register counts the number of "transfers", + * where each transfer is equal to the bus width. Thus, count + * MUST be a multiple of the bus width. + */ +void +ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count) +{ + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + +#ifdef DEBUG_4xxDMA + { + int error = 0; + switch (p_dma_ch->pwidth) { + case PW_8: + break; + case PW_16: + if (count & 0x1) + error = 1; + break; + case PW_32: + if (count & 0x3) + error = 1; + break; + case PW_64: + if (count & 0x7) + error = 1; + break; + default: + printk("set_dma_count: invalid bus width: 0x%x\n", + p_dma_ch->pwidth); + return; + } + if (error) + printk + ("Warning: set_dma_count count 0x%x bus width %d\n", + count, p_dma_ch->pwidth); + } +#endif + + count = count >> p_dma_ch->shift; + switch (dmanr) { + case 0: + mtdcr(DCRN_DMACT0, count); + break; + case 1: + mtdcr(DCRN_DMACT1, count); + break; + case 2: + mtdcr(DCRN_DMACT2, count); + break; + case 3: + mtdcr(DCRN_DMACT3, count); + break; + default: + printk("ppc4xx_set_dma_count: bad channel: %d\n", dmanr); + } +} + +/* + * Returns the number of bytes left to be transfered. + * After a DMA transfer, this should return zero. + * Reading this while a DMA transfer is still in progress will return + * unpredictable results. + */ +int +ppc4xx_get_dma_residue(unsigned int dmanr) +{ + unsigned int count; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + switch (dmanr) { + case 0: + count = mfdcr(DCRN_DMACT0); + break; + case 1: + count = mfdcr(DCRN_DMACT1); + break; + case 2: + count = mfdcr(DCRN_DMACT2); + break; + case 3: + count = mfdcr(DCRN_DMACT3); + break; + default: + printk("ppc4xx_get_dma_residue: bad channel: %d\n", dmanr); + return 0; + } + + return (count << p_dma_ch->shift); +} + +/* + * Sets the DMA address for a memory to peripheral or peripheral + * to memory transfer. The address is just saved in the channel + * structure for now and used later in enable_dma(). + */ +void +ppc4xx_set_dma_addr(unsigned int dmanr, phys_addr_t addr) +{ + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_set_dma_addr: bad channel: %d\n", dmanr); + return; + } + +#ifdef DEBUG_4xxDMA + { + int error = 0; + switch (p_dma_ch->pwidth) { + case PW_8: + break; + case PW_16: + if ((unsigned) addr & 0x1) + error = 1; + break; + case PW_32: + if ((unsigned) addr & 0x3) + error = 1; + break; + case PW_64: + if ((unsigned) addr & 0x7) + error = 1; + break; + default: + printk("ppc4xx_set_dma_addr: invalid bus width: 0x%x\n", + p_dma_ch->pwidth); + return; + } + if (error) + printk("Warning: ppc4xx_set_dma_addr addr 0x%x bus width %d\n", + addr, p_dma_ch->pwidth); + } +#endif + + /* save dma address and program it later after we know the xfer mode */ + p_dma_ch->addr = addr; +} + +/* + * Sets both DMA addresses for a memory to memory transfer. + * For memory to peripheral or peripheral to memory transfers + * the function set_dma_addr() should be used instead. + */ +void +ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr, + phys_addr_t dst_dma_addr) +{ +#ifdef DEBUG_4xxDMA + { + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + int error = 0; + switch (p_dma_ch->pwidth) { + case PW_8: + break; + case PW_16: + if (((unsigned) src_dma_addr & 0x1) || + ((unsigned) dst_dma_addr & 0x1) + ) + error = 1; + break; + case PW_32: + if (((unsigned) src_dma_addr & 0x3) || + ((unsigned) dst_dma_addr & 0x3) + ) + error = 1; + break; + case PW_64: + if (((unsigned) src_dma_addr & 0x7) || + ((unsigned) dst_dma_addr & 0x7) + ) + error = 1; + break; + default: + printk("ppc4xx_set_dma_addr2: invalid bus width: 0x%x\n", + p_dma_ch->pwidth); + return; + } + if (error) + printk + ("Warning: ppc4xx_set_dma_addr2 src 0x%x dst 0x%x bus width %d\n", + src_dma_addr, dst_dma_addr, p_dma_ch->pwidth); + } +#endif + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_set_dma_addr2: bad channel: %d\n", dmanr); + } + else { + ppc4xx_set_src_addr(dmanr, src_dma_addr); + ppc4xx_set_dst_addr(dmanr, dst_dma_addr); + } +} + +/* + * Enables the channel interrupt. + * + * If performing a scatter/gatter transfer, this function + * MUST be called before calling alloc_dma_handle() and building + * the sgl list. Otherwise, interrupts will not be enabled, if + * they were previously disabled. + */ +int +ppc4xx_enable_dma_interrupt(unsigned int dmanr) +{ + unsigned int control; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + p_dma_ch->int_enable = 1; + switch (dmanr) { + case 0: + control = mfdcr(DCRN_DMACR0); + control |= DMA_CIE_ENABLE; /* Channel Interrupt Enable */ + mtdcr(DCRN_DMACR0, control); + break; + case 1: + control = mfdcr(DCRN_DMACR1); + control |= DMA_CIE_ENABLE; + mtdcr(DCRN_DMACR1, control); + break; + case 2: + control = mfdcr(DCRN_DMACR2); + control |= DMA_CIE_ENABLE; + mtdcr(DCRN_DMACR2, control); + break; + case 3: + control = mfdcr(DCRN_DMACR3); + control |= DMA_CIE_ENABLE; + mtdcr(DCRN_DMACR3, control); + break; + default: + printk("ppc4xx_enable_dma_interrupt: bad channel: %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + return DMA_STATUS_GOOD; +} + +/* + * Disables the channel interrupt. + * + * If performing a scatter/gatter transfer, this function + * MUST be called before calling alloc_dma_handle() and building + * the sgl list. Otherwise, interrupts will not be disabled, if + * they were previously enabled. + */ +int +ppc4xx_disable_dma_interrupt(unsigned int dmanr) +{ + unsigned int control; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + p_dma_ch->int_enable = 0; + switch (dmanr) { + case 0: + control = mfdcr(DCRN_DMACR0); + control &= ~DMA_CIE_ENABLE; /* Channel Interrupt Enable */ + mtdcr(DCRN_DMACR0, control); + break; + case 1: + control = mfdcr(DCRN_DMACR1); + control &= ~DMA_CIE_ENABLE; + mtdcr(DCRN_DMACR1, control); + break; + case 2: + control = mfdcr(DCRN_DMACR2); + control &= ~DMA_CIE_ENABLE; + mtdcr(DCRN_DMACR2, control); + break; + case 3: + control = mfdcr(DCRN_DMACR3); + control &= ~DMA_CIE_ENABLE; + mtdcr(DCRN_DMACR3, control); + break; + default: + printk("ppc4xx_disable_dma_interrupt: bad channel: %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + return DMA_STATUS_GOOD; +} + +/* + * Configures a DMA channel, including the peripheral bus width, if a + * peripheral is attached to the channel, the polarity of the DMAReq and + * DMAAck signals, etc. This information should really be setup by the boot + * code, since most likely the configuration won't change dynamically. + * If the kernel has to call this function, it's recommended that it's + * called from platform specific init code. The driver should not need to + * call this function. + */ +int +ppc4xx_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t * p_init) +{ + unsigned int polarity; + uint32_t control = 0; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + DMA_MODE_READ = (unsigned long) DMA_TD; /* Peripheral to Memory */ + DMA_MODE_WRITE = 0; /* Memory to Peripheral */ + + if (!p_init) { + printk("ppc4xx_init_dma_channel: NULL p_init\n"); + return DMA_STATUS_NULL_POINTER; + } + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_init_dma_channel: bad channel %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + +#if DCRN_POL > 0 + polarity = mfdcr(DCRN_POL); +#else + polarity = 0; +#endif + + /* Setup the control register based on the values passed to + * us in p_init. Then, over-write the control register with this + * new value. + */ + control |= SET_DMA_CONTROL; + + switch (dmanr) { + case 0: + /* clear all polarity signals and then "or" in new signal levels */ + polarity &= ~GET_DMA_POLARITY(0); + polarity |= p_dma_ch->polarity; +#if DCRN_POL > 0 + mtdcr(DCRN_POL, polarity); +#endif + mtdcr(DCRN_DMACR0, control); + break; + case 1: + polarity &= ~GET_DMA_POLARITY(1); + polarity |= p_dma_ch->polarity; +#if DCRN_POL > 0 + mtdcr(DCRN_POL, polarity); +#endif + mtdcr(DCRN_DMACR1, control); + break; + case 2: + polarity &= ~GET_DMA_POLARITY(2); + polarity |= p_dma_ch->polarity; +#if DCRN_POL > 0 + mtdcr(DCRN_POL, polarity); +#endif + mtdcr(DCRN_DMACR2, control); + break; + case 3: + polarity &= ~GET_DMA_POLARITY(3); + polarity |= p_dma_ch->polarity; +#if DCRN_POL > 0 + mtdcr(DCRN_POL, polarity); +#endif + mtdcr(DCRN_DMACR3, control); + break; + default: + return DMA_STATUS_BAD_CHANNEL; + } + + /* save these values in our dma channel structure */ + memcpy(p_dma_ch, p_init, sizeof (ppc_dma_ch_t)); + + /* + * The peripheral width values written in the control register are: + * PW_8 0 + * PW_16 1 + * PW_32 2 + * PW_64 3 + * + * Since the DMA count register takes the number of "transfers", + * we need to divide the count sent to us in certain + * functions by the appropriate number. It so happens that our + * right shift value is equal to the peripheral width value. + */ + p_dma_ch->shift = p_init->pwidth; + + /* + * Save the control word for easy access. + */ + p_dma_ch->control = control; + + mtdcr(DCRN_DMASR, 0xffffffff); /* clear status register */ + return DMA_STATUS_GOOD; +} + +/* + * This function returns the channel configuration. + */ +int +ppc4xx_get_channel_config(unsigned int dmanr, ppc_dma_ch_t * p_dma_ch) +{ + unsigned int polarity; + unsigned int control; + +#if DCRN_POL > 0 + polarity = mfdcr(DCRN_POL); +#else + polarity = 0; +#endif + + switch (dmanr) { + case 0: + p_dma_ch->polarity = polarity & GET_DMA_POLARITY(0); + control = mfdcr(DCRN_DMACR0); + break; + case 1: + p_dma_ch->polarity = polarity & GET_DMA_POLARITY(1); + control = mfdcr(DCRN_DMACR1); + break; + case 2: + p_dma_ch->polarity = polarity & GET_DMA_POLARITY(2); + control = mfdcr(DCRN_DMACR2); + break; + case 3: + p_dma_ch->polarity = polarity & GET_DMA_POLARITY(3); + control = mfdcr(DCRN_DMACR3); + break; + default: + return DMA_STATUS_BAD_CHANNEL; + } + + p_dma_ch->cp = GET_DMA_PRIORITY(control); + p_dma_ch->pwidth = GET_DMA_PW(control); + p_dma_ch->psc = GET_DMA_PSC(control); + p_dma_ch->pwc = GET_DMA_PWC(control); + p_dma_ch->phc = GET_DMA_PHC(control); + p_dma_ch->ce = GET_DMA_CE_ENABLE(control); + p_dma_ch->int_enable = GET_DMA_CIE_ENABLE(control); + p_dma_ch->shift = GET_DMA_PW(control); + +#ifdef CONFIG_PPC4xx_EDMA + p_dma_ch->pf = GET_DMA_PREFETCH(control); +#else + p_dma_ch->ch_enable = GET_DMA_CH(control); + p_dma_ch->ece_enable = GET_DMA_ECE(control); + p_dma_ch->tcd_disable = GET_DMA_TCD(control); +#endif + return DMA_STATUS_GOOD; +} + +/* + * Sets the priority for the DMA channel dmanr. + * Since this is setup by the hardware init function, this function + * can be used to dynamically change the priority of a channel. + * + * Acceptable priorities: + * + * PRIORITY_LOW + * PRIORITY_MID_LOW + * PRIORITY_MID_HIGH + * PRIORITY_HIGH + * + */ +int +ppc4xx_set_channel_priority(unsigned int dmanr, unsigned int priority) +{ + unsigned int control; + + if ((priority != PRIORITY_LOW) && + (priority != PRIORITY_MID_LOW) && + (priority != PRIORITY_MID_HIGH) && (priority != PRIORITY_HIGH)) { + printk("ppc4xx_set_channel_priority: bad priority: 0x%x\n", priority); + } + + switch (dmanr) { + case 0: + control = mfdcr(DCRN_DMACR0); + control |= SET_DMA_PRIORITY(priority); + mtdcr(DCRN_DMACR0, control); + break; + case 1: + control = mfdcr(DCRN_DMACR1); + control |= SET_DMA_PRIORITY(priority); + mtdcr(DCRN_DMACR1, control); + break; + case 2: + control = mfdcr(DCRN_DMACR2); + control |= SET_DMA_PRIORITY(priority); + mtdcr(DCRN_DMACR2, control); + break; + case 3: + control = mfdcr(DCRN_DMACR3); + control |= SET_DMA_PRIORITY(priority); + mtdcr(DCRN_DMACR3, control); + break; + default: + printk("ppc4xx_set_channel_priority: bad channel: %d\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + return DMA_STATUS_GOOD; +} + +/* + * Returns the width of the peripheral attached to this channel. This assumes + * that someone who knows the hardware configuration, boot code or some other + * init code, already set the width. + * + * The return value is one of: + * PW_8 + * PW_16 + * PW_32 + * PW_64 + * + * The function returns 0 on error. + */ +unsigned int +ppc4xx_get_peripheral_width(unsigned int dmanr) +{ + unsigned int control; + + switch (dmanr) { + case 0: + control = mfdcr(DCRN_DMACR0); + break; + case 1: + control = mfdcr(DCRN_DMACR1); + break; + case 2: + control = mfdcr(DCRN_DMACR2); + break; + case 3: + control = mfdcr(DCRN_DMACR3); + break; + default: + printk("ppc4xx_get_peripheral_width: bad channel: %d\n", dmanr); + return 0; + } + return (GET_DMA_PW(control)); +} + + +EXPORT_SYMBOL(ppc4xx_init_dma_channel); +EXPORT_SYMBOL(ppc4xx_get_channel_config); +EXPORT_SYMBOL(ppc4xx_set_channel_priority); +EXPORT_SYMBOL(ppc4xx_get_peripheral_width); +EXPORT_SYMBOL(dma_channels); +EXPORT_SYMBOL(ppc4xx_set_src_addr); +EXPORT_SYMBOL(ppc4xx_set_dst_addr); +EXPORT_SYMBOL(ppc4xx_set_dma_addr); +EXPORT_SYMBOL(ppc4xx_set_dma_addr2); +EXPORT_SYMBOL(ppc4xx_enable_dma); +EXPORT_SYMBOL(ppc4xx_disable_dma); +EXPORT_SYMBOL(ppc4xx_set_dma_mode); +EXPORT_SYMBOL(ppc4xx_set_dma_count); +EXPORT_SYMBOL(ppc4xx_get_dma_residue); +EXPORT_SYMBOL(ppc4xx_enable_dma_interrupt); +EXPORT_SYMBOL(ppc4xx_disable_dma_interrupt); +EXPORT_SYMBOL(ppc4xx_get_dma_status); diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/ppc4xx_sgdma.c linuxppc-2.4/arch/ppc/kernel/ppc4xx_sgdma.c --- linux-2.4.22/arch/ppc/kernel/ppc4xx_sgdma.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/ppc4xx_sgdma.c 2003-05-26 09:40:53.000000000 +1000 @@ -0,0 +1,559 @@ +/* + * arch/ppc/kernel/ppc4xx_sgdma.c + * + * IBM PPC4xx DMA engine scatter/gather library + * + * Copyright 2002-2003 MontaVista Software Inc. + * + * Cleaned by Matt Porter + * + * Original code by Armin Kuster + * and Pete Popov + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static __inline__ void +ppc4xx_set_sg_addr(int dmanr, phys_addr_t sg_addr) +{ + switch (dmanr) { + case 0: +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_ASGH0, (u32)(sg_addr >> 32)); +#endif + mtdcr(DCRN_ASG0, (u32)sg_addr); + break; + case 1: +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_ASGH1, (u32)(sg_addr >> 32)); +#endif + mtdcr(DCRN_ASG1, (u32)sg_addr); + break; + case 2: +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_ASGH2, (u32)(sg_addr >> 32)); +#endif + mtdcr(DCRN_ASG2, sg_addr); + break; + case 3: +#ifdef PPC4xx_DMA_64BIT + mtdcr(DCRN_ASGH3, (u32)(sg_addr >> 32)); +#endif + mtdcr(DCRN_ASG3, (u32)sg_addr); + break; + } +} + + +/* + * Add a new sgl descriptor to the end of a scatter/gather list + * which was created by alloc_dma_handle(). + * + * For a memory to memory transfer, both dma addresses must be + * valid. For a peripheral to memory transfer, one of the addresses + * must be set to NULL, depending on the direction of the transfer: + * memory to peripheral: set dst_addr to NULL, + * peripheral to memory: set src_addr to NULL. + */ +static __inline__ int +ppc4xx_add_dma_sgl(sgl_handle_t handle, phys_addr_t src_addr, phys_addr_t dst_addr, + unsigned int count) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *) handle; + ppc_dma_ch_t *p_dma_ch; + + if (!handle) { + printk("ppc4xx_add_dma_sgl: null handle\n"); + return DMA_STATUS_BAD_HANDLE; + } + + if (psgl->dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_add_dma_sgl: bad channel: %d\n", psgl->dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + p_dma_ch = &dma_channels[psgl->dmanr]; + +#ifdef DEBUG_4xxDMA + { + int error = 0; + unsigned int aligned = + (unsigned) src_addr | (unsigned) dst_addr | count; + switch (p_dma_ch->pwidth) { + case PW_8: + break; + case PW_16: + if (aligned & 0x1) + error = 1; + break; + case PW_32: + if (aligned & 0x3) + error = 1; + break; + case PW_64: + if (aligned & 0x7) + error = 1; + break; + default: + printk("ppc4xx_add_dma_sgl: invalid bus width: 0x%x\n", + p_dma_ch->pwidth); + return DMA_STATUS_GENERAL_ERROR; + } + if (error) + printk + ("Alignment warning: ppc4xx_add_dma_sgl src 0x%x dst 0x%x count 0x%x bus width var %d\n", + src_addr, dst_addr, count, p_dma_ch->pwidth); + + } +#endif + + if ((unsigned) (psgl->ptail + 1) >= ((unsigned) psgl + SGL_LIST_SIZE)) { + printk("sgl handle out of memory \n"); + return DMA_STATUS_OUT_OF_MEMORY; + } + + if (!psgl->ptail) { + psgl->phead = (ppc_sgl_t *) + ((unsigned) psgl + sizeof (sgl_list_info_t)); + psgl->ptail = psgl->phead; + } else { + psgl->ptail->next = virt_to_bus(psgl->ptail + 1); + psgl->ptail++; + } + + psgl->ptail->control = psgl->control; + psgl->ptail->src_addr = src_addr; + psgl->ptail->dst_addr = dst_addr; + psgl->ptail->control_count = (count >> p_dma_ch->shift) | + psgl->sgl_control; + psgl->ptail->next = (uint32_t) NULL; + + return DMA_STATUS_GOOD; +} + +/* + * Enable (start) the DMA described by the sgl handle. + */ +static __inline__ void +ppc4xx_enable_dma_sgl(sgl_handle_t handle) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *) handle; + ppc_dma_ch_t *p_dma_ch; + uint32_t sg_command; + + if (!handle) { + printk("ppc4xx_enable_dma_sgl: null handle\n"); + return; + } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) { + printk("ppc4xx_enable_dma_sgl: bad channel in handle %d\n", + psgl->dmanr); + return; + } else if (!psgl->phead) { + printk("ppc4xx_enable_dma_sgl: sg list empty\n"); + return; + } + + p_dma_ch = &dma_channels[psgl->dmanr]; + psgl->ptail->control_count &= ~SG_LINK; /* make this the last dscrptr */ + sg_command = mfdcr(DCRN_ASGC); + + ppc4xx_set_sg_addr(psgl->dmanr, virt_to_bus(psgl->phead)); + + switch (psgl->dmanr) { + case 0: + sg_command |= SSG0_ENABLE; + break; + case 1: + sg_command |= SSG1_ENABLE; + break; + case 2: + sg_command |= SSG2_ENABLE; + break; + case 3: + sg_command |= SSG3_ENABLE; + break; + default: + printk("ppc4xx_enable_dma_sgl: bad channel: %d\n", psgl->dmanr); + } + +#if 0 /* debug */ + printk("\n\nppc4xx_enable_dma_sgl at dma_addr 0x%x\n", + virt_to_bus(psgl->phead)); + { + ppc_sgl_t *pnext, *sgl_addr; + + pnext = psgl->phead; + while (pnext) { + printk("dma descriptor at 0x%x, dma addr 0x%x\n", + (unsigned) pnext, (unsigned) virt_to_bus(pnext)); + printk + ("control 0x%x src 0x%x dst 0x%x c_count 0x%x, next 0x%x\n", + (unsigned) pnext->control, + (unsigned) pnext->src_addr, + (unsigned) pnext->dst_addr, + (unsigned) pnext->control_count, + (unsigned) pnext->next); + + (unsigned) pnext = bus_to_virt(pnext->next); + } + printk("sg_command 0x%x\n", sg_command); + } +#endif + mtdcr(DCRN_ASGC, sg_command); /* start transfer */ +} + +/* + * Halt an active scatter/gather DMA operation. + */ +static __inline__ void +ppc4xx_disable_dma_sgl(sgl_handle_t handle) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *) handle; + uint32_t sg_command; + + if (!handle) { + printk("ppc4xx_enable_dma_sgl: null handle\n"); + return; + } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) { + printk("ppc4xx_enable_dma_sgl: bad channel in handle %d\n", + psgl->dmanr); + return; + } + + sg_command = mfdcr(DCRN_ASGC); + switch (psgl->dmanr) { + case 0: + sg_command &= ~SSG0_ENABLE; + break; + case 1: + sg_command &= ~SSG1_ENABLE; + break; + case 2: + sg_command &= ~SSG2_ENABLE; + break; + case 3: + sg_command &= ~SSG3_ENABLE; + break; + default: + printk("ppc4xx_enable_dma_sgl: bad channel: %d\n", psgl->dmanr); + } + + mtdcr(DCRN_ASGC, sg_command); /* stop transfer */ +} + +/* + * Returns number of bytes left to be transferred from the entire sgl list. + * *src_addr and *dst_addr get set to the source/destination address of + * the sgl descriptor where the DMA stopped. + * + * An sgl transfer must NOT be active when this function is called. + */ +static __inline__ int +ppc4xx_get_dma_sgl_residue(sgl_handle_t handle, phys_addr_t * src_addr, + phys_addr_t * dst_addr) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *) handle; + ppc_dma_ch_t *p_dma_ch; + ppc_sgl_t *pnext, *sgl_addr; + uint32_t count_left; + + if (!handle) { + printk("ppc4xx_get_dma_sgl_residue: null handle\n"); + return DMA_STATUS_BAD_HANDLE; + } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) { + printk("ppc4xx_get_dma_sgl_residue: bad channel in handle %d\n", + psgl->dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + switch (psgl->dmanr) { + case 0: + sgl_addr = (ppc_sgl_t *) bus_to_virt(mfdcr(DCRN_ASG0)); + count_left = mfdcr(DCRN_DMACT0); + break; + case 1: + sgl_addr = (ppc_sgl_t *) bus_to_virt(mfdcr(DCRN_ASG1)); + count_left = mfdcr(DCRN_DMACT1); + break; + case 2: + sgl_addr = (ppc_sgl_t *) bus_to_virt(mfdcr(DCRN_ASG2)); + count_left = mfdcr(DCRN_DMACT2); + break; + case 3: + sgl_addr = (ppc_sgl_t *) bus_to_virt(mfdcr(DCRN_ASG3)); + count_left = mfdcr(DCRN_DMACT3); + break; + default: + printk("ppc4xx_get_dma_sgl_residue: bad channel %d\n", psgl->dmanr); + goto error; + } + + if (!sgl_addr) { + printk("ppc4xx_get_dma_sgl_residue: sgl addr register is null\n"); + goto error; + } + + pnext = psgl->phead; + while (pnext && + ((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE) && + (pnext != sgl_addr)) + ) { + pnext++; + } + + if (pnext == sgl_addr) { /* found the sgl descriptor */ + + *src_addr = pnext->src_addr; + *dst_addr = pnext->dst_addr; + + /* + * Now search the remaining descriptors and add their count. + * We already have the remaining count from this descriptor in + * count_left. + */ + pnext++; + + while ((pnext != psgl->ptail) && + ((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE)) + ) { + count_left += pnext->control_count & SG_COUNT_MASK; + } + + if (pnext != psgl->ptail) { /* should never happen */ + printk + ("ppc4xx_get_dma_sgl_residue error (1) psgl->ptail 0x%x handle 0x%x\n", + (unsigned int) psgl->ptail, (unsigned int) handle); + goto error; + } + + /* success */ + p_dma_ch = &dma_channels[psgl->dmanr]; + return (count_left << p_dma_ch->shift); /* count in bytes */ + + } else { + /* this shouldn't happen */ + printk + ("get_dma_sgl_residue, unable to match current address 0x%x, handle 0x%x\n", + (unsigned int) sgl_addr, (unsigned int) handle); + + } + + error: + *src_addr = (phys_addr_t) NULL; + *dst_addr = (phys_addr_t) NULL; + return 0; +} + +/* + * Returns the address(es) of the buffer(s) contained in the head element of + * the scatter/gather list. The element is removed from the scatter/gather + * list and the next element becomes the head. + * + * This function should only be called when the DMA is not active. + */ +static __inline__ int +ppc4xx_delete_dma_sgl_element(sgl_handle_t handle, phys_addr_t * src_dma_addr, + phys_addr_t * dst_dma_addr) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *) handle; + + if (!handle) { + printk("ppc4xx_delete_sgl_element: null handle\n"); + return DMA_STATUS_BAD_HANDLE; + } else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) { + printk("ppc4xx_delete_sgl_element: bad channel in handle %d\n", + psgl->dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + if (!psgl->phead) { + printk("ppc4xx_delete_sgl_element: sgl list empty\n"); + *src_dma_addr = (phys_addr_t) NULL; + *dst_dma_addr = (phys_addr_t) NULL; + return DMA_STATUS_SGL_LIST_EMPTY; + } + + *src_dma_addr = (phys_addr_t) psgl->phead->src_addr; + *dst_dma_addr = (phys_addr_t) psgl->phead->dst_addr; + + if (psgl->phead == psgl->ptail) { + /* last descriptor on the list */ + psgl->phead = NULL; + psgl->ptail = NULL; + } else { + psgl->phead++; + } + + return DMA_STATUS_GOOD; +} + + +/* + * Create a scatter/gather list handle. This is simply a structure which + * describes a scatter/gather list. + * + * A handle is returned in "handle" which the driver should save in order to + * be able to access this list later. A chunk of memory will be allocated + * to be used by the API for internal management purposes, including managing + * the sg list and allocating memory for the sgl descriptors. One page should + * be more than enough for that purpose. Perhaps it's a bit wasteful to use + * a whole page for a single sg list, but most likely there will be only one + * sg list per channel. + * + * Interrupt notes: + * Each sgl descriptor has a copy of the DMA control word which the DMA engine + * loads in the control register. The control word has a "global" interrupt + * enable bit for that channel. Interrupts are further qualified by a few bits + * in the sgl descriptor count register. In order to setup an sgl, we have to + * know ahead of time whether or not interrupts will be enabled at the completion + * of the transfers. Thus, enable_dma_interrupt()/disable_dma_interrupt() MUST + * be called before calling alloc_dma_handle(). If the interrupt mode will never + * change after powerup, then enable_dma_interrupt()/disable_dma_interrupt() + * do not have to be called -- interrupts will be enabled or disabled based + * on how the channel was configured after powerup by the hw_init_dma_channel() + * function. Each sgl descriptor will be setup to interrupt if an error occurs; + * however, only the last descriptor will be setup to interrupt. Thus, an + * interrupt will occur (if interrupts are enabled) only after the complete + * sgl transfer is done. + */ +int +ppc4xx_alloc_dma_handle(sgl_handle_t * phandle, unsigned int mode, unsigned int dmanr) +{ + sgl_list_info_t *psgl; + dma_addr_t dma_addr; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + uint32_t sg_command; + void *ret; + + if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) { + printk("ppc4xx_alloc_dma_handle: invalid channel 0x%x\n", dmanr); + return DMA_STATUS_BAD_CHANNEL; + } + + if (!phandle) { + printk("ppc4xx_alloc_dma_handle: null handle pointer\n"); + return DMA_STATUS_NULL_POINTER; + } + + /* Get a page of memory, which is zeroed out by consistent_alloc() */ + ret = consistent_alloc(GFP_KERNEL, DMA_PPC4xx_SIZE, &dma_addr); + if (ret != NULL) { + memset(ret, 0, DMA_PPC4xx_SIZE); + psgl = (sgl_list_info_t *) ret; + } + + if (psgl == NULL) { + *phandle = (sgl_handle_t) NULL; + return DMA_STATUS_OUT_OF_MEMORY; + } + + psgl->dma_addr = dma_addr; + psgl->dmanr = dmanr; + + /* + * Modify and save the control word. These words will be + * written to each sgl descriptor. The DMA engine then + * loads this control word into the control register + * every time it reads a new descriptor. + */ + psgl->control = p_dma_ch->control; + /* Clear all mode bits */ + psgl->control &= ~(DMA_TM_MASK | DMA_TD); + /* Save control word and mode */ + psgl->control |= (mode | DMA_CE_ENABLE); + + /* In MM mode, we must set ETD/TCE */ + if (mode == DMA_MODE_MM) + psgl->control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE; + + if (p_dma_ch->int_enable) { + /* Enable channel interrupt */ + psgl->control |= DMA_CIE_ENABLE; + } else { + psgl->control &= ~DMA_CIE_ENABLE; + } + + sg_command = mfdcr(DCRN_ASGC); + switch (dmanr) { + case 0: + sg_command |= SSG0_MASK_ENABLE; + break; + case 1: + sg_command |= SSG1_MASK_ENABLE; + break; + case 2: + sg_command |= SSG2_MASK_ENABLE; + break; + case 3: + sg_command |= SSG3_MASK_ENABLE; + break; + default: + printk("ppc4xx_alloc_dma_handle: bad channel: %d\n", dmanr); + ppc4xx_free_dma_handle((sgl_handle_t) psgl); + *phandle = (sgl_handle_t) NULL; + return DMA_STATUS_BAD_CHANNEL; + } + + /* Enable SGL control access */ + mtdcr(DCRN_ASGC, sg_command); + psgl->sgl_control = SG_ERI_ENABLE | SG_LINK; + + if (p_dma_ch->int_enable) { + if (p_dma_ch->tce_enable) + psgl->sgl_control |= SG_TCI_ENABLE; + else + psgl->sgl_control |= SG_ETI_ENABLE; + } + + *phandle = (sgl_handle_t) psgl; + return DMA_STATUS_GOOD; +} + +/* + * Destroy a scatter/gather list handle that was created by alloc_dma_handle(). + * The list must be empty (contain no elements). + */ +void +ppc4xx_free_dma_handle(sgl_handle_t handle) +{ + sgl_list_info_t *psgl = (sgl_list_info_t *) handle; + + if (!handle) { + printk("ppc4xx_free_dma_handle: got NULL\n"); + return; + } else if (psgl->phead) { + printk("ppc4xx_free_dma_handle: list not empty\n"); + return; + } else if (!psgl->dma_addr) { /* should never happen */ + printk("ppc4xx_free_dma_handle: no dma address\n"); + return; + } + + consistent_free((void *) psgl); +} + +EXPORT_SYMBOL(ppc4xx_alloc_dma_handle); +EXPORT_SYMBOL(ppc4xx_free_dma_handle); +EXPORT_SYMBOL(ppc4xx_add_dma_sgl); +EXPORT_SYMBOL(ppc4xx_delete_dma_sgl_element); +EXPORT_SYMBOL(ppc4xx_enable_dma_sgl); +EXPORT_SYMBOL(ppc4xx_disable_dma_sgl); +EXPORT_SYMBOL(ppc4xx_get_dma_sgl_residue); diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/ppc4xx_stbdma.c linuxppc-2.4/arch/ppc/kernel/ppc4xx_stbdma.c --- linux-2.4.22/arch/ppc/kernel/ppc4xx_stbdma.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/ppc4xx_stbdma.c 2003-05-26 09:40:53.000000000 +1000 @@ -0,0 +1,134 @@ +/* + * arch/ppc/kernel/ppc4xx_stbdma.c + * + * BRIEF MODULE DESCRIPTION + * IBM PPC4xx STBxxxx DMA Controller Functions + * + * Copyright 2002 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * Armin Kuster + * + * Based on ppc4xx_dma.c by + * ppopov@mvista.com or source@mvista. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int +ppc4xx_clr_dma_status(unsigned int dmanr) +{ + unsigned int control; + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + p_dma_ch->int_enable = 1; + + switch (dmanr) { + case 0: + control = DMA_CS0 | DMA_CH0_ERR | DMA_CT0; + break; + case 1: + control = DMA_CS1 | DMA_CH1_ERR | DMA_CT1; + break; + case 2: + control = DMA_CS2 | DMA_CH2_ERR | DMA_CT2; + break; + case 3: + control = DMA_CS3 | DMA_CH3_ERR | DMA_CT3; + break; + default: +#ifdef DEBUG_4xxDMA + printk("ppc4xx_clr_dma_status: bad channel: %d\n", dmanr); +#endif + return DMA_STATUS_BAD_CHANNEL; + } + mtdcr(DCRN_DMASR, control); + return DMA_STATUS_GOOD; +} + +/* + * Maps a given port to a one of the dma + * channels + */ +int +ppc4xx_map_dma_port(unsigned int dmanr, unsigned int ocp_dma,short dma_chan) +{ + unsigned int map; + int connect_port_to_chan, select; + + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + connect_port_to_chan = ((ocp_dma & 0x7)*4); + + select = ocp_dma >> 3; + switch (select) { + case 0: + map = mfdcr(DCRN_DMAS1); + map |= (connect_port_to_chan << dma_chan); /* */ + mtdcr(DCRN_DMAS1, map); + break; + case 1: + map = mfdcr(DCRN_DMAS2); + map |= (connect_port_to_chan << dma_chan); + mtdcr(DCRN_DMAS2, map); + break; + default: +#ifdef DEBUG_4xxDMA + printk("map_dma_port: bad channel: %d\n", dmanr); +#endif + return DMA_STATUS_BAD_CHANNEL; + } + return DMA_STATUS_GOOD; +} + +int +ppc4xx_disable_dma_port(unsigned int dmanr, unsigned int ocp_dma,short dma_chan) +{ + unsigned int map; + int connect_port_to_chan, select; + + ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr]; + + connect_port_to_chan = ((ocp_dma & 0x7)*4); + + select = ocp_dma >> 3; + switch (select) { + case 0: + map = mfdcr(DCRN_DMAS1); + map &= ~(connect_port_to_chan << dma_chan); /* */ + mtdcr(DCRN_DMAS1, map); + break; + case 1: + map = mfdcr(DCRN_DMAS2); + map &= ~(connect_port_to_chan << dma_chan); + mtdcr(DCRN_DMAS2, map); + break; + default: +#ifdef DEBUG_4xxDMA + printk("disable_dma_port: bad channel: %d\n", dmanr); +#endif + return DMA_STATUS_BAD_CHANNEL; + } + return DMA_STATUS_GOOD; +} + +EXPORT_SYMBOL(ppc4xx_disable_dma_port); +EXPORT_SYMBOL(ppc4xx_map_dma_port); +EXPORT_SYMBOL(ppc4xx_clr_dma_status); diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/ppc_ksyms.c linuxppc-2.4/arch/ppc/kernel/ppc_ksyms.c --- linux-2.4.22/arch/ppc/kernel/ppc_ksyms.c 2003-08-01 00:48:03.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/ppc_ksyms.c 2003-08-05 09:59:45.000000000 +1000 @@ -159,11 +159,14 @@ EXPORT_SYMBOL(_outsw_ns); EXPORT_SYMBOL(_insl_ns); EXPORT_SYMBOL(_outsl_ns); +EXPORT_SYMBOL(iopa); +EXPORT_SYMBOL(mm_ptov); EXPORT_SYMBOL(ioremap); +#ifdef CONFIG_440 +EXPORT_SYMBOL(ioremap64); +#endif EXPORT_SYMBOL(__ioremap); EXPORT_SYMBOL(iounmap); -EXPORT_SYMBOL(iopa); -EXPORT_SYMBOL(mm_ptov); #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) \ || defined(CONFIG_USB_STORAGE) || defined(CONFIG_USB_STORAGE_MODULE) @@ -178,6 +181,14 @@ EXPORT_SYMBOL(pci_free_consistent); #endif /* CONFIG_PCI */ +#ifdef CONFIG_NOT_COHERENT_CACHE +EXPORT_SYMBOL(consistent_alloc); +EXPORT_SYMBOL(consistent_free); +EXPORT_SYMBOL(consistent_sync); +EXPORT_SYMBOL(consistent_sync_page); +EXPORT_SYMBOL(flush_dcache_all); +#endif + EXPORT_SYMBOL(start_thread); EXPORT_SYMBOL(kernel_thread); @@ -338,8 +349,10 @@ EXPORT_SYMBOL(debugger_fault_handler); #endif -#ifdef CONFIG_8xx +#if defined(CONFIG_8xx) || defined(CONFIG_4xx) EXPORT_SYMBOL(__res); +#endif +#ifdef CONFIG_8xx EXPORT_SYMBOL(cpm_install_handler); EXPORT_SYMBOL(cpm_free_handler); EXPORT_SYMBOL(m8xx_cpm_hostalloc); diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/setup.c linuxppc-2.4/arch/ppc/kernel/setup.c --- linux-2.4.22/arch/ppc/kernel/setup.c 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/setup.c 2003-08-26 11:57:12.000000000 +1000 @@ -566,12 +566,10 @@ #if defined(CONFIG_KGDB) kgdb_map_scc(); set_debug_traps(); - if (strstr(cmd_line, "nokgdb")) - printk("kgdb default breakpoint deactivated on command line\n"); - else { + if (strstr(cmd_line, "gdb")) { if (ppc_md.progress) ppc_md.progress("setup_arch: kgdb breakpoint", 0x4000); - printk("kgdb default breakpoint activated\n"); + printk("kgdb breakpoint activated\n"); breakpoint(); } #endif diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/syscalls.c linuxppc-2.4/arch/ppc/kernel/syscalls.c --- linux-2.4.22/arch/ppc/kernel/syscalls.c 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/syscalls.c 2003-07-25 02:06:49.000000000 +1000 @@ -167,6 +167,16 @@ return error; } +#ifndef CONFIG_40x +#define allow_mmap_address(addr) 1 +#else +/* Blech. On 40x allowing mmap() (MAP_FIXED) at the first few pages + * of (any process's) virtual memory is a security hole due to chip + * erratum #67 (and possibly also due to the (documented) bizarre + * prefetch behaviour around 'sc' see S3.8.2.1 of the user manual). */ +#define allow_mmap_address(addr) ((((addr) & PAGE_MASK) >= 0x2100) || suser()) +#endif + static inline unsigned long do_mmap2(unsigned long addr, size_t len, unsigned long prot, unsigned long flags, @@ -181,6 +191,10 @@ goto out; } + ret = -EINVAL; + if ((! allow_mmap_address(addr)) && (flags & MAP_FIXED)) + goto out; + down_write(¤t->mm->mmap_sem); ret = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); up_write(¤t->mm->mmap_sem); diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/traps.c linuxppc-2.4/arch/ppc/kernel/traps.c --- linux-2.4.22/arch/ppc/kernel/traps.c 2003-06-25 20:56:52.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/traps.c 2003-08-26 11:57:12.000000000 +1000 @@ -457,7 +457,7 @@ } #endif /* CONFIG_8xx */ -#if defined(CONFIG_4xx) +#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) void DebugException(struct pt_regs *regs) { @@ -484,7 +484,7 @@ _exception(SIGTRAP, regs, 0, 0); } } -#endif /* CONFIG_4xx */ +#endif /* CONFIG_4xx || CONFIG_BOOKE */ #if !defined(CONFIG_TAU_INT) void diff -urN -X bkexcl linux-2.4.22/arch/ppc/kernel/xilinx_pic.c linuxppc-2.4/arch/ppc/kernel/xilinx_pic.c --- linux-2.4.22/arch/ppc/kernel/xilinx_pic.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/kernel/xilinx_pic.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,151 @@ +/* + * xilinx_pic.c + * + * Interrupt controller driver for Xilinx Virtex-II Pro. + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include + +#define ACKNOWLEDGE_AFTER /* SAATODO: Figure out the "right" answer */ + +/* No one else should require these constants, so define them locally here. */ +#define ISR 0 /* Interrupt Status Register */ +#define IPR 1 /* Interrupt Pending Register */ +#define IER 2 /* Interrupt Enable Register */ +#define IAR 3 /* Interrupt Acknowledge Register */ +#define SIE 4 /* Set Interrupt Enable bits */ +#define CIE 5 /* Clear Interrupt Enable bits */ +#define IVR 6 /* Interrupt Vector Register */ +#define MER 7 /* Master Enable Register */ + +#if XPAR_XINTC_USE_DCR == 0 +static volatile u32 *intc; +#define intc_out_be32(addr, mask) out_be32((addr), (mask)) +#define intc_in_be32(addr) in_be32((addr)) +#else +#define intc XPAR_INTC_0_BASEADDR +#define intc_out_be32(addr, mask) mtdcr((addr), (mask)) +#define intc_in_be32(addr) mfdcr((addr)) +#endif + +/* Global Variables */ +struct hw_interrupt_type *ppc4xx_pic; + +static void +xilinx_intc_enable(unsigned int irq) +{ + unsigned long mask = (0x80000000 >> (irq & 31)); + // printk(KERN_INFO "enable: %d\n", irq); + intc_out_be32(intc + SIE, mask); +} + +static void +xilinx_intc_disable(unsigned int irq) +{ + unsigned long mask = (0x80000000 >> (irq & 31)); + // printk(KERN_INFO "disable: %d\n", irq); + intc_out_be32(intc + CIE, mask); +} + +static void +xilinx_intc_disable_and_ack(unsigned int irq) +{ + unsigned long mask = (0x80000000 >> (irq & 31)); + // printk(KERN_INFO "disable_and_ack: %d\n", irq); + intc_out_be32(intc + CIE, mask); +#if !defined(ACKNOWLEDGE_AFTER) + intc_out_be32(intc + IAR, mask); /* Don't ack until xilinx_intc_end. */ +#endif +} + +static void +xilinx_intc_end(unsigned int irq) +{ + unsigned long mask = (0x80000000 >> (irq & 31)); + + // printk(KERN_INFO "end: %d\n", irq); + if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + intc_out_be32(intc + SIE, mask); +#if defined(ACKNOWLEDGE_AFTER) + intc_out_be32(intc + IAR, mask); +#endif + } +} + +static struct hw_interrupt_type xilinx_intc = { + "Xilinx Interrupt Controller", + NULL, + NULL, + xilinx_intc_enable, + xilinx_intc_disable, + xilinx_intc_disable_and_ack, + xilinx_intc_end, + 0 +}; + +int +xilinx_pic_get_irq(struct pt_regs *regs) +{ + int irq; + + /* + * NOTE: This function is the one that needs to be improved in + * order to handle multiple interrupt controllers. It currently + * is hardcoded to check for interrupts only on the first INTC. + */ + + /* + * The Interrupt Vector Register returns the vector + * corresponding to the highest priority (most significant or + * lowest numbered bit) interrupt pending (enabled and active). + * To convert this to an interrupt number, we just need to shift + * it two places to the right. + */ + irq = intc_in_be32(intc + IVR); + if (irq != -1) + irq = 31 - irq; + + // printk(KERN_INFO "get_irq: %d\n", irq); + + return (irq); +} + +void __init +ppc4xx_pic_init(void) +{ +#if XPAR_XINTC_USE_DCR == 0 + intc = ioremap(XPAR_INTC_0_BASEADDR, 32); + + printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX mapped to 0x%08lX\n", + (unsigned long) XPAR_INTC_0_BASEADDR, (unsigned long) intc); +#else + printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX (DCR)\n", + (unsigned long) XPAR_INTC_0_BASEADDR); +#endif + + /* + * Disable all external interrupts until they are + * explicity requested. + */ + intc_out_be32(intc + IER, 0); + + /* Acknowledge any pending interrupts just in case. */ + intc_out_be32(intc + IAR, ~(u32) 0); + + /* Turn on the Master Enable. */ + intc_out_be32(intc + MER, 0x3UL); + + ppc4xx_pic = &xilinx_intc; + ppc_md.get_irq = xilinx_pic_get_irq; +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/mm/440_mmu.c linuxppc-2.4/arch/ppc/mm/440_mmu.c --- linux-2.4.22/arch/ppc/mm/440_mmu.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/mm/440_mmu.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,68 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + */ +/* + * This file contains the routines for initializing the MMU + * on the 4xx series of chips. + * -- paulus + * + * Derived from arch/ppc/mm/init.c: + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Modifications by Paul Mackerras (PowerMac) (paulus@cs.anu.edu.au) + * and Cort Dougan (PReP) (cort@cs.nmt.edu) + * Copyright (C) 1996 Paul Mackerras + * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Used by the 4xx TLB replacement exception handler. + * Just needed it declared someplace (and initialized to zero). + */ +unsigned int tlb_4xx_index; + +/* + * MMU_init_hw does the chip-specific initialization of the MMU hardware. + */ +void __init MMU_init_hw(void) +{ + flush_instruction_cache(); +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/mm/Makefile linuxppc-2.4/arch/ppc/mm/Makefile --- linux-2.4.22/arch/ppc/mm/Makefile 2003-06-25 20:56:52.000000000 +1000 +++ linuxppc-2.4/arch/ppc/mm/Makefile 2003-06-25 21:30:07.000000000 +1000 @@ -19,6 +19,7 @@ obj-$(CONFIG_PPC_STD_MMU) += hashtable.o ppc_mmu.o tlb.o obj-$(CONFIG_40x) += 4xx_mmu.o +obj-$(CONFIG_440) += 440_mmu.o obj-$(CONFIG_NOT_COHERENT_CACHE) += cachemap.o include $(TOPDIR)/Rules.make diff -urN -X bkexcl linux-2.4.22/arch/ppc/mm/cachemap.c linuxppc-2.4/arch/ppc/mm/cachemap.c --- linux-2.4.22/arch/ppc/mm/cachemap.c 2003-02-15 08:07:09.000000000 +1100 +++ linuxppc-2.4/arch/ppc/mm/cachemap.c 2003-03-14 20:46:44.000000000 +1100 @@ -11,6 +11,7 @@ * is the virtual address and 'dma_handle' is the physical address. * Mostly stolen from the ARM port, with some changes for PowerPC. * -- Dan + * Modified for 36-bit support. -Matt * * 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 @@ -48,14 +49,24 @@ #include #include -extern int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep); +#include "mmu_decl.h" +extern int map_page(unsigned long va, phys_addr_t pa, int flags); + +/* This function will allocate the requested contiguous pages and + * map them into the kernel's vmalloc() space. This is done so we + * get unique mapping for these pages, outside of the kernel's 1:1 + * virtual:physical mapping. This is necessary so we can cover large + * portions of the kernel with single large page TLB entries, and + * still get unique uncached pages for consistent DMA. + */ void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle) { - int order, rsize; - unsigned long page; - void *ret; - pte_t *pte; + int order, err, i; + unsigned long page, va, flags; + phys_addr_t pa; + struct vm_struct *area; + void *ret; if (in_interrupt()) BUG(); @@ -77,23 +88,41 @@ */ invalidate_dcache_range(page, page + size); - ret = (void *)page; - *dma_handle = virt_to_bus(ret); + /* Allocate some common virtual space to map the new pages. + */ + area = get_vm_area(size, VM_ALLOC); + if (area == 0) { + free_pages(page, order); + return NULL; + } + va = VMALLOC_VMADDR(area->addr); + ret = (void *)va; - /* Chase down all of the PTEs and mark them uncached. + /* This gives us the real physical address of the first page. */ - rsize = (int)size; - while (rsize > 0) { - if (get_pteptr(&init_mm, page, &pte)) { - pte_val(*pte) |= _PAGE_NO_CACHE | _PAGE_GUARDED; - flush_tlb_page(find_vma(&init_mm,page),page); - } - else { - BUG(); - return NULL; - } - page += PAGE_SIZE; - rsize -= PAGE_SIZE; + *dma_handle = pa = virt_to_bus((void *)page); + + flags = _PAGE_KERNEL | _PAGE_NO_CACHE; + + /* + * Set refcount=1 on all pages in an order>0 + * allocation so that vfree() will actually + * free all pages that were allocated. + */ + if (order > 0) + { + struct page *rpage = virt_to_page(page); + for (i = 1; i < (1 << order); i++) + set_page_count(rpage+i, 1); + } + + err = 0; + for (i = 0; i < size && err == 0; i += PAGE_SIZE) + err = map_page(va+i, pa+i, flags); + + if (err) { + vfree((void *)va); + return NULL; } return ret; @@ -101,42 +130,12 @@ /* * free page(s) as defined by the above mapping. - * The caller has to tell us the size so we can free the proper number - * of pages. We can't vmalloc() a new space for these pages and simply - * call vfree() like some other architectures because we could end up - * with aliased cache lines (or at least a cache line with the wrong - * attributes). This can happen when the PowerPC speculative loads - * across page boundaries. */ -void consistent_free(void *vaddr, size_t size) +void consistent_free(void *vaddr) { - int order, rsize; - unsigned long addr; - pte_t *pte; - if (in_interrupt()) BUG(); - - size = PAGE_ALIGN(size); - order = get_order(size); - - /* Chase down all of the PTEs and mark them cached again. - */ - addr = (unsigned long)vaddr; - rsize = (int)size; - while (rsize > 0) { - if (get_pteptr(&init_mm, addr, &pte)) { - pte_val(*pte) &= ~(_PAGE_NO_CACHE | _PAGE_GUARDED); - flush_tlb_page(find_vma(&init_mm,addr),addr); - } - else { - BUG(); - return; - } - addr += PAGE_SIZE; - rsize -= PAGE_SIZE; - } - free_pages((unsigned long)vaddr, order); + vfree(vaddr); } /* @@ -161,3 +160,17 @@ break; } } + +/* + * consistent_sync_page make a page are consistent. identical + * to consistent_sync, but takes a struct page instead of a virtual address + */ + +void consistent_sync_page(struct page *page, unsigned long offset, +size_t size, int direction) +{ + void *start; + + start = page_address(page) + offset; + consistent_sync(start, size, direction); +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/mm/fault.c linuxppc-2.4/arch/ppc/mm/fault.c --- linux-2.4.22/arch/ppc/mm/fault.c 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/mm/fault.c 2003-07-25 02:06:49.000000000 +1000 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -51,6 +52,7 @@ extern void die_if_kernel(char *, struct pt_regs *, long); void bad_page_fault(struct pt_regs *, unsigned long, int sig); void do_page_fault(struct pt_regs *, unsigned long, unsigned long); +extern int get_pteptr(struct mm_struct *mm, unsigned long addr, pte_t **ptep); /* * Check whether the instruction at regs->nip is a store using @@ -100,7 +102,7 @@ struct mm_struct *mm = current->mm; siginfo_t info; int code = SEGV_MAPERR; -#if defined(CONFIG_4xx) +#if defined(CONFIG_4xx) || defined (CONFIG_BOOKE) int is_write = error_code & ESR_DST; #else int is_write = 0; @@ -115,14 +117,14 @@ error_code &= 0x48200000; else is_write = error_code & 0x02000000; -#endif /* CONFIG_4xx */ +#endif /* CONFIG_4xx || CONFIG_BOOKE */ #if defined(CONFIG_XMON) || defined(CONFIG_KGDB) if (debugger_fault_handler && regs->trap == 0x300) { debugger_fault_handler(regs); return; } -#if !defined(CONFIG_4xx) +#ifndef CONFIG_4xx if (error_code & 0x00400000) { /* DABR match */ if (debugger_dabr_match(regs)) @@ -202,6 +204,36 @@ if (is_write) { if (!(vma->vm_flags & VM_WRITE)) goto bad_area; +#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) + /* an exec - 4xx/BookE allows for per-page execute permission */ + } else if (regs->trap == 0x400) { + pte_t *ptep; + +#if 0 + /* It would be nice to actually enforce the VM execute + permission on CPUs which can do so, but far too + much stuff in userspace doesn't get the permissions + right, so we let any page be executed for now. */ + if (! (vma->vm_flags & VM_EXEC)) + goto bad_area; +#endif + + /* Since 4xx supports per-page execute permission, + * we lazily flush dcache to icache. */ + if (get_pteptr(mm, address, &ptep) && pte_present(*ptep)) { + struct page *page = pte_page(*ptep); + + if (! test_bit(PG_arch_1, &page->flags)) { + __flush_dcache_icache(kmap(page)); + kunmap(page); + set_bit(PG_arch_1, &page->flags); + } + pte_update(ptep, 0, _PAGE_HWEXEC); + _tlbie(address); + up_read(&mm->mmap_sem); + return; + } +#endif /* a read */ } else { /* protection fault */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/mm/init.c linuxppc-2.4/arch/ppc/mm/init.c --- linux-2.4.22/arch/ppc/mm/init.c 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/mm/init.c 2003-07-25 02:06:49.000000000 +1000 @@ -6,6 +6,7 @@ * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * PPC440/36-bit changes by Matt Porter (mporter@mvista.com) * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -408,6 +409,15 @@ free_bootmem(phys_avail.regions[i].address, phys_avail.regions[i].size); +#ifdef CONFIG_440 + /* + * Reserve space in the pinned TLB area (ZONE_DMA). + * Assume all 44x systems have >16MB RAM and any + * initrd is located outside of our pinned TLB area. + */ + reserve_bootmem(0x00800000, 0x00800000); +#endif + init_bootmem_done = 1; } @@ -429,9 +439,23 @@ /* * All pages are DMA-able so we put them all in the DMA zone. */ +#ifndef CONFIG_440 zones_size[ZONE_DMA] = total_lowmem >> PAGE_SHIFT; + for (i = 1; i < MAX_NR_ZONES; i++) zones_size[i] = 0; +#else + /* + * On 440, we highjack the DMA zone to provide a zone + * which limits allocations to our pinned TLB region. + */ + zones_size[ZONE_DMA] = PPC440_PIN_SIZE >> PAGE_SHIFT; + zones_size[ZONE_NORMAL] = (total_lowmem - PPC440_PIN_SIZE) >> PAGE_SHIFT; + + for (i = 2; i < MAX_NR_ZONES; i++) + zones_size[i] = 0; +#endif + #ifdef CONFIG_HIGHMEM zones_size[ZONE_HIGHMEM] = (total_memory - total_lowmem) >> PAGE_SHIFT; @@ -457,6 +481,12 @@ high_memory = (void *) __va(PPC_MEMSTART + total_lowmem); num_physpages = max_mapnr; /* RAM is assumed contiguous */ +#ifdef CONFIG_440 + /* Free reserved space in the pinned TLB area (ZONE_DMA) */ + if (total_lowmem > PPC440_PIN_SIZE) + free_bootmem(0x00800000, 0x00800000); +#endif + totalram_pages += free_all_bootmem(); #ifdef CONFIG_BLK_DEV_INITRD diff -urN -X bkexcl linux-2.4.22/arch/ppc/mm/mem_pieces.c linuxppc-2.4/arch/ppc/mm/mem_pieces.c --- linux-2.4.22/arch/ppc/mm/mem_pieces.c 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/mm/mem_pieces.c 2003-07-25 02:06:49.000000000 +1000 @@ -42,7 +42,7 @@ a = (a + align - 1) & -align; if (a + size <= e) { mem_pieces_remove(mp, a, size, 1); - return __va(a); + return (void *) __va(a); } } panic("Couldn't find %u bytes at %u alignment\n", size, align); diff -urN -X bkexcl linux-2.4.22/arch/ppc/mm/mmu_decl.h linuxppc-2.4/arch/ppc/mm/mmu_decl.h --- linux-2.4.22/arch/ppc/mm/mmu_decl.h 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/mm/mmu_decl.h 2003-07-25 02:06:49.000000000 +1000 @@ -9,6 +9,7 @@ * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * PPC440-36-bit changes by Matt Porter (mporter@mvista.com) * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -25,7 +26,7 @@ extern void mapin_ram(void); extern void bat_mapin_ram(unsigned long bat2, unsigned long bat3); extern void adjust_total_lowmem(void); -extern int map_page(unsigned long va, unsigned long pa, int flags); +extern int map_page(unsigned long va, phys_addr_t pa, int flags); extern void setbat(int index, unsigned long virt, unsigned long phys, unsigned int size, int flags); extern void reserve_phys_mem(unsigned long start, unsigned long size); diff -urN -X bkexcl linux-2.4.22/arch/ppc/mm/pgtable.c linuxppc-2.4/arch/ppc/mm/pgtable.c --- linux-2.4.22/arch/ppc/mm/pgtable.c 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/mm/pgtable.c 2003-08-02 07:10:51.000000000 +1000 @@ -9,6 +9,7 @@ * and Cort Dougan (PReP) (cort@cs.nmt.edu) * Copyright (C) 1996 Paul Mackerras * Amiga/APUS changes by Jesper Skov (jskov@cygnus.co.uk). + * PPC440/36-bit changes by Matt Porter (mporter@mvista.com) * * Derived from "arch/i386/mm/init.c" * Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds @@ -40,7 +41,7 @@ /* Maximum 768Mb of lowmem. On SMP, this value will be * trimmed down to whatever can be covered by BATs though. */ -#define MAX_LOW_MEM 0x30000000 +#define MAX_LOW_MEM CONFIG_LOWMEM_SIZE #ifndef CONFIG_SMP struct pgtable_cache_struct quicklists; @@ -67,16 +68,34 @@ #define p_mapped_by_bats(x) (0UL) #endif /* HAVE_BATS */ +#ifndef CONFIG_440 void * -ioremap(unsigned long addr, unsigned long size) +ioremap(phys_addr_t addr, unsigned long size) +{ + return __ioremap(addr, size, _PAGE_NO_CACHE); +} +#else /* CONFIG_440 */ +void * +ioremap64(unsigned long long addr, unsigned long size) { return __ioremap(addr, size, _PAGE_NO_CACHE); } void * -__ioremap(unsigned long addr, unsigned long size, unsigned long flags) +ioremap(phys_addr_t addr, unsigned long size) { - unsigned long p, v, i; + phys_addr_t addr64 = fixup_bigphys_addr(addr, size); + + return ioremap64(addr64, size); +} +#endif /* !CONFIG_440 */ + +void * +__ioremap(phys_addr_t addr, unsigned long size, unsigned long flags) +{ + unsigned long v, i; + phys_addr_t p; + int err; /* @@ -101,8 +120,13 @@ */ if ( mem_init_done && (p < virt_to_phys(high_memory)) ) { +#ifndef CONFIG_440 printk("__ioremap(): phys addr %0lx is RAM lr %p\n", p, __builtin_return_address(0)); +#else + printk("__ioremap(): phys addr %0Lx is RAM lr %p\n", p, + __builtin_return_address(0)); +#endif /* CONFIG_440 */ return NULL; } @@ -152,7 +176,7 @@ } out: - return (void *) (v + (addr & ~PAGE_MASK)); + return (void *) (v + ((unsigned long)addr & ~PAGE_MASK)); } void iounmap(void *addr) @@ -168,7 +192,7 @@ } int -map_page(unsigned long va, unsigned long pa, int flags) +map_page(unsigned long va, phys_addr_t pa, int flags) { pmd_t *pd; pte_t *pg; @@ -265,7 +289,8 @@ */ void __init mapin_ram(void) { - unsigned long v, p, s, f; + unsigned long v, s, f; + phys_addr_t p; #ifdef HAVE_BATS if (!__map_without_bats) @@ -279,6 +304,10 @@ * don't get ASID compares on kernel space. */ f = _PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_SHARED | _PAGE_HWEXEC; +#ifdef CONFIG_440 + /* Prevent bogus speculative cycles */ + f |= _PAGE_GUARDED; +#endif #if defined(CONFIG_KGDB) || defined(CONFIG_XMON) || defined(CONFIG_BDI_SWITCH) /* Allows stub to set breakpoints everywhere */ f |= _PAGE_WRENABLE; @@ -308,7 +337,7 @@ * virt, phys, size must all be page-aligned. * This should only be called before ioremap is called. */ -void __init io_block_mapping(unsigned long virt, unsigned long phys, +void __init io_block_mapping(unsigned long virt, phys_addr_t phys, unsigned int size, int flags) { int i; diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/Makefile linuxppc-2.4/arch/ppc/platforms/Makefile --- linux-2.4.22/arch/ppc/platforms/Makefile 2003-07-18 09:24:47.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/Makefile 2003-07-25 02:06:49.000000000 +1000 @@ -26,9 +26,29 @@ O_TARGET := platform.o -export-objs := prep_setup.o +export-objs := prep_setup.o beech.o ibm405lp.o arctic2.o ibm440gp.o +obj-$(CONFIG_CEDER) += ceder.o ibmnp405l.o +obj-$(CONFIG_CPCI405) += cpci405.o ibm405gp.o +obj-$(CONFIG_EVB405EP) += evb405ep.o ibm405ep.o +obj-$(CONFIG_EP405) += ep405.o ibm405gp.o +obj-$(CONFIG_XILINX_ML300) += xilinx_ml300.o +subdir-$(CONFIG_XILINX_OCP) += xilinx_ocp +obj-$(CONFIG_XILINX_OCP) += xilinx_ocp/xilinx_ocp.o +obj-$(CONFIG_REDWOOD_4) += redwood.o ibmstb3.o +obj-$(CONFIG_REDWOOD_5) += redwood5.o ibmstb4.o +obj-$(CONFIG_REDWOOD_6) += redwood6.o ibmstbx25.o obj-$(CONFIG_WALNUT) += walnut.o ibm405gp.o +obj-$(CONFIG_ASH) += ash.o ibmnp405h.o +obj-$(CONFIG_RAINIER) += rainier.o ibmnp4gs.o +obj-$(CONFIG_BEECH) += beech.o ibm405lp.o +obj-$(CONFIG_ARCTIC2) += arctic2.o subzero.o ibm405lp.o +ifeq ($(CONFIG_405LP),y) +obj-$(CONFIG_PM) += ibm405lp_pm.o ibm405lp_pmasm.o +endif +obj-$(CONFIG_SYCAMORE) += sycamore.o ibm405gpr.o + +obj-$(CONFIG_EBONY) += ebony.o ibm440gp.o obj-$(CONFIG_APUS) += apus_setup.o ifeq ($(CONFIG_APUS),y) diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/arctic2.c linuxppc-2.4/arch/ppc/platforms/arctic2.c --- linux-2.4.22/arch/ppc/platforms/arctic2.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/arctic2.c 2003-06-25 21:15:52.000000000 +1000 @@ -0,0 +1,230 @@ +/* + * arch/ppc/platforms/arctic2.c Platform setup for the IBM Arctic-2 reference platform + * with the Subzero core card and Beech personality card + * Based on beech.c by Bishop Brock + * + * 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 + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * Ken Inoue + * IBM Thomas J. Watson Research Center + * keninoue@us.ibm.com + * + * David Gibson + * IBM Ozlabs, Canberra, Australia + * arctic@gibson.dropbear.id.au + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Virtual address of the PCCF macro, which needs to be ioremap()ed + * and initialized by the board setup code. */ +volatile u16 *pccf_4xx_macro_vaddr; +unsigned long pccf_4xx_io_base; +unsigned long pccf_4xx_mem_base; +EXPORT_SYMBOL(pccf_4xx_macro_vaddr); +EXPORT_SYMBOL(pccf_4xx_io_base); +EXPORT_SYMBOL(pccf_4xx_mem_base); + +volatile u8 *arctic2_fpga_regs; +EXPORT_SYMBOL(arctic2_fpga_regs); + +/* Different Arctic2 versions have different capabilities in terms of dynamic + and static power control. Older units do not support the APM peripheral or + voltage scaling. The unit's capabilities are determined at boot and recorded + in these variables. Run-time rather than compile-time control is used to + simplify kernel distribution. */ + +int arctic2_supports_apm = 0; +int arctic2_supports_dvs = 0; + +#define GPIO0_OR ((u32 *)(GPIO0_BASE + 0)) +#define GPIO0_TCR ((u32 *)(GPIO0_BASE + 4)) +#define GPIO0_ODR ((u32 *)(GPIO0_BASE + 8)) +#define GPIO0_IR ((u32 *)(GPIO0_BASE + 12)) + +void __init +board_setup_arch(void) +{ +} + +void __init +board_io_mapping(void) +{ + ibm405lp_setup_pccf(&pccf_4xx_macro_vaddr, &pccf_4xx_io_base, + &pccf_4xx_mem_base); +} + +void __init +board_setup_irq(void) +{ + ibm405lp_setup_apm_pic(); + + /* + * Set USB interrupt edge-triggered polarity=rising edge. + */ + + mtdcr(DCRN_UIC0_TR, mfdcr(DCRN_UIC0_TR) | (1 << (31 - UIC_IRQ_EIR0))); + mtdcr(DCRN_UIC0_PR, mfdcr(DCRN_UIC0_PR) | (1 << (31 - UIC_IRQ_EIR0))); +} + +void +arctic2_poweroff(void) +{ + if (! arctic2_fpga_regs) + BUG(); + + __cli(); + + writeb(1, ARCTIC2_FPGA_POWERDOWN); + eieio(); + + while (1) + ; +} + +void __init +board_init(void) +{ + cpc0_cgcr1_t cgcr1; + u32 cfg; + +#ifdef CONFIG_PPC_RTC + ppc_md.time_init = ibm405lp_time_init; + ppc_md.set_rtc_time = ibm405lp_set_rtc_time; + ppc_md.get_rtc_time = ibm405lp_get_rtc_time; +#endif + ppc_md.power_off = arctic2_poweroff; + + /* Set up the EBC, then Disable the LCD controller, which may have been + left on by the BIOS. */ + + subzero_core_ebc_setup(); + + /* Turn on PerClk, so that the SDIO chip works */ + /* FIXME: This is bad for power usage - this will want to be + * fixed to turn the clock on "on demand" when we merge with + * the DPM code. */ + cgcr1.reg=mfdcr(DCRN_CPC0_CGCR1); + cgcr1.fields.csel=CPC0_CGCR1_CSEL_PERCLK; + mtdcr(DCRN_CPC0_CGCR1, cgcr1.reg); + + /* Configure the Arctic-II specific EBC banks */ + + /* Bank 1: 16-bit FPGA peripherals (ethernet data, SDIO, USB, DOC) + * 1MB, RW, 16-bit at 0xf9000000-0xf90fffff */ + /* The access parameters are programmed assuming a 33Mhz EBC + clock, which is true for nearly all the operating points we + have defined: + BME=0, TWT=5, CSN=0, OEN=1, WBN=1, WBF=1 TH=4 + RE=1, SOR=0, BEM=0, PEN=0 + */ + mtdcri(DCRN_EBC0, BnAP(1), 0x02815900); + mtdcri(DCRN_EBC0, BnCR(1), ARCTIC2_FPGA16_PADDR | 0x1a000); + + /* Bank 2: 8-bit FPGA peripherals (switch/control, ethernet regs, TCPA) + * 1MB, RW, 8-bit at 0xf8000000-0xf80fffff */ + mtdcri(DCRN_EBC0, BnAP(2), 0x02815580); + mtdcri(DCRN_EBC0, BnCR(2), ARCTIC2_FPGA8_PADDR | 0x18000); + + mtdcri(DCRN_LCD0, DER, 0); + + /* Data access of the Arctic2 debug sled ethernet chip will time out + under certain conditions unless the EBC ready wait is extended. The + data sheet doesn't give a bound on this, so we allow a generous + amount of time. Note that this problem is normally masked by the + PCMCIA setup, which sets an even longer timeout. */ + + cfg = mfdcri(DCRN_EBC0, CFG); + if ((cfg & EBC_CFG_RTC) < EBC_CFG_RTC_128) + mtdcri(DCRN_EBC0, CFG, (cfg & ~EBC_CFG_RTC) | EBC_CFG_RTC_128); +} + +void +arctic2_set_lcdpower(int on) +{ + iobarrier_rw(); + if (on) + out_be32(GPIO0_TCR, in_be32(GPIO0_TCR) | 0x80000000 ); + else + out_be32(GPIO0_TCR, in_be32(GPIO0_TCR) & ~0x80000000); + iobarrier_rw(); + udelay(100); /* KI guard time */ +} + +EXPORT_SYMBOL(arctic2_poweroff); +EXPORT_SYMBOL(arctic2_set_lcdpower); + +/* Units that support APM/DVS pull GPIO3 low as a strap. On older units this + GPIO is pulled high. After boot this can be used as a trace/debug signal, as + it has no other purpose on the board. */ + +static void __init +check_apm_dvs_support(void) +{ + u32 gpio3 = 0x10000000; + + iobarrier_rw(); + out_be32(GPIO0_TCR, in_be32(GPIO0_TCR) & ~gpio3); + out_be32(GPIO0_ODR, in_be32(GPIO0_ODR) & ~gpio3); + iobarrier_rw(); + arctic2_supports_apm = ((in_be32(GPIO0_IR) & gpio3) == 0); + arctic2_supports_dvs = arctic2_supports_apm; +} + +int __init +arctic2_init(void) +{ + if (! request_mem_region(ARCTIC2_FPGA8_PADDR, + ARCTIC2_FPGA_REGS_EXTENT, + "Arctic-2 FPGA Control Registers")) + BUG(); /* If someone's grabbed these addresses + * already, something's seriously wrong */ + + arctic2_fpga_regs = ioremap(ARCTIC2_FPGA8_PADDR, + ARCTIC2_FPGA_REGS_EXTENT); + if (!arctic2_fpga_regs) + BUG(); + + check_apm_dvs_support(); + + return 0; +} + +__initcall(arctic2_init); + +/* + * Local variables: + * c-basic-offset: 8 + * End: */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/arctic2.h linuxppc-2.4/arch/ppc/platforms/arctic2.h --- linux-2.4.22/arch/ppc/platforms/arctic2.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/arctic2.h 2003-06-25 21:15:52.000000000 +1000 @@ -0,0 +1,148 @@ +/* + * arch/ppc/platforms/arctic2.h Platform definitions for the IBM Arctic-II + * based on beech.h by Bishop Brock + * + * 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 + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * Ken Inoue + * IBM Thomas J. Watson Research Center + * keninoue@us.ibm.com + * + * David Gibson + * IBM Ozlabs, Canberra, Australia + * arctic@gibson.dropbear.id.au + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_ARCTIC2_H__ +#define __ASM_ARCTIC2_H__ + +#include + +#ifndef __ASSEMBLY__ + +/* Physical address for the 8-bit peripheral bank */ +#define ARCTIC2_FPGA8_PADDR (0xf8000000) + +/* Physical address for the 16-bit peripheral bank */ +#define ARCTIC2_FPGA16_PADDR (0xf9000000) + +/* Virtual address of the FPGA control registers */ +extern volatile u8 *arctic2_fpga_regs; + +#define ARCTIC2_FPGA_REGS_EXTENT (0xf) +#define ARCTIC2_FPGA_POWERDOWN (arctic2_fpga_regs + 0x0) +#define ARCTIC2_FPGA_BUTTONS (arctic2_fpga_regs + 0x1) +#define ARCTIC2_FPGA_MULTIWAY (arctic2_fpga_regs + 0x2) +#define ARCTIC2_FPGA_IRQ_ENABLE (arctic2_fpga_regs + 0x3) +#define ARCTIC2_FPGA_PCCF_POWER (arctic2_fpga_regs + 0x4) +#define ARCTIC2_FPGA_JACKET (arctic2_fpga_regs + 0x5) +#define ARCTIC2_FPGA_SDIO_CTRL (arctic2_fpga_regs + 0x6) +#define ARCTIC2_FPGA_USB_CTRL (arctic2_fpga_regs + 0x7) +#define ARCTIC2_FPGA_MDOC_CTRL (arctic2_fpga_regs + 0x8) +#define ARCTIC2_FPGA_CHARGER (arctic2_fpga_regs + 0x9) +#define ARCTIC2_FPGA_CRYO (arctic2_fpga_regs + 0xa) +#define ARCTIC2_FPGA_LED_DATA_HI (arctic2_fpga_regs + 0xc) +#define ARCTIC2_FPGA_LED_DATA_LOW (arctic2_fpga_regs + 0xd) +#define ARCTIC2_FPGA_LED_ADDR (arctic2_fpga_regs + 0xe) +#define ARCTIC2_FPGA_LED_CTRL (arctic2_fpga_regs + 0xf) + +#define ARCTIC2_FPGA_BTN_PWR 0x20 +#define ARCTIC2_FPGA_BTN_MIC 0x10 + +#define ARCTIC2_FPGA_MULTIWAY_PUSH 0x01 +#define ARCTIC2_FPGA_MULTIWAY_NE 0x02 +#define ARCTIC2_FPGA_MULTIWAY_SE 0x04 +#define ARCTIC2_FPGA_MULTIWAY_SW 0x08 +#define ARCTIC2_FPGA_MULTIWAY_NW 0x10 + +#define ARCTIC2_FPGA_MULTIWAY_N 0x12 +#define ARCTIC2_FPGA_MULTIWAY_E 0x06 +#define ARCTIC2_FPGA_MULTIWAY_S 0x0c +#define ARCTIC2_FPGA_MULTIWAY_W 0x18 + +#define ARCTIC2_FPGA_IRQ_PWR 0x10 +#define ARCTIC2_FPGA_IRQ_TCPA 0x08 +#define ARCTIC2_FPGA_IRQ_JACKET 0x04 +#define ARCTIC2_FPGA_IRQ_MIC 0x02 +#define ARCTIC2_FPGA_IRQ_BTN 0x01 + +#define ARCTIC2_FPGA_PCCF_POWER_5V 0x01 + +/* Arctic II uses the internal clock for UART. Note that the OPB + frequency must be more than 2x the UART clock frequency. At OPB + frequencies less than this the serial port will not function due to + the way that SerClk is sampled. We use 11.1111MHz as the frequency + because it can be generated from a wide range of OPB frequencies we + want to use. */ + +#define PPC4xx_SERCLK_FREQ 11111111 + +#define BASE_BAUD (PPC4xx_SERCLK_FREQ / 16) + +#define RTC_DVBITS RTC_DVBITS_33KHZ /* 33kHz RTC */ + +#define PPC4xx_MACHINE_NAME "IBM Arctic II" + +#include +#define _IO_BASE (pccf_4xx_io_base) +#define _ISA_MEM_BASE (pccf_4xx_mem_base) + +void arctic2_poweroff(void) __attribute__ ((noreturn)); +void arctic2_set_lcdpower(int on); + +extern int arctic2_supports_apm; +extern int arctic2_supports_dvs; + +/***************************************************************************** + * Serial port definitions + *****************************************************************************/ + +/* + * Arctic UART1 is touchscreen handled by separate driver, not included in + * standard serial defines. + */ + +#define UART0_INT UIC_IRQ_U0 +#define UART1_INT UIC_IRQ_U1 +#define UART0_IO_BASE 0xEF600300 +#define UART1_IO_BASE 0xEF600400 + +#define RS_TABLE_SIZE 2 + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base:(u8 *) UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) + +/* PM Button support */ + +#ifdef CONFIG_405LP_PM_BUTTON +#define IBM405LP_PM_IRQ APM0_IRQ_WUI0 +#define IBM405LP_PM_POLARITY 1 +#endif + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_ARCTIC2_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ash.c linuxppc-2.4/arch/ppc/platforms/ash.c --- linux-2.4.22/arch/ppc/platforms/ash.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ash.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,250 @@ +/* + * + * Copyright 2001-2002 MontaVista Software Inc. + * + * IBM NP405H ash eval board + * + * V 0.2 04/18/02 - Armin + * fixed board info debug + * V 0.3 06/12/02 - Armin + * added bios_fixup. + * V 0.4 06/17/02 - Armin /Todd + * fixed bios fixup, order + * + * Please read the COPYING file for all license details. + */ +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +void *ash_rtc_base; + +/* Some IRQs unique to Walnut. + * Used by the generic 405 PCI setup functions in ppc4xx_pci.c + */ +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {24, 24, 24, 24}, /* IDSEL 1 - PCI slot 1 */ + {25, 25, 25, 25}, /* IDSEL 2 - PCI slot 2 */ + {26, 26, 26, 26}, /* IDSEL 3 - PCI slot 3 */ + {27, 27, 27, 27}, /* IDSEL 4 - PCI slot 4 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +void __init +board_setup_arch(void) +{ + + bd_t *bip = (bd_t *) __res; + +#ifdef CONFIG_DEBUG_BRINGUP + int i; + printk("\n"); + printk("machine\t: %s\n", PPC4xx_MACHINE_NAME); + printk("\n"); + printk("bi_s_version\t %s\n", bip->bi_s_version); + printk("bi_r_version\t %s\n", bip->bi_r_version); + printk("bi_memsize\t 0x%8.8x\t %dMBytes\n", bip->bi_memsize, + bip->bi_memsize / (1024 * 1000)); + for (i = 0; i < EMAC_NUMS; i++) { + printk("bi_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", i, + bip->bi_enetaddr[i][0], bip->bi_enetaddr[i][1], + bip->bi_enetaddr[i][2], bip->bi_enetaddr[i][3], + bip->bi_enetaddr[i][4], bip->bi_enetaddr[i][5]); + } + printk("bi_pci_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", 0, + bip->bi_pci_enetaddr[0], bip->bi_pci_enetaddr[1], + bip->bi_pci_enetaddr[2], bip->bi_pci_enetaddr[3], + bip->bi_pci_enetaddr[4], bip->bi_pci_enetaddr[5]); + + printk("bi_intfreq\t 0x%8.8x\t clock:\t %dMhz\n", + bip->bi_intfreq, bip->bi_intfreq / 1000000); + + printk("bi_busfreq\t 0x%8.8x\t plb bus clock:\t %dMHz\n", + bip->bi_busfreq, bip->bi_busfreq / 1000000); + printk("bi_pci_busfreq\t 0x%8.8x\t pci bus clock:\t %dMHz\n", + bip->bi_pci_busfreq, bip->bi_pci_busfreq / 1000000); + + printk("\n"); +#endif + /* RTC step for ash */ + ash_rtc_base = (void *) ASH_RTC_VADDR; + TODC_INIT(TODC_TYPE_DS1743, ash_rtc_base, ash_rtc_base, ash_rtc_base, + 8); + +} + +void __init +bios_fixup(struct pci_controller *hose, struct pcil0_regs *pcip) +{ + + unsigned int bar_response, bar; + /* + * Expected PCI mapping: + * + * PLB addr PCI memory addr + * --------------------- --------------------- + * 0000'0000 - 7fff'ffff <--- 0000'0000 - 7fff'ffff + * 8000'0000 - Bfff'ffff ---> 8000'0000 - Bfff'ffff + * + * PLB addr PCI io addr + * --------------------- --------------------- + * e800'0000 - e800'ffff ---> 0000'0000 - 0001'0000 + * + * The following code is simplified by assuming that the bootrom + * has been well behaved in following this mapping. + */ + +#ifdef DEBUG + int i; + + printk("ioremap PCLIO_BASE = 0x%x\n", pcip); + printk("PCI bridge regs before fixup \n"); + for (i = 0; i <= 2; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dla\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dpcila\t0x%x\n", i, + in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dpciha\t0x%x\n", i, + in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + +#endif + if (ppc_md.progress) + ppc_md.progress("bios_fixup(): enter", 0x800); + + /* added for IBM boot rom version 1.15 bios bar changes -AK */ + + /* Disable region first */ + out_le32((void *) &(pcip->pmm[0].ma), 0x00000000); + /* PLB starting addr, PCI: 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].la), 0x80000000); + /* PCI start addr, 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE); + /* 512MB range of PLB to PCI */ + out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000); + /* Enable no pre-fetch, enable region */ + out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff - + (PPC405_PCI_UPPER_MEM - + PPC405_PCI_MEM_BASE)) | 0x01)); + + /* Disable region one */ + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[1].la), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + + /* Disable region two */ + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[2].la), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + + /* Enable PTM1 and PTM2, mapped to PLB address 0. */ + + out_le32((void *) &(pcip->ptm1la), 0x00000000); + out_le32((void *) &(pcip->ptm1ms), 0x00000001); + out_le32((void *) &(pcip->ptm2la), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000001); + + /* Write zero to PTM1 BAR. */ + + early_write_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), + PCI_BASE_ADDRESS_1, + 0x00000000); + + /* Disable PTM2 (unused) */ + + out_le32((void *) &(pcip->ptm2la), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + + /* end work arround */ + if (ppc_md.progress) + ppc_md.progress("bios_fixup(): done", 0x800); + +#ifdef DEBUG + printk("PCI bridge regs after fixup \n"); + for (i = 0; i <= 2; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dla\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dpcila\t0x%x\n", i, + in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dpciha\t0x%x\n", i, + in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + + +#endif +} + +void __init +board_io_mapping(void) +{ + io_block_mapping(ASH_RTC_VADDR, ASH_RTC_PADDR, ASH_RTC_SIZE, _PAGE_IO); +} + +void __init +board_setup_irq(void) +{ + +} + +void __init +board_init(void) +{ + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ash.h linuxppc-2.4/arch/ppc/platforms/ash.h --- linux-2.4.22/arch/ppc/platforms/ash.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ash.h 2003-07-25 02:00:01.000000000 +1000 @@ -0,0 +1,88 @@ +/* + * + * + * Copyright 2000-2002 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * akuster@mvista.com or source@mvista.com + * + * Module name: ash.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * Ash eval board. + * + * Please read the COPYING file for all license details. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_ASH_H__ +#define __ASM_ASH_H__ +#include +#include + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's "Ash" evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[4][6]; /* Local Ethernet MAC address */ + unsigned char bi_pci_enetaddr[6]; + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI speed in Hz */ + unsigned int bi_opb_busfreq; /* OPB Bus speed, in Hz */ + int bi_iic_fast[1]; /* Use fast i2c mode */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +/* Memory map for the IBM "Ash" NP405H evaluation board. + */ + +extern void *ash_rtc_base; +#define ASH_RTC_PADDR ((uint)0xf0000000) +#define ASH_RTC_VADDR ASH_RTC_PADDR +#define ASH_RTC_SIZE ((uint)8*1024) + + +/* Early initialization address mapping for block_io. + * Standard 405GP map. + */ +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +#define NR_BOARD_IRQS 32 + +#ifdef CONFIG_PPC405GP_INTERNAL_CLOCK +#define BASE_BAUD 201600 +#else +#define BASE_BAUD 691200 +#endif + +#define PPC4xx_MACHINE_NAME "IBM NP405H Ash" + +extern char pci_irq_table[][4]; + + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_ASH_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/beech.c linuxppc-2.4/arch/ppc/platforms/beech.c --- linux-2.4.22/arch/ppc/platforms/beech.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/beech.c 2003-06-02 14:37:41.000000000 +1000 @@ -0,0 +1,297 @@ +/* + * arch/ppc/platforms/beech.c Platform setup for the IBM Beech board + * + * 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 + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved + * + * Bishop Brock + * IBM Research, Austin Center for Low-Power Computing + * bcbrock@us.ibm.com + * March, 2002 + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +static void beech_ebc_setup(void); +static void beech_fpga_setup(void); + +volatile u8 *beech_fpga_reg_0 = NULL; +volatile u8 *beech_fpga_reg_2 = NULL; +volatile u8 *beech_fpga_reg_4 = NULL; + +EXPORT_SYMBOL(beech_fpga_reg_0); +EXPORT_SYMBOL(beech_fpga_reg_2); +EXPORT_SYMBOL(beech_fpga_reg_4); + +/* Virtual address of the PCCF macro, which needs to be ioremap()ed + * and initialized by the board setup code. */ +volatile u16 *pccf_4xx_macro_vaddr; +unsigned long pccf_4xx_io_base; +unsigned long pccf_4xx_mem_base; +EXPORT_SYMBOL(pccf_4xx_macro_vaddr); +EXPORT_SYMBOL(pccf_4xx_io_base); +EXPORT_SYMBOL(pccf_4xx_mem_base); + +/* + Beech board physical memory map: + + Main Memory (Initialized by the BIOS) + ======================================================================= + + SDRAM (64 MB) 0x00000000 - 0x04000000 + + OPB Space: (Mapped virtual = physical in ppc4xx_setup.c) + ======================================================================= + + UART0 0xEF600300 + UART1 0xEF600400 + IIC 0xEF600500 + OPB Arbiter 0xEF600600 + GPIO Controller 0xEF600700 + CODEC Interface 0xEF600900 + Touch Panel Controller 0xEF600A00 + DES Controller 0xEF600B00 + + + EBC Space: (Mapped virtual = physical in board_io_mapping(); EBC setup + for PCMCIA left to 4xx_pccf) + Space EBC Bank Physical Addresses EBC Base Address + ========================================================================= + + PCMCIA (32 MB) x F0000000 - F1FFFFFF F0000000 + + Expansion 2 F8000000 - F8FFFFFF F8000000 + Linux Flash (16 MB) F9000000 - F9FFFFFF + + NVRAM (32 KB) 1 FFE00000 - FFE07FFF FFE00000 + + + Ethernet(I/O) 1 FFE20300 - FFE2030F FFE00000 + (MEM) FFE40000 - FFE40FFF + + FPGA_REG_4 1 FFE60000 - FFE60000 FFE00000 + FPGA_REG_0 1 FFE80000 - FFE80000 FFE00000 + FPGA_REG_1 1 FFEA0000 - FFEA0000 FFE00000 + FPGA_REG_2 1 FFEC0000 - FFEC0000 FFE00000 + FPGA_REG_3 1 FFEE0000 - FFEE0000 FFE00000 + + SRAM (512 KB) 0 FFF00000 - FFF7FFFF FFF00000 + + Boot Flash (512 KB) 0 FFF80000 - FFFFFFFF FFF00000 + + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + NB: On Beech 1, address ranges for Bank 2 were reversed + +*/ + +void __init +board_setup_arch(void) +{ + /* Set up Beech FPGA. */ + + beech_fpga_setup(); +} + +void __init +board_io_mapping(void) +{ + ibm405lp_setup_pccf(&pccf_4xx_macro_vaddr, &pccf_4xx_io_base, + &pccf_4xx_mem_base); +} + +void __init +board_setup_irq(void) +{ + ibm405lp_setup_apm_pic(); +} + +/* The 405LP on Beech does not control the board power supply. All it can + really do is power down the CPU core supply, but this is still useful for + test and development. Note that when the core is powered down as part of a + suspend-to-RAM or hibernate, the poweroff sequence is executed from power + management code. The APM0_CFG[CDIV] field is used as a non-volatile + register to communicate the suspend/resume mode to the BIOS. */ + +static void +beech_poweroff(void) +{ + apm0_cfg_t cfg; + + __cli(); + + cfg.reg = mfdcr(DCRN_APM0_CFG); + cfg.fields.sm = APM0_CFG_SM_POWER_DOWN; + cfg.fields.cdiv = IBM405LP_POWERDOWN_REBOOT; + cfg.fields.isp = 1; + mtdcr_interlock(DCRN_APM0_CFG, cfg.reg, APM0_CFG_MASK); + while (1); +} + +void __init +board_init(void) +{ + ppc_md.time_init = ibm405lp_time_init; + ppc_md.set_rtc_time = ibm405lp_set_rtc_time; + ppc_md.get_rtc_time = ibm405lp_get_rtc_time; + + ppc_md.power_off = beech_poweroff; + + /* Disable the LCD controller, which may have been left on by the + BIOS. Then do initialization of the EBC. */ + + mtdcri(DCRN_LCD0, DER, 0); + beech_ebc_setup(); +} + +/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + Non-standard board support follows + +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/ + +/**************************************************************************** + * EBC Setup + ****************************************************************************/ + +/* The EBC is set up for Beech. This may simply replicate the setup already + done by the IBM BIOS for Beech (possibly with some address map changes), or + may be the first initialization if the board is booting from another BIOS. + Virtually all that is required to boot Linux on Beech is that the BIOS + enable the memory controller, load a Linux image from flash, and run it. + + For optimal dynamic frequency scaling the EBC settings will also vary as the + frequency varies. +*/ + +static void __init +beech_ebc_setup(void) +{ + ebc0_bnap_t ap; + ebc0_bncr_t cr; + + /* Set EBC bank 0 for the SRAM and boot flash. + + Access parameters assume 120ns AMD flash @ 66.66 MHz maximum bus + speed = 8 cycle access with 2 turnaround cycles (30 ns). + + These parameters will work for the SRAM as well, which is a 70 ns + part. + + NB: IBM BIOS sets this bank to burst, however bursting will never + happen in Linux because this region is mapped non-cacheable and + guarded, so it is set non-burst here. */ + + cr.reg = (BEECH_BANK0_PADDR & 0xfff00000) | + (mfdcri(DCRN_EBC0, BnCR(0)) & EBC0_BnCR_MASK); + cr.fields.bs = BEECH_BANK0_EBC_SIZE; + cr.fields.bu = EBC0_BnCR_BU_RW; + cr.fields.bw = EBC0_BnCR_BW_16; + mtdcri(DCRN_EBC0, BnCR(0), cr.reg); + + ap.reg = mfdcri(DCRN_EBC0, BnAP(0)) & EBC0_BnAP_MASK; + ap.fields.twt = 8; + ap.fields.th = 2; + mtdcri(DCRN_EBC0, BnAP(0), ap.reg); + + /* EBC bank 1 is used for many purposes: NVRAM, Ethernet, and FPGA + registers. This is a 1 MB, 16-bit bank. The access parameters must + handle the worst case of all of the devices. + + The Ethernet chip needs 20 ns setup of the addresses to the I/O + write signal (generated from the chip select), a minimum 150 ns + cycle, and 30 ns of turnaround. These settings will work for the + other devices as well. + */ + + cr.reg = (BEECH_BANK1_PADDR & 0xfff00000) | + (mfdcri(DCRN_EBC0, BnCR(1)) & EBC0_BnCR_MASK); + cr.fields.bs = BEECH_BANK1_EBC_SIZE; + cr.fields.bu = EBC0_BnCR_BU_RW; + cr.fields.bw = EBC0_BnCR_BW_16; + mtdcri(DCRN_EBC0, BnCR(1), cr.reg); + + ap.reg = mfdcri(DCRN_EBC0, BnAP(1)) & EBC0_BnAP_MASK; + ap.fields.twt = 10; + ap.fields.csn = 2; + ap.fields.th = 2; + mtdcri(DCRN_EBC0, BnAP(1), ap.reg); + + /* Set EBC bank 2 for the big (Linux) flash. There is 16 MB of flash, + but the CPLD decodes a 32 MB region. + + Access parameters assume 90ns AMD flash @ 66.66 MHz maximum bus + speed = 6 cycle access with 2 turnaround cycles (30 ns). + + NB: IBM BIOS sets this bank to burst, however bursting will never + happen in Linux because this region is mapped non-cacheable and + guarded, so it is set non-burst here. */ + + cr.reg = (BEECH_BANK2_PADDR & 0xfff00000) | + (mfdcri(DCRN_EBC0, BnCR(2)) & EBC0_BnCR_MASK); + cr.fields.bs = BEECH_BANK2_EBC_SIZE; + cr.fields.bu = EBC0_BnCR_BU_RW; + cr.fields.bw = EBC0_BnCR_BW_8; + mtdcri(DCRN_EBC0, BnCR(2), cr.reg); + + ap.reg = mfdcri(DCRN_EBC0, BnAP(2)) & EBC0_BnAP_MASK; + ap.fields.twt = 6; + ap.fields.th = 2; + mtdcri(DCRN_EBC0, BnAP(2), ap.reg); +} + +/**************************************************************************** + * FPGA Setup + ****************************************************************************/ + +/* The Beech FPGA is set up for Linux. */ + +static void __init +beech_fpga_setup(void) +{ + beech_fpga_reg_0 = (volatile u8 *) + ioremap(BEECH_FPGA_REG_0_PADDR, BEECH_FPGA_REG_0_SIZE); + beech_fpga_reg_2 = (volatile u8 *) + ioremap(BEECH_FPGA_REG_2_PADDR, BEECH_FPGA_REG_2_SIZE); + beech_fpga_reg_4 = (volatile u8 *) + ioremap(BEECH_FPGA_REG_4_PADDR, BEECH_FPGA_REG_4_SIZE); + + /* Set RTS/CTS mode for UART 1 */ + + *beech_fpga_reg_2 |= FPGA_REG_2_DEFAULT_UART1_N; +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/beech.h linuxppc-2.4/arch/ppc/platforms/beech.h --- linux-2.4.22/arch/ppc/platforms/beech.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/beech.h 2003-07-25 02:00:01.000000000 +1000 @@ -0,0 +1,245 @@ +/* + * include/asm-ppc/platforms/beech.h Platform definitions for the IBM Beech + * board + * + * 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 + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * Bishop Brock + * IBM Research, Austin Center for Low-Power Computing + * bcbrock@us.ibm.com + * March, 2002 + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_BEECH_H__ +#define __ASM_BEECH_H__ + +#include + +#ifndef __ASSEMBLY__ + +/* + * Data structure defining board information maintained by the standard boot + * ROM on the IBM Beech board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned long bi_tbfreq; /* Frequency of SysTmrClk */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned long sysclock_period; /* SysClk period in ns */ + unsigned long sys_speed; /* SysCLk frequency in Hz */ + unsigned long bi_intfreq; /* Processor speed, in Hz */ + unsigned long vco_speed; /* PLL VCO speed, in Hz */ + unsigned long bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_opb_busfreq; /* OPB Bus speed, in Hz */ + unsigned long bi_ebc_busfreq; /* EBC Bus speed, in Hz */ + int bi_iic_fast[1]; /* Use fast i2c mode */ +} bd_t; + +/* EBC Bank 0 controls the boot flash and SRAM */ + +#define BEECH_BANK0_PADDR ((uint)0xfff00000) +#define BEECH_BANK0_EBC_SIZE EBC0_BnCR_BS_1MB + +#define BEECH_SRAM_PADDR BEECH_BANK0_PADDR +#define BEECH_SRAM_SIZE ((uint)(512 * 1024)) + +#define BEECH_BOOTFLASH_PADDR (BEECH_BANK0_PADDR + (512 * 1024)) +#define BEECH_BOOTFLASH_SIZE ((uint)(512 * 1024)) + +/* EBC bank 1 controls the NVRAM, Ethernet and CPLD registers. The different + areas are mapped in as small an area as possible to help catch any kernel + addressing errors. + + NVRAM is improperly connected on Beech Pass 1. Only every other location is + accessible. This is a 32 KB NVRAM. + + The Ethernet chip maps 13 address lines. We only map the "I/O" space used by + the current driver. + + The FPGA "registers" are decoded on 128 KB boundarys. Each is mapped in a + separate page. */ + +#define BEECH_BANK1_PADDR ((uint)0xffe00000) +#define BEECH_BANK1_EBC_SIZE EBC0_BnCR_BS_1MB + +#define BEECH_NVRAM_PADDR BEECH_BANK1_PADDR +#define BEECH_NVRAM_SIZE ((uint) (32 * 1024)) + +#define BEECH_ETHERNET_PADDR (BEECH_BANK1_PADDR + 0x00020000) +#define BEECH_ETHERNET_SIZE ((uint) (8 * 1024)) + +#define BEECH_FPGA_REG_0_PADDR (BEECH_BANK1_PADDR + 0x00080000) +#define BEECH_FPGA_REG_0_SIZE PAGE_SIZE + +#define BEECH_FPGA_REG_1_PADDR (BEECH_BANK1_PADDR + 0x000A0000) +#define BEECH_FPGA_REG_1_SIZE PAGE_SIZE + +#define BEECH_FPGA_REG_2_PADDR (BEECH_BANK1_PADDR + 0x000C0000) +#define BEECH_FPGA_REG_2_SIZE PAGE_SIZE + +#define BEECH_FPGA_REG_3_PADDR (BEECH_BANK1_PADDR + 0x000E0000) +#define BEECH_FPGA_REG_3_SIZE PAGE_SIZE + +#define BEECH_FPGA_REG_4_PADDR (BEECH_BANK1_PADDR + 0x00060000) +#define BEECH_FPGA_REG_4_SIZE PAGE_SIZE + +/* FPGA Register Bits (From IBM BIOS) [ May not be valid for Beech Pass 1 ]*/ + +#define FPGA_REG_0_FLASH_N 0x01 +#define FPGA_REG_0_FLASH_ONBD_N 0x02 +#define FPGA_REG_0_HITA_TOSH_N 0x04 /* New in Pass 2 */ +#define FPGA_REG_0_STAT_OC 0x20 +#define FPGA_REG_0_AC_SOURCE_SEL_N 0x40 +#define FPGA_REG_0_AC_ACTIVE_N 0x80 + +#define FPGA_REG_1_USB_ACTIVE 0x01 /* New in Pass 2 */ +#define FPGA_REG_1_CLK_VARIABLE 0x02 +#define FPGA_REG_1_CLK_TEST 0x04 +#define FPGA_REG_1_CLK_SS 0x08 +#define FPGA_REG_1_EXT_IRQ_N 0x10 +#define FPGA_REG_1_SMI_MODE_N 0x20 +#define FPGA_REG_1_BATT_LOW_N 0x40 +#define FPGA_REG_1_PCMCIA_PWR_FAULT_N 0x80 + +#define FPGA_REG_2_DEFAULT_UART1_N 0x01 +#define FPGA_REG_2_EN_1_8V_PLL_N 0x02 +#define FPGA_REG_2_PC_BUF_EN_N 0x08 +#define FPGA_REG_2_CODEC_RESET_N 0x10 /* New in Pass 2 */ +#define FPGA_REG_2_TP_JSTICK_N 0x20 /* New in Pass 2 */ + +#define FPGA_REG_3_GAS_GAUGE_IO 0x01 + +#define FPGA_REG_4_SDRAM_CLK3_ENAB 0x01 +#define FPGA_REG_4_SDRAM_CLK2_ENAB 0x02 +#define FPGA_REG_4_SDRAM_CLK1_ENAB 0x04 +#define FPGA_REG_4_SDRAM_CLK0_ENAB 0x08 +#define FPGA_REG_4_PCMCIA_5V 0x10 /* New in Pass 2 */ +#define FPGA_REG_4_IRQ3 0x20 /* New in Pass 2 */ + +/* EBC Bank 2 contains the 16 MB "Linux" flash. The FPGA decodes a 32 MB + bank. The lower 16 MB are available for expansion devices. The upper 16 MB + are used for the "Linux" flash. + + Partitioning information is for the benefit of the MTD driver. See + drivers/mtd/maps/ibm4xx.c. We currently allocate the lower 1 MB for a + kernel, and the other 15 MB for a filesystem. + +*/ + +/* Bank 2 mappings changed between Beech Pass 1 and Pass 2 */ + +#ifdef CONFIG_BEECH_PASS1 +#define BEECH_BIGFLASH_OFFSET 0 +#else +#define BEECH_BIGFLASH_OFFSET (16 * 1024 * 1024) +#endif + +#define BEECH_BANK2_PADDR ((uint)0xf8000000) +#define BEECH_BANK2_EBC_SIZE EBC0_BnCR_BS_32MB + +#define BEECH_BIGFLASH_PADDR (BEECH_BANK2_PADDR + BEECH_BIGFLASH_OFFSET) +#define BEECH_BIGFLASH_SIZE (16 * 1024 * 1024) + +#define BEECH_KERNEL_OFFSET 0 +#define BEECH_KERNEL_SIZE (1 * 1024 * 1024) + +#define BEECH_FREE_AREA_OFFSET BEECH_KERNEL_SIZE +#define BEECH_FREE_AREA_SIZE (BEECH_BIGFLASH_SIZE - BEECH_KERNEL_SIZE) + +/* We do not currently support the internal clock mode for the UART. This + limits the minimum OPB frequency to just over 2X the UART oscillator + frequency. At OPB frequencies less than this the serial port will not + function due to the way that SerClk is sampled. */ + +#define PPC4xx_SERCLK_FREQ 11059200 +#define BASE_BAUD (PPC4xx_SERCLK_FREQ / 16) + +#define RTC_DVBITS RTC_DVBITS_1MHZ /* 1MHz RTC */ + +#define PPC4xx_MACHINE_NAME "IBM 405LP Beech" + +#define PCI_DRAM_OFFSET 0 + +#include +#define _IO_BASE (pccf_4xx_io_base) +#define _ISA_MEM_BASE (pccf_4xx_mem_base) + + +/***************************************************************************** + * Serial port definitions + *****************************************************************************/ + +#ifdef CONFIG_SERIAL_MANY_PORTS +#define RS_TABLE_SIZE 64 +#else +#define RS_TABLE_SIZE 4 +#endif + +#define UART0_INT UIC_IRQ_U0 +#define UART1_INT UIC_IRQ_U1 +#define UART0_IO_BASE 0xEF600300 +#define UART1_IO_BASE 0xEF600400 + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base:(u8 *) UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_DEBUG_IO_BASE UART1_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) +#endif + +/**************************************************************************** + * Non-standard board support follows + ****************************************************************************/ + +extern volatile u8 *beech_fpga_reg_0; +extern volatile u8 *beech_fpga_reg_2; +extern volatile u8 *beech_fpga_reg_4; + +extern int beech_sram_free(void *p); +extern int ibm405lp_set_pixclk(unsigned pixclk_low, unsigned pixclk_high); +extern void *beech_sram_alloc(size_t size); + +/* PM Button support */ + +#ifdef CONFIG_405LP_PM_BUTTON +#define IBM405LP_PM_IRQ APM0_IRQ_WUI2 +#define IBM405LP_PM_POLARITY 1 +#endif + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_BEECH_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ceder.c linuxppc-2.4/arch/ppc/platforms/ceder.c --- linux-2.4.22/arch/ppc/platforms/ceder.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ceder.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,84 @@ +/* + * + * Copyright 2001 MontaVista Software Inc. + * + * IBM NP405L ceder eval board + * + * Please read the COPYING file for all license details. + */ +#include +#include +#include +#include +#include +#include + +void *ceder_rtc_base; + +void __init +board_setup_arch(void) +{ + + bd_t *bip = (bd_t *) __res; + + /* RTC step for the walnut */ + ceder_rtc_base = (void *) CEDER_RTC_VADDR; + TODC_INIT(TODC_TYPE_DS1743, ceder_rtc_base, ceder_rtc_base, + ceder_rtc_base, 8); + +#ifdef CONFIG_DEBUG_BRINGUP + printk("\n"); + printk("machine\t: %s\n", PPC4xx_MACHINE_NAME); + printk("\n"); + printk("bi_s_version\t %s\n", bip->bi_s_version); + printk("bi_r_version\t %s\n", bip->bi_r_version); + printk("bi_memsize\t 0x%8.8x\t %dMBytes\n", bip->bi_memsize, + bip->bi_memsize / (1024 * 1000)); + printk("bi_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", 0, + bip->bi_enetaddr[0][0], bip->bi_enetaddr[0][1], + bip->bi_enetaddr[0][2], bip->bi_enetaddr[0][3], + bip->bi_enetaddr[0][4], bip->bi_enetaddr[0][5]); + + printk("bi_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", 1, + bip->bi_enetaddr[1][0], bip->bi_enetaddr[1][1], + bip->bi_enetaddr[1][2], bip->bi_enetaddr[1][3], + bip->bi_enetaddr[1][4], bip->bi_enetaddr[1][5]); + + printk("bi_intfreq\t 0x%8.8x\t clock:\t %dMhz\n", + bip->bi_intfreq, bip->bi_intfreq / 1000000); + + printk("bi_busfreq\t 0x%8.8x\t plb bus clock:\t %dMHz\n", + bip->bi_busfreq, bip->bi_busfreq / 1000000); + printk("bi_pci_busfreq\t 0x%8.8x\t pci bus clock:\t %dMHz\n", + bip->bi_pci_busfreq, bip->bi_pci_busfreq / 1000000); + + printk("\n"); +#endif + + /* Identify the system */ + printk + ("IBM Ceder port (C) 2002 MontaVista Software, Inc. (source@mvista.com)\n"); + +} + +void __init +board_io_mapping(void) +{ + io_block_mapping(CEDER_RTC_VADDR, + CEDER_RTC_PADDR, CEDER_RTC_SIZE, _PAGE_IO); +} + +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ceder.h linuxppc-2.4/arch/ppc/platforms/ceder.h --- linux-2.4.22/arch/ppc/platforms/ceder.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ceder.h 2003-07-25 02:00:01.000000000 +1000 @@ -0,0 +1,77 @@ +/* + * + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * akuster@mvista.com or source@mvista.com + * + * Module name: ceder.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * Ceder eval board. + * + * Please read the COPYING file for all license details. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_CEDER_H__ +#define __ASM_CEDER_H__ +#include + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's "Ceder" evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[2][6]; /* Local Ethernet MAC address */ + unsigned char bi_pci_mac[6]; + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI speed in Hz */ + unsigned int bi_opb_busfreq; /* OPB Bus speed, in Hz */ + int bi_iic_fast[1]; /* Use fast i2c mode */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +/* Memory map for the IBM "Ceder" NP405 evaluation board. + */ + +extern void *ceder_rtc_base; +#define CEDER_RTC_PADDR ((uint)0xf0000000) +#define CEDER_RTC_VADDR CEDER_RTC_PADDR +#define CEDER_RTC_SIZE ((uint)8*1024) + + +/* Early initialization address mapping for block_io. + * Standard 405GP map. + */ +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + + +#ifdef CONFIG_PPC405GP_INTERNAL_CLOCK +#define BASE_BAUD 201600 +#else +#define BASE_BAUD 691200 +#endif + +#define PPC4xx_MACHINE_NAME "IBM NP405L Ceder" + + + + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_CEDER_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/cpci405.c linuxppc-2.4/arch/ppc/platforms/cpci405.c --- linux-2.4.22/arch/ppc/platforms/cpci405.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/cpci405.c 2003-03-31 18:46:11.000000000 +1000 @@ -0,0 +1,141 @@ +/* + * arch/ppc/platforms/cpci405.c + * + * Board setup routines for the esd CPCI-405 cPCI Board. + * + * Author: Stefan Roese + * stefan.roese@esd-electronics.com + * + * Copyright 2001-2003 esd electronic system design - hannover germany + * + * 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. + * + * History: 11/09/2001 - armin + * added board_init to add in additional instuctions needed during platfrom_init + * + * : 03/26/03 - stefan + * Added cpci405_early_serial_map (cloned from evb405ep) to generate + * BASE_BAUD dynamically from the UDIV settings (configured by U-Boot). + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +void *cpci405_nvram; + +/* + * Some IRQs unique to CPCI-405. + */ +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {28, 28, 28, 28}, /* IDSEL 15 - cPCI slot 8 */ + {29, 29, 29, 29}, /* IDSEL 16 - cPCI slot 7 */ + {30, 30, 30, 30}, /* IDSEL 17 - cPCI slot 6 */ + {27, 27, 27, 27}, /* IDSEL 18 - cPCI slot 5 */ + {28, 28, 28, 28}, /* IDSEL 19 - cPCI slot 4 */ + {29, 29, 29, 29}, /* IDSEL 20 - cPCI slot 3 */ + {30, 30, 30, 30}, /* IDSEL 21 - cPCI slot 2 */ + }; + const long min_idsel = 15, max_idsel = 21, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +/* The serial clock for the chip is an internal clock determined by + * different clock speeds/dividers. + * Calculate the proper input baud rate and setup the serial driver. + */ +static void __init +cpci405_early_serial_map(void) +{ + u32 uart_div; + int serial_baud_405; + bd_t *bip = (bd_t *) __res; + struct serial_struct serialreq = {0}; + + /* Calculate the serial clock input frequency + * + * The base baud is the PLL OUTA (provided in the board info + * structure) divided by the external UART Divisor, divided + * by 16. + */ + uart_div = ((mfdcr(DCRN_CHCR_BASE) & CHR0_UDIV) >> 1) + 1; + serial_baud_405 = bip->bi_procfreq / uart_div / 16; + + /* Update the serial port attributes */ + serialreq.baud_base = serial_baud_405; + serialreq.flags = (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST); + serialreq.io_type = SERIAL_IO_MEM; + + serialreq.line = 0; + serialreq.port = 0; + serialreq.irq = ACTING_UART0_INT; + serialreq.iomem_base = (void*)ACTING_UART0_IO_BASE; + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + /* Configure debug serial access */ + gen550_init(0, &serialreq); +#endif + + if (early_serial_setup(&serialreq) != 0) { + printk("Early serial init of port 0 failed\n"); + } + + serialreq.line = 1; + serialreq.port = 1; + serialreq.irq = ACTING_UART1_INT; + serialreq.iomem_base = (void*)ACTING_UART1_IO_BASE; + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + /* Configure debug serial access */ + gen550_init(1, &serialreq); +#endif + + if (early_serial_setup(&serialreq) != 0) { + printk("Early serial init of port 1 failed\n"); + } +} + +void __init +board_setup_arch(void) +{ + cpci405_early_serial_map(); + TODC_INIT(TODC_TYPE_MK48T35, cpci405_nvram, cpci405_nvram, cpci405_nvram, 8); +} + +void __init +board_io_mapping(void) +{ + cpci405_nvram = ioremap(CPCI405_NVRAM_PADDR, CPCI405_NVRAM_SIZE); +} + +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/cpci405.h linuxppc-2.4/arch/ppc/platforms/cpci405.h --- linux-2.4.22/arch/ppc/platforms/cpci405.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/cpci405.h 2003-03-31 18:46:12.000000000 +1000 @@ -0,0 +1,52 @@ +/* + * CPCI-405 board specific definitions + * + * Copyright (c) 2001 Stefan Roese (stefan.roese@esd-electronics.com) + */ + +#ifdef __KERNEL__ +#ifndef __ASM_CPCI405_H__ +#define __ASM_CPCI405_H__ + +#include + +/* We have a 405GP core */ +#include + +#include + +#ifndef __ASSEMBLY__ +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +/* Map for the NVRAM space */ +#define CPCI405_NVRAM_PADDR ((uint)0xf0200000) +#define CPCI405_NVRAM_SIZE ((uint)32*1024) + +#if defined(CONFIG_UART0_TTYS0) +#define ACTING_UART0_IO_BASE UART0_IO_BASE +#define ACTING_UART1_IO_BASE UART1_IO_BASE +#define ACTING_UART0_INT UART0_INT +#define ACTING_UART1_INT UART1_INT +#else +#define ACTING_UART0_IO_BASE UART1_IO_BASE +#define ACTING_UART1_IO_BASE UART0_IO_BASE +#define ACTING_UART0_INT UART1_INT +#define ACTING_UART1_INT UART0_INT +#endif + +/* The UART clock is based off an internal clock - + * define BASE_BAUD based on the internal clock and divider(s). + * Since BASE_BAUD must be a constant, we will initialize it + * using clock/divider values which U-Boot initializes + * for typical configurations at various CPU speeds. + * The base baud is calculated as (FWDA / EXT UART DIV / 16) + */ +#define BASE_BAUD 0 + +#define PPC4xx_MACHINE_NAME "esd CPCI-405" + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_CPCI405_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ebony.c linuxppc-2.4/arch/ppc/platforms/ebony.c --- linux-2.4.22/arch/ppc/platforms/ebony.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ebony.c 2003-07-25 02:00:01.000000000 +1000 @@ -0,0 +1,538 @@ +/* + * arch/ppc/platforms/ebony.c + * + * Ebony board specific routines + * + * Matt Porter + * + * Copyright 2002 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern void gen550_progress(char *, unsigned short); +extern void gen550_init(int, struct serial_struct *); + +/* + * Ebony IRQ triggering/polarity settings + */ +static u_char ebony_IRQ_initsenses[] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 0: UART 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 1: UART 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 2: IIC 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 3: IIC 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 4: PCI Inb Mess */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 5: PCI Cmd Wrt */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 6: PCI PM */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* 7: PCI MSI 0 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* 8: PCI MSI 1 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* 9: PCI MSI 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 10: MAL TX EOB */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 11: MAL RX EOB */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 12: DMA Chan 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 13: DMA Chan 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 14: DMA Chan 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 15: DMA Chan 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 16: Reserved */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 17: Reserved */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 18: GPT Timer 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 19: GPT Timer 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 20: GPT Timer 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 21: GPT Timer 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 22: GPT Timer 4 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 23: Ext Int 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 24: Ext Int 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 25: Ext Int 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 26: Ext Int 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 27: Ext Int 4 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_NEGATIVE), /* 28: Ext Int 5 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 29: Ext Int 6 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 30: UIC1 NC Int */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 31: UIC1 Crit Int */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 32: MAL SERR */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 33: MAL TXDE */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 34: MAL RXDE */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 35: ECC Unc Err */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 36: ECC Corr Err */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 37: Ext Bus Ctrl */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 38: Ext Bus Mstr */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 39: OPB->PLB */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* 40: PCI MSI 3 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* 41: PCI MSI 4 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* 42: PCI MSI 5 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* 43: PCI MSI 6 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* 44: PCI MSI 7 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* 45: PCI MSI 8 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* 46: PCI MSI 9 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* 47: PCI MSI 10 */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* 48: PCI MSI 11 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 49: PLB Perf Mon */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 50: Ext Int 7 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 51: Ext Int 8 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 52: Ext Int 9 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 53: Ext Int 10 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 54: Ext Int 11 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 55: Ext Int 12 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 56: Ser ROM Err */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 57: Reserved */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 58: Reserved */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 59: PCI Async Err */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 60: EMAC 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 61: EMAC 0 WOL */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 62: EMAC 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 63: EMAC 1 WOL */ +}; + +extern void abort(void); + +/* Global Variables */ +unsigned char __res[sizeof (bd_t)]; + +static void __init +ebony_calibrate_decr(void) +{ + unsigned int freq; + + /* + * Determine system clock speed + * + * If we are on Rev. B silicon, then use + * default external system clock. If we are + * on Rev. C silicon then errata forces us to + * use the internal clock. + */ + switch (PVR_REV(mfspr(PVR))) { + case PVR_REV(PVR_440GP_RB): + freq = EBONY_440GP_RB_SYSCLK; + break; + case PVR_REV(PVR_440GP_RC1): + default: + freq = EBONY_440GP_RC_SYSCLK; + break; + } + + tb_ticks_per_jiffy = freq / HZ; + tb_to_us = mulhwu_scale_factor(freq, 1000000); + + /* Set the time base to zero */ + mtspr(SPRN_TBWL, 0); + mtspr(SPRN_TBWU, 0); + + /* Clear any pending timer interrupts */ + mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS); + + /* Enable decrementer interrupt */ + mtspr(SPRN_TCR, TCR_DIE); +} + +static int +ebony_show_cpuinfo(struct seq_file *m) +{ + seq_printf(m, "vendor\t\t: IBM\n"); + seq_printf(m, "machine\t\t: Ebony\n"); + + return 0; +} + +static inline int +ebony_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + { 23, 23, 23, 23 }, /* IDSEL 1 - PCI Slot 0 */ + { 24, 24, 24, 24 }, /* IDSEL 2 - PCI Slot 1 */ + { 25, 25, 25, 25 }, /* IDSEL 3 - PCI Slot 2 */ + { 26, 26, 26, 26 }, /* IDSEL 4 - PCI Slot 3 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +} + +#define PCIX_WRITEL(value, offset) \ + (writel(value, (u32)pcix_reg_base+offset)) + +/* + * FIXME: This is only here to "make it work". This will move + * to a ibm_pcix.c which will contain a generic IBM PCIX bridge + * configuration library. -Matt + */ +static void __init +ebony_setup_pcix(void) +{ + void *pcix_reg_base; + + pcix_reg_base = ioremap64(PCIX0_REG_BASE, PCIX0_REG_SIZE); + + /* Disable all windows */ + PCIX_WRITEL(0, PCIX0_POM0SA); + PCIX_WRITEL(0, PCIX0_POM1SA); + PCIX_WRITEL(0, PCIX0_POM2SA); + PCIX_WRITEL(0, PCIX0_PIM0SA); + PCIX_WRITEL(0, PCIX0_PIM1SA); + PCIX_WRITEL(0, PCIX0_PIM2SA); + + /* Setup 2GB PLB->PCI outbound mem window (3_8000_0000->0_8000_0000) */ + PCIX_WRITEL(0x00000003, PCIX0_POM0LAH); + PCIX_WRITEL(0x80000000, PCIX0_POM0LAL); + PCIX_WRITEL(0x00000000, PCIX0_POM0PCIAH); + PCIX_WRITEL(0x80000000, PCIX0_POM0PCIAL); + PCIX_WRITEL(0x80000001, PCIX0_POM0SA); + + /* Setup 2GB PCI->PLB inbound memory window at 0, enable MSIs */ + PCIX_WRITEL(0x00000000, PCIX0_PIM0LAH); + PCIX_WRITEL(0x00000000, PCIX0_PIM0LAL); + PCIX_WRITEL(0x80000007, PCIX0_PIM0SA); + + eieio(); +} + +static void __init +ebony_setup_hose(void) +{ + struct pci_controller *hose; + + /* Configure windows on the PCI-X host bridge */ + ebony_setup_pcix(); + + hose = pcibios_alloc_controller(); + + if (!hose) + return; + + hose->first_busno = 0; + hose->last_busno = 0xff; + + hose->pci_mem_offset = EBONY_PCI_MEM_OFFSET; + + pci_init_resource(&hose->io_resource, + EBONY_PCI_LOWER_IO, + EBONY_PCI_UPPER_IO, + IORESOURCE_IO, + "PCI host bridge"); + + pci_init_resource(&hose->mem_resources[0], + EBONY_PCI_LOWER_MEM, + EBONY_PCI_UPPER_MEM, + IORESOURCE_MEM, + "PCI host bridge"); + + hose->io_space.start = EBONY_PCI_LOWER_IO; + hose->io_space.end = EBONY_PCI_UPPER_IO; + hose->mem_space.start = EBONY_PCI_LOWER_MEM; + hose->mem_space.end = EBONY_PCI_UPPER_MEM; + isa_io_base = + (unsigned long)ioremap64(EBONY_PCI_IO_BASE, EBONY_PCI_IO_SIZE); + hose->io_base_virt = (void *)isa_io_base; + + setup_indirect_pci(hose, + EBONY_PCI_CFGA_PLB32, + EBONY_PCI_CFGD_PLB32); + hose->set_cfg_type = 1; + + hose->last_busno = pciauto_bus_scan(hose, hose->first_busno); + + ppc_md.pci_swizzle = common_swizzle; + ppc_md.pci_map_irq = ebony_map_irq; +} + +TODC_ALLOC(); + +static void __init +ebony_early_serial_map(void) +{ + struct serial_struct serial_req; + + /* Setup ioremapped serial port access */ + memset(&serial_req, 0, sizeof(serial_req)); + serial_req.line = 0; + serial_req.baud_base = BASE_BAUD; + serial_req.port = 0; + serial_req.irq = 0; + serial_req.flags = ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST; + serial_req.io_type = SERIAL_IO_MEM; + serial_req.iomem_base = ioremap64(PPC440GP_UART0_ADDR, 8); + serial_req.iomem_reg_shift = 0; + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + /* Configure debug serial access */ + gen550_init(0, &serial_req); +#endif + + if (early_serial_setup(&serial_req) != 0) { + printk("Early serial init of port 0 failed\n"); + } + + /* Assume early_serial_setup() doesn't modify serial_req */ + serial_req.line = 1; + serial_req.port = 1; + serial_req.irq = 1; + serial_req.iomem_base = ioremap64(PPC440GP_UART1_ADDR, 8); + +#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB) + /* Configure debug serial access */ + gen550_init(1, &serial_req); +#endif + + if (early_serial_setup(&serial_req) != 0) { + printk("Early serial init of port 1 failed\n"); + } +} + +static void __init +ebony_setup_arch(void) +{ + bd_t *bip = (bd_t *)__res; + unsigned char * vpd_base; + struct ibm440gp_clocks clocks; + +#if !defined(CONFIG_BDI_SWITCH) + /* + * The Abatron BDI JTAG debugger does not tolerate others + * mucking with the debug registers. + */ + mtspr(SPRN_DBCR0, (DBCR0_TDE | DBCR0_IDM)); +#endif + + /* Retrieve MAC addresses */ + vpd_base = ioremap64(EBONY_VPD_BASE, EBONY_VPD_SIZE); + memcpy(bip->bi_enetaddr[0],EBONY_NA0_ADDR(vpd_base),6); + memcpy(bip->bi_enetaddr[1],EBONY_NA1_ADDR(vpd_base),6); + + /* + * Determine various clocks. + * To be completely correct we should get SysClk + * from FPGA, because it can be changed by on-board switches + * --ebs + */ + ibm440gp_get_clocks(&clocks, 33333333, 6 * 1843200); + bip->bi_opb_busfreq = clocks.opb; + + /* Use IIC in standard (100 kHz) mode */ + bip->bi_iic_fast[0] = bip->bi_iic_fast[1] = 0; + +#ifdef CONFIG_PPC_RTC + /* Setup TODC access */ + TODC_INIT(TODC_TYPE_DS1743, + 0, + 0, + ioremap64(EBONY_RTC_ADDR, EBONY_RTC_SIZE), + 8); +#endif /* CONFIG_PPC_RTC */ + + /* init to some ~sane value until calibrate_delay() runs */ + loops_per_jiffy = 50000000/HZ; + + /* Setup PCI host bridge */ + ebony_setup_hose(); + +#ifdef CONFIG_BLK_DEV_INITRD + if (initrd_start) + ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0); /* /dev/ram */ + else +#endif +#ifdef CONFIG_ROOT_NFS + ROOT_DEV = to_kdev_t(0x00FF); /* /dev/nfs pseudo device */ +#else + ROOT_DEV = to_kdev_t(0x1601); /* /dev/hdc1 */ +#endif + +#ifdef CONFIG_DUMMY_CONSOLE + conswitchp = &dummy_con; +#endif + + ebony_early_serial_map(); + + ibm4xxPIC_InitSenses = ebony_IRQ_initsenses; + ibm4xxPIC_NumInitSenses = sizeof(ebony_IRQ_initsenses); + + /* Identify the system */ + printk("IBM Ebony port (C) 2002 MontaVista Software, Inc. (source@mvista.com)\n"); +} + +static void +ebony_restart(char *cmd) +{ + __cli(); + abort(); +} + +static void +ebony_power_off(void) +{ + __cli(); + for(;;); +} + +static void +ebony_halt(void) +{ + __cli(); + for(;;); +} + +/* + * Read the 440GP memory controller to get size of system memory. + */ +static unsigned long __init +ebony_find_end_of_memory(void) +{ + u32 i, bank_config; + u32 mem_size = 0; + + for (i=0; i<4; i++) + { + switch (i) + { + case 0: + mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B0CR); + break; + case 1: + mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B1CR); + break; + case 2: + mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B2CR); + break; + case 3: + mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B3CR); + break; + } + + bank_config = mfdcr(DCRN_SDRAM0_CFGDATA); + + if (!(bank_config & SDRAM_CONFIG_BANK_ENABLE)) + continue; + switch (SDRAM_CONFIG_BANK_SIZE(bank_config)) + { + case SDRAM_CONFIG_SIZE_8M: + mem_size += PPC440_MEM_SIZE_8M; + break; + case SDRAM_CONFIG_SIZE_16M: + mem_size += PPC440_MEM_SIZE_16M; + break; + case SDRAM_CONFIG_SIZE_32M: + mem_size += PPC440_MEM_SIZE_32M; + break; + case SDRAM_CONFIG_SIZE_64M: + mem_size += PPC440_MEM_SIZE_64M; + break; + case SDRAM_CONFIG_SIZE_128M: + mem_size += PPC440_MEM_SIZE_128M; + break; + case SDRAM_CONFIG_SIZE_256M: + mem_size += PPC440_MEM_SIZE_256M; + break; + case SDRAM_CONFIG_SIZE_512M: + mem_size += PPC440_MEM_SIZE_512M; + break; + } + } + return mem_size; +} + +static void __init +ebony_init_irq(void) +{ + int i; + + ppc4xx_pic_init(); + + for (i = 0; i < NR_IRQS; i++) + irq_desc[i].handler = ppc4xx_pic; +} + +void __init platform_init(unsigned long r3, unsigned long r4, + unsigned long r5, unsigned long r6, unsigned long r7) +{ + parse_bootinfo((struct bi_record *) (r3 + KERNELBASE)); + + ppc_md.setup_arch = ebony_setup_arch; + ppc_md.show_cpuinfo = ebony_show_cpuinfo; + ppc_md.init_IRQ = ebony_init_irq; + ppc_md.get_irq = NULL; /* Set in ppc4xx_pic_init() */ + + ppc_md.find_end_of_memory = ebony_find_end_of_memory; + ppc_md.setup_io_mappings = NULL; + + ppc_md.restart = ebony_restart; + ppc_md.power_off = ebony_power_off; + ppc_md.halt = ebony_halt; + + ppc_md.calibrate_decr = ebony_calibrate_decr; +#ifdef CONFIG_PPC_RTC + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#endif /* CONFIG_PPC_RTC */ + +#ifdef CONFIG_SERIAL_TEXT_DEBUG + ppc_md.progress = gen550_progress; +#endif /* CONFIG_SERIAL_TEXT_DEBUG */ +#ifdef CONFIG_KGDB + ppc_md.early_serial_map = ebony_early_serial_map; +#endif +#ifdef CONFIG_VT + /* USB keyboard support */ + ppc_md.kbd_setkeycode = pckbd_setkeycode; + ppc_md.kbd_getkeycode = pckbd_getkeycode; + ppc_md.kbd_translate = pckbd_translate; +#endif + + /* + * Fill PHY id map, actually, default settings will do just + * fine, provided that emac_probe is called for EMAC0 and + * _then_ for EMAC1. I added it as an example. -ebs + * + * This mapping corresponds to the Ebony factory configuration (RMII): + * J33 2-3, J40 1-2, J34 2-3, J77 1-2 + * EMAC0 <-> PHY 0x8 + * EMAC1 <-> PHY 0x9 + */ + emac_phy_map[0] = ~(1 << 8); + emac_phy_map[1] = ~(1 << 9); +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ebony.h linuxppc-2.4/arch/ppc/platforms/ebony.h --- linux-2.4.22/arch/ppc/platforms/ebony.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ebony.h 2003-04-18 12:42:42.000000000 +1000 @@ -0,0 +1,106 @@ +/* + * arch/ppc/platforms/ebony.h + * + * Ebony board definitions + * + * Matt Porter + * + * Copyright 2002 MontaVista Software Inc. + * + * 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. + * + * 05/07/02 - Armin + * removed IIC_PORT_DFNS + */ + +#ifdef __KERNEL__ +#ifndef __ASM_EBONY_H__ +#define __ASM_EBONY_H__ + +#include +#include + +/* F/W TLB mapping used in bootloader glue to reset EMAC */ +#define EBONY_EMAC0_MR0 0xE0000800 + +/* Macros to get at Ebony VPD info */ +#define EBONY_VPD_BASE 0x00000001fffffe00 +#define EBONY_VPD_SIZE 0x24 +#define EBONY_NA0_OFFSET 0x0c +#define EBONY_NA1_OFFSET 0x18 +#define EBONY_NA0_ADDR(base) (base + EBONY_NA0_OFFSET) +#define EBONY_NA1_ADDR(base) (base + EBONY_NA1_OFFSET) + +/* Default clock rates for Rev. B and Rev. C silicon */ +#define EBONY_440GP_RB_SYSCLK 33000000 +#define EBONY_440GP_RC_SYSCLK 400000000 + +/* RTC/NVRAM location */ +#define EBONY_RTC_ADDR 0x0000000148000000 +#define EBONY_RTC_SIZE 0x2000 + +/* Flash */ +#define EBONY_FPGA_ADDR 0x0000000148300000 +#define EBONY_BOOT_SMALL_FLASH(x) (x & 0x20) +#define EBONY_ONBRD_FLASH_EN(x) (x & 0x02) +#define EBONY_FLASH_SEL(x) (x & 0x01) +#define EBONY_SMALL_FLASH_LOW1 0x00000001ff800000 +#define EBONY_SMALL_FLASH_LOW2 0x00000001ff880000 +#define EBONY_SMALL_FLASH_HIGH1 0x00000001fff00000 +#define EBONY_SMALL_FLASH_HIGH2 0x00000001fff80000 +#define EBONY_SMALL_FLASH_SIZE 0x80000 +#define EBONY_LARGE_FLASH_LOW 0x00000001ff800000 +#define EBONY_LARGE_FLASH_HIGH 0x00000001ffc00000 +#define EBONY_LARGE_FLASH_SIZE 0x400000 + +#define EBONY_SMALL_FLASH_BASE 0x00000001fff80000 +#define EBONY_LARGE_FLASH_BASE 0x00000001ff800000 + +/* + * Serial port defines + */ +#define RS_TABLE_SIZE 2 + +/* OpenBIOS defined UART mappings, used before early_serial_setup */ +#define UART0_IO_BASE (u8 *) 0xE0000200 +#define UART1_IO_BASE (u8 *) 0xE0000300 + +#define BASE_BAUD 33000000/3/16 +#define UART0_INT 0 +#define UART1_INT 1 + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) + +/* USB keyboard support */ +#define kbd_read_input() 0 +#define kbd_read_status() 0 +#define kbd_write_output(val) {} +#define kbd_write_command(val) {} + +/* PCI support */ +#define EBONY_PCI_LOWER_IO 0x00000000 +#define EBONY_PCI_UPPER_IO 0x0000ffff +#define EBONY_PCI_LOWER_MEM 0x80002000 +#define EBONY_PCI_UPPER_MEM 0xffffefff + +#define EBONY_PCI_CFGREGS_BASE 0x000000020ec00000 +#define EBONY_PCI_CFGA_PLB32 0x0ec00000 +#define EBONY_PCI_CFGD_PLB32 0x0ec00004 + +#define EBONY_PCI_IO_BASE 0x0000000208000000 +#define EBONY_PCI_IO_SIZE 0x00010000 +#define EBONY_PCI_MEM_OFFSET 0x00000000 + +#endif /* __ASM_EBONY_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ep405.c linuxppc-2.4/arch/ppc/platforms/ep405.c --- linux-2.4.22/arch/ppc/platforms/ep405.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ep405.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,194 @@ +/* + * BK Id: %F% %I% %G% %U% %#% + * + * Copyright 2001 MontaVista Software Inc. + * + * + * Not much needed for the Embedded Planet 405gp board + * + * History: 11/09/2001 - armin + * added board_init to add in additional instuctions needed during platfrom_init + * cleaned up map_irq. + * + * 1/22/2002 - Armin + * converted pci to ocp + * + * Please read the COPYING file for all license details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +u8 *ep405_bcsr; +u8 *ep405_nvram; + +static struct { + u8 cpld_xirq_select; + int pci_idsel; + int irq; +} ep405_devtable[] = { +#ifdef CONFIG_EP405PC + {0x07, 0x0E, 25}, /* EP405PC: USB */ +#endif +}; +#define EP405_DEVTABLE_SIZE (sizeof(ep405_devtable)/sizeof(ep405_devtable[0])) + +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + int i; + + /* AFAICT this is only called a few times during PCI setup, so + performance is not critical */ + for (i = 0; i < EP405_DEVTABLE_SIZE; i++) { + if (idsel == ep405_devtable[i].pci_idsel) + return ep405_devtable[i].irq; + } + return -1; +}; + +void __init +board_setup_arch(void) +{ + bd_t *bip = (bd_t *) __res; + + if (bip->bi_nvramsize == 512*1024) { + /* FIXME: we should properly handle NVRTCs of different sizes */ + TODC_INIT(TODC_TYPE_DS1557, ep405_nvram, ep405_nvram, ep405_nvram, 8); + } +} + +void __init +bios_fixup(struct pci_controller *hose, struct pcil0_regs *pcip) +{ + unsigned int bar_response, bar; + /* + * Expected PCI mapping: + * + * PLB addr PCI memory addr + * --------------------- --------------------- + * 0000'0000 - 7fff'ffff <--- 0000'0000 - 7fff'ffff + * 8000'0000 - Bfff'ffff ---> 8000'0000 - Bfff'ffff + * + * PLB addr PCI io addr + * --------------------- --------------------- + * e800'0000 - e800'ffff ---> 0000'0000 - 0001'0000 + * + */ + + /* Disable region zero first */ + out_le32((void *) &(pcip->pmm[0].ma), 0x00000000); + /* PLB starting addr, PCI: 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].la), 0x80000000); + /* PCI start addr, 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE); + /* 512MB range of PLB to PCI */ + out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000); + /* Enable no pre-fetch, enable region */ + out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff - + (PPC405_PCI_UPPER_MEM - + PPC405_PCI_MEM_BASE)) | 0x01)); + + /* Disable region one */ + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[1].la), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->ptm1ms), 0x00000000); + + /* Disable region two */ + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[2].la), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + + /* Configure PTM (PCI->PLB) region 1 */ + out_le32((void *) &(pcip->ptm1la), 0x00000000); /* PLB base address */ + /* Disable PTM region 2 */ + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + + /* Zero config bars */ + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_write_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + 0x00000000); + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + /* end work arround */ +} + +void __init +board_io_mapping(void) +{ + bd_t *bip = (bd_t *) __res; + + ep405_bcsr = ioremap(EP405_BCSR_PADDR, EP405_BCSR_SIZE); + + if (bip->bi_nvramsize > 0) { + ep405_nvram = ioremap(EP405_NVRAM_PADDR, bip->bi_nvramsize); + } +} + +void __init +board_setup_irq(void) +{ + int i; + + /* Workaround for a bug in the firmware it incorrectly sets + the IRQ polarities for XIRQ0 and XIRQ1 */ + mtdcr(DCRN_UIC_PR(DCRN_UIC0_BASE), 0xffffff80); /* set the polarity */ + mtdcr(DCRN_UIC_SR(DCRN_UIC0_BASE), 0x00000060); /* clear bogus interrupts */ + + /* Activate the XIRQs from the CPLD */ + writeb(0xf0, ep405_bcsr+10); + + /* Set up IRQ routing */ + for (i = 0; i < EP405_DEVTABLE_SIZE; i++) { + if ( (ep405_devtable[i].irq >= 25) + && (ep405_devtable[i].irq) <= 31) { + writeb(ep405_devtable[i].cpld_xirq_select, ep405_bcsr+5); + writeb(ep405_devtable[i].irq - 25, ep405_bcsr+6); + } + } +} + +void __init +board_init(void) +{ + bd_t *bip = (bd_t *) __res; + +#ifdef CONFIG_PPC_RTC + /* FIXME: we should be able to access the NVRAM even if PPC_RTC is not configured */ + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; + + if (bip->bi_nvramsize == 512*1024) { + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + } else { + printk("EP405: NVRTC size is not 512k (not a DS1557). Not sure what to do with it\n"); + } + +#endif +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ep405.h linuxppc-2.4/arch/ppc/platforms/ep405.h --- linux-2.4.22/arch/ppc/platforms/ep405.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ep405.h 2003-07-25 02:00:01.000000000 +1000 @@ -0,0 +1,50 @@ +/* + * Copyright 2000 MontaVista Software Inc. + * http://www.mvista.com + * + * + * Embedded Planet 405GP board + * http://www.embeddedplanet.com + * + * Please read the COPYING file for all license details. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_EP405_H__ +#define __ASM_EP405_H__ + +/* We have a 405GP core */ +#include + +#ifndef __ASSEMBLY__ +typedef struct board_info { + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[6]; /* Local Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ + unsigned int bi_nvramsize; /* Size of the NVRAM/RTC */ + unsigned int bi_opb_busfreq; /* OPB Bus speed, in Hz */ + int bi_iic_fast[1]; /* Use fast i2c mode */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +extern u8 *ep405_bcsr; +extern u8 *ep405_nvram; + +/* Map for the BCSR and NVRAM space */ +#define EP405_BCSR_PADDR ((uint)0xf4000000) +#define EP405_BCSR_SIZE ((uint)16) +#define EP405_NVRAM_PADDR ((uint)0xf4200000) + +/* serial defines */ +#define BASE_BAUD 399193 + +#define PPC4xx_MACHINE_NAME "Embedded Planet 405GP" + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_EP405_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/evb405ep.c linuxppc-2.4/arch/ppc/platforms/evb405ep.c --- linux-2.4.22/arch/ppc/platforms/evb405ep.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/evb405ep.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,276 @@ +/* + * arch/ppc/platforms/evb405ep.c + * + * Support for IBM PPC 405EP evaluation board ("Elvis"). + * + * Author: SAW (IBM), derived from walnut.c. + * Maintained by MontaVista Software + * + * 2003 (c) MontaVista Softare Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PPC_RTC +#include +#endif + +#include + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + + +void *evb405ep_rtc_base; + +/* Some IRQs unique to the board + * Used by the generic 405 PCI setup functions in ppc4xx_pci.c + */ +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {28, 28, 28, 28}, /* IDSEL 1 - PCI slot 1 */ + {29, 29, 29, 29}, /* IDSEL 2 - PCI slot 2 */ + {30, 30, 30, 30}, /* IDSEL 3 - PCI slot 3 */ + {31, 31, 31, 31}, /* IDSEL 4 - PCI slot 4 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +/* The serial clock for the chip is an internal clock determined by + * different clock speeds/dividers. + * Calculate the proper input baud rate and setup the serial driver. + */ +void __init +setup_serial(void) +{ + + u32 uart_div; + int serial_baud_405ep; + bd_t *bip = (bd_t *) __res; + struct serial_struct serialreq = {0}; + + /* Calculate the serial clock input frequency + * + * The base baud is the PLL OUTA (provided in the board info + * structure) divided by the external UART Divisor, divided + * by 16. + */ + uart_div = (mfdcr(DCRN_CPC0_UCR_BASE) & DCRN_CPC0_UCR_U0DIV); + serial_baud_405ep = bip->bi_pllouta_freq / uart_div / 16; + + + /* Update the serial port attributes */ + serialreq.baud_base = serial_baud_405ep; + serialreq.flags = (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST); + serialreq.io_type = SERIAL_IO_MEM; + + serialreq.line = 0; + serialreq.port = 0; + serialreq.irq = ACTING_UART0_INT; + serialreq.iomem_base = (void*)ACTING_UART0_IO_BASE; + + if (early_serial_setup(&serialreq) != 0) { + printk("Early serial init of port 0 failed\n"); + } + + serialreq.line = 1; + serialreq.port = 1; + serialreq.irq = ACTING_UART1_INT; + serialreq.iomem_base = (void*)ACTING_UART1_IO_BASE; + + if (early_serial_setup(&serialreq) != 0) { + printk("Early serial init of port 1 failed\n"); + } +} + + +void __init +board_setup_arch(void) +{ +#define EVB405EP_FPGA_BASE 0xF0300000 + + void *fpga_reg0; + void *fpga_reg1; + + setup_serial(); + fpga_reg0 = ioremap(EVB405EP_FPGA_BASE, 8); + if (!fpga_reg0) { + printk(KERN_CRIT + "evb405ep_setup_arch() fpga_reg0 ioremap failed\n"); + return; + } + + fpga_reg1 = fpga_reg0 + 1; + +#ifdef CONFIG_PPC_RTC + /* RTC step for the evb405ep */ + evb405ep_rtc_base = (void *) EVB405EP_RTC_VADDR; + TODC_INIT(TODC_TYPE_DS1743, evb405ep_rtc_base, evb405ep_rtc_base, + evb405ep_rtc_base, 8); +#endif /* CONFIG_PPC_RTC */ + /* Identify the system */ + printk("IBM 405EP Evaluation Board port (C) 2003 MontaVista Software, Inc. (source@mvista.com)\n"); + + +} + +void __init +bios_fixup(struct pci_controller *hose, struct pcil0_regs *pcip) +{ + + unsigned int bar_response, bar; + /* + * Expected PCI mapping: + * + * PLB addr PCI memory addr + * --------------------- --------------------- + * 0000'0000 - 7fff'ffff <--- 0000'0000 - 7fff'ffff + * 8000'0000 - Bfff'ffff ---> 8000'0000 - Bfff'ffff + * + * PLB addr PCI io addr + * --------------------- --------------------- + * e800'0000 - e800'ffff ---> 0000'0000 - 0001'0000 + * + * The following code is simplified by assuming that the bootrom + * has been well behaved in following this mapping. + */ + +#ifdef DEBUG + int i; + + printk("ioremap PCLIO_BASE = 0x%x\n", pcip); + printk("PCI bridge regs before fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif + + /* added for IBM boot rom version 1.15 bios bar changes -AK */ + + /* Disable region first */ + out_le32((void *) &(pcip->pmm[0].ma), 0x00000000); + /* PLB starting addr, PCI: 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].la), 0x80000000); + /* PCI start addr, 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE); + /* 512MB range of PLB to PCI */ + out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000); + /* Enable no pre-fetch, enable region */ + out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff - + (PPC405_PCI_UPPER_MEM - + PPC405_PCI_MEM_BASE)) | 0x01)); + + /* Disable region one */ + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[1].la), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->ptm1ms), 0x00000001); + + /* Disable region two */ + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[2].la), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + out_le32((void *) &(pcip->ptm2la), 0x00000000); + + /* Zero config bars */ + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_write_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + 0x00000000); + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + /* end work arround */ + +#ifdef DEBUG + printk("PCI bridge regs after fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif +} + +void __init +board_io_mapping(void) +{ + io_block_mapping(EVB405EP_RTC_VADDR, + EVB405EP_RTC_PADDR, EVB405EP_RTC_SIZE, _PAGE_IO); +} + +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ +#ifdef CONFIG_PPC_RTC + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +#endif +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/evb405ep.h linuxppc-2.4/arch/ppc/platforms/evb405ep.h --- linux-2.4.22/arch/ppc/platforms/evb405ep.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/evb405ep.h 2003-07-25 02:00:01.000000000 +1000 @@ -0,0 +1,72 @@ +/* + * arch/ppc/platforms/evb405ep.h + * + * Support for IBM PPC 405EP evaluation board ("Elvis"). + * + * Author: SAW (IBM), derived from walnut.h. + * Maintained by MontaVista Software + * + * 2003 (c) MontaVista Softare Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_EVB405EP_H__ +#define __ASM_EVB405EP_H__ + +/* 405EP */ +#include + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[2][6]; /* Local Ethernet MAC address */ unsigned char bi_pci_enetaddr[6]; /* PCI Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ + unsigned int bi_pllouta_freq; /* PLL OUTA speed, in Hz */ + unsigned int bi_opb_busfreq; /* OPB Bus speed, in Hz */ + int bi_iic_fast[1]; /* Use fast i2c mode */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + + +/* Memory map for the IBM 405EP evaluation board. + * Generic 4xx plus RTC. + */ + +extern void *evb405ep_rtc_base; +#define EVB405EP_RTC_PADDR ((uint)0xf0000000) +#define EVB405EP_RTC_VADDR EVB405EP_RTC_PADDR +#define EVB405EP_RTC_SIZE ((uint)8*1024) + +/* The UART clock is based off an internal clock - + * define BASE_BAUD based on the internal clock and divider(s). + * Since BASE_BAUD must be a constant, we will initialize it + * using clock/divider values which OpenBIOS initializes + * for typical configurations at various CPU speeds. + * The base baud is calculated as (FWDA / EXT UART DIV / 16) + */ +#define BASE_BAUD 0 + +#define EVB405EP_FPGA_BASE 0xF0300000 + +#define PPC4xx_MACHINE_NAME "IBM 405EP Evaluation Board" + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_EVB405EP_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibm405ep.c linuxppc-2.4/arch/ppc/platforms/ibm405ep.c --- linux-2.4.22/arch/ppc/platforms/ibm405ep.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibm405ep.c 2003-03-28 08:48:01.000000000 +1100 @@ -0,0 +1,113 @@ +/* + * arch/ppc/platforms/ibm405ep.c + * + * Support for IBM PPC 405EP processors. + * + * Author: SAW (IBM), derived from ibmnp405l.c. + * Maintained by MontaVista Software + * + * 2003 (c) MontaVista Softare Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static struct ocp_func_emac_data ibm405ep_emac0_def = { + .zmii_idx = -1, /* ZMII device index */ + .zmii_mux = 0, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 0, /* MAL rx channel number */ + .mal_tx1_chan = 0, /* MAL tx channel 1 number */ + .mal_tx2_chan = 1, /* MAL tx channel 2 number */ + .wol_irq = BL_MAC_WOL, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ +}; + +// FIXME: Check WOL IRQ +static struct ocp_func_emac_data ibm405ep_emac1_def = { + .zmii_idx = -1, /* ZMII device index */ + .zmii_mux = 0, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 1, /* MAL rx channel number */ + .mal_tx1_chan = 2, /* MAL tx channel 1 number */ + .mal_tx2_chan = 3, /* MAL tx channel 2 number */ + .wol_irq = BL_MAC_WOL, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ +}; + +static struct ocp_func_mal_data ibm405ep_mal0_def = { + .num_tx_chans = 2*EMAC_NUMS, /* Number of TX channels */ + .num_rx_chans = EMAC_NUMS, /* Number of RX channels */ +}; + +struct ocp_def core_ocp[] __initdata = { + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_OPB, + .index = 0, + .paddr = OPB_BASE_START, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = UART0_IO_BASE, + .irq = UART0_INT, + .pm = IBM_CPM_UART0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = UART1_IO_BASE, + .irq = UART1_INT, + .pm = IBM_CPM_UART1 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .paddr = IIC0_BASE, + .irq = IIC0_IRQ, + .pm = IBM_CPM_IIC0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .paddr = GPIO0_BASE, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_MAL, + .paddr = OCP_PADDR_NA, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + .additions = &ibm405ep_mal0_def, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 0, + .paddr = EMAC0_BASE, + .irq = BL_MAC_ETH0, + .pm = OCP_CPM_NA, + .additions = &ibm405ep_emac0_def, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 1, + .paddr = EMAC0_BASE, + .irq = BL_MAC_ETH1, + .pm = OCP_CPM_NA, + .additions = &ibm405ep_emac1_def, + }, + { .vendor = OCP_VENDOR_INVALID + } +}; diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibm405ep.h linuxppc-2.4/arch/ppc/platforms/ibm405ep.h --- linux-2.4.22/arch/ppc/platforms/ibm405ep.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibm405ep.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,171 @@ +/* + * arch/ppc/platforms/ibm405ep.h + * + * IBM PPC 405EP processor defines. + * + * Author: SAW (IBM), derived from ibm405gp.h. + * Maintained by MontaVista Software + * + * 2003 (c) MontaVista Softare Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBM405EP_H__ +#define __ASM_IBM405EP_H__ + +#include +#include + +/* ibm405.h at bottom of this file */ + +/* PCI + * PCI Bridge config reg definitions + * see 17-19 of manual + */ + +#define PPC405_PCI_CONFIG_ADDR 0xeec00000 +#define PPC405_PCI_CONFIG_DATA 0xeec00004 + +#define PPC405_PCI_PHY_MEM_BASE 0x80000000 /* hose_a->pci_mem_offset */ + /* setbat */ +#define PPC405_PCI_MEM_BASE PPC405_PCI_PHY_MEM_BASE /* setbat */ +#define PPC405_PCI_PHY_IO_BASE 0xe8000000 /* setbat */ +#define PPC405_PCI_IO_BASE PPC405_PCI_PHY_IO_BASE /* setbat */ + +#define PPC405_PCI_LOWER_MEM 0x80000000 /* hose_a->mem_space.start */ +#define PPC405_PCI_UPPER_MEM 0xBfffffff /* hose_a->mem_space.end */ +#define PPC405_PCI_LOWER_IO 0x00000000 /* hose_a->io_space.start */ +#define PPC405_PCI_UPPER_IO 0x0000ffff /* hose_a->io_space.end */ + +#define PPC405_ISA_IO_BASE PPC405_PCI_IO_BASE + +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +/* serial port defines */ +#define RS_TABLE_SIZE 2 + +#define UART0_INT 0 +#define UART1_INT 1 + +#define PCIL0_BASE 0xEF400000 +#define UART0_IO_BASE 0xEF600300 +#define UART1_IO_BASE 0xEF600400 +#define IIC0_BASE 0xEF600500 +#define OPB0_BASE 0xEF600600 +#define GPIO0_BASE 0xEF600700 + +#define EMAC0_BASE 0xEF600800 +#define EMAC1_BASE 0xEF600900 +#define EMAC_NUMS 2 + +/* EMAC IRQ's */ + +#define BL_MAC_WOL 9 /* WOL */ +#define BL_MAL_SERR 10 /* MAL SERR */ +#define BL_MAL_TXEOB 11 /* MAL TX EOB */ +#define BL_MAL_RXEOB 12 /* MAL RX EOB */ +#define BL_MAL_TXDE 13 /* MAL TXDE */ +#define BL_MAL_RXDE 14 /* MAL RXDE */ +#define BL_MAC_ETH0 15 /* MAC 0 */ +#define BL_MAC_ETH1 17 /* MAC 1 */ + +#define IIC0_IRQ 2 +#define IIC1_IRQ 0 + +#define IIC_OWN 0x55 +#define IIC_CLOCK 50 +#define BD_EMAC_ADDR(e,i) bi_enetaddr[e][i] + +#if defined(CONFIG_UART0_TTYS0) +#define ACTING_UART0_IO_BASE UART0_IO_BASE +#define ACTING_UART1_IO_BASE UART1_IO_BASE +#define ACTING_UART0_INT UART0_INT +#define ACTING_UART1_INT UART1_INT +#else +#define ACTING_UART0_IO_BASE UART1_IO_BASE +#define ACTING_UART1_IO_BASE UART0_IO_BASE +#define ACTING_UART0_INT UART1_INT +#define ACTING_UART1_INT UART0_INT +#endif + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, ACTING_UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: (u8 *)ACTING_UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#define SERIAL_DEBUG_IO_BASE ACTING_UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) + +/* DCR defines */ +#define DCRN_CPMSR_BASE 0x0BA +#define DCRN_CPMFR_BASE 0x0B9 + +#define DCRN_CPC0_PLLMR0_BASE 0x0F0 +#define DCRN_CPC0_BOOT_BASE 0x0F1 +#define DCRN_CPC0_CR1_BASE 0x0F2 +#define DCRN_CPC0_EPRCSR_BASE 0x0F3 +#define DCRN_CPC0_PLLMR1_BASE 0x0F4 +#define DCRN_CPC0_UCR_BASE 0x0F5 +#define DCRN_CPC0_UCR_U0DIV 0x07F +#define DCRN_CPC0_SRR_BASE 0x0F6 +#define DCRN_CPC0_JTAGID_BASE 0x0F7 +#define DCRN_CPC0_SPARE_BASE 0x0F8 +#define DCRN_CPC0_PCI_BASE 0x0F9 + + +#define IBM_CPM_GPT 0x80000000 /* GPT interface */ +#define IBM_CPM_PCI 0x40000000 /* PCI bridge */ +#define IBM_CPM_UIC 0x00010000 /* Universal Int Controller */ +#define IBM_CPM_CPU 0x00008000 /* processor core */ +#define IBM_CPM_EBC 0x00002000 /* EBC controller */ +#define IBM_CPM_SDRAM0 0x00004000 /* SDRAM memory controller */ +#define IBM_CPM_GPIO0 0x00001000 /* General Purpose IO */ +#define IBM_CPM_TMRCLK 0x00000400 /* CPU timers */ +#define IBM_CPM_PLB 0x00000100 /* PLB bus arbiter */ +#define IBM_CPM_OPB 0x00000080 /* PLB to OPB bridge */ +#define IBM_CPM_DMA 0x00000040 /* DMA controller */ +#define IBM_CPM_IIC0 0x00000010 /* IIC interface */ +#define IBM_CPM_UART1 0x00000002 /* serial port 0 */ +#define IBM_CPM_UART0 0x00000001 /* serial port 1 */ +#define DFLT_IBM4xx_PM ~(IBM_CPM_PCI | IBM_CPM_CPU | IBM_CPM_DMA \ + | IBM_CPM_OPB | IBM_CPM_EBC \ + | IBM_CPM_SDRAM0 | IBM_CPM_PLB \ + | IBM_CPM_UIC | IBM_CPM_TMRCLK) +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_MAL_BASE 0x180 +#define DCRN_OCM0_BASE 0x018 +#define DCRN_PLB0_BASE 0x084 +#define DCRN_PLLMR_BASE 0x0B0 +#define DCRN_POB0_BASE 0x0A0 +#define DCRN_SDRAM0_BASE 0x010 +#define DCRN_UIC0_BASE 0x0C0 +#define UIC0 DCRN_UIC0_BASE + +#include + +#endif /* __ASM_IBM405EP_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibm405gpr.c linuxppc-2.4/arch/ppc/platforms/ibm405gpr.c --- linux-2.4.22/arch/ppc/platforms/ibm405gpr.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibm405gpr.c 2003-03-28 08:48:01.000000000 +1100 @@ -0,0 +1,108 @@ +/* + * + * Copyright 2000-2002 MontaVista Software Inc. + * Current maintainer + * Armin Kuster akuster@mvista.com + * + * Module name: ibm405gpr.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct ocp_func_emac_data ibm405gpr_emac0_def = { + .zmii_idx = -1, /* ZMII device index */ + .zmii_mux = 0, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 0, /* MAL rx channel number */ + .mal_tx1_chan = 0, /* MAL tx channel 1 number */ + .mal_tx2_chan = 1, /* MAL tx channel 2 number */ + .wol_irq = BL_MAC_WOL, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ +}; + +static struct ocp_func_mal_data ibm405gpr_mal0_def = { + .num_tx_chans = 2*EMAC_NUMS, /* Number of TX channels */ + .num_rx_chans = EMAC_NUMS, /* Number of RX channels */ +}; + +struct ocp_def core_ocp[] __initdata = { + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_OPB, + .index = 0, + .paddr = OPB_BASE_START, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = UART0_IO_BASE, + .irq = UART0_INT, + .pm = IBM_CPM_UART0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = UART1_IO_BASE, + .irq = UART1_INT, + .pm = IBM_CPM_UART1 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .paddr = IIC0_BASE, + .irq = IIC0_IRQ, + .pm = IBM_CPM_IIC0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .paddr = GPIO0_BASE, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_MAL, + .paddr = OCP_PADDR_NA, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + .additions = &ibm405gpr_mal0_def, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 0, + .paddr = EMAC0_BASE, + .irq = BL_MAC_ETH0, + .pm = IBM_CPM_EMAC0, + .additions = &ibm405gpr_emac0_def, + }, + { .vendor = OCP_VENDOR_INVALID + } +}; diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibm405gpr.h linuxppc-2.4/arch/ppc/platforms/ibm405gpr.h --- linux-2.4.22/arch/ppc/platforms/ibm405gpr.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibm405gpr.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,191 @@ +/* + * ibm405gpr.h + * + * + * Armin Kuster akuster@mvista.com + * Aug, 2002 + * + * + * Copyright 2002 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Version 1.0 (10/01/02) - A. Kuster + * Initial version + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBM405GPR_H__ +#define __ASM_IBM405GPR_H__ + +#include +#include + +/* ibm405.h at bottom of this file */ + +/* PCI + * PCI Bridge config reg definitions + * see 17-19 of manual + */ + +#define PPC405_PCI_CONFIG_ADDR 0xeec00000 +#define PPC405_PCI_CONFIG_DATA 0xeec00004 + +#define PPC405_PCI_PHY_MEM_BASE 0x80000000 /* hose_a->pci_mem_offset */ + /* setbat */ +#define PPC405_PCI_MEM_BASE PPC405_PCI_PHY_MEM_BASE /* setbat */ +#define PPC405_PCI_PHY_IO_BASE 0xe8000000 /* setbat */ +#define PPC405_PCI_IO_BASE PPC405_PCI_PHY_IO_BASE /* setbat */ + +#define PPC405_PCI_LOWER_MEM 0x80000000 /* hose_a->mem_space.start */ +#define PPC405_PCI_UPPER_MEM 0xBfffffff /* hose_a->mem_space.end */ +#define PPC405_PCI_LOWER_IO 0x00000000 /* hose_a->io_space.start */ +#define PPC405_PCI_UPPER_IO 0x0000ffff /* hose_a->io_space.end */ + +#define PPC405_ISA_IO_BASE PPC405_PCI_IO_BASE + +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +/* serial port defines */ +#define RS_TABLE_SIZE 2 + +#define UART0_INT 0 +#define UART1_INT 1 + +#define PCIL0_BASE 0xEF400000 +#define UART0_IO_BASE 0xEF600300 +#define UART1_IO_BASE 0xEF600400 +#define IIC0_BASE 0xEF600500 +#define OPB0_BASE 0xEF600600 +#define GPIO0_BASE 0xEF600700 +#define EMAC0_BASE 0xEF600800 +#define BL_MAC_WOL 9 /* WOL */ +#define BL_MAL_SERR 10 /* MAL SERR */ +#define BL_MAL_TXDE 13 /* MAL TXDE */ +#define BL_MAL_RXDE 14 /* MAL RXDE */ +#define BL_MAL_TXEOB 11 /* MAL TX EOB */ +#define BL_MAL_RXEOB 12 /* MAL RX EOB */ +#define BL_MAC_ETH0 15 /* MAC */ + +#define EMAC_NUMS 1 +#define IIC0_IRQ 2 + +#define IIC_OWN 0x55 +#define IIC_CLOCK 50 +#define BD_EMAC_ADDR(e,i) bi_enetaddr[i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: (u8 *)UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_DEBUG_IO_BASE UART1_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) +#endif + +/* DCR defines */ +#define DCRN_CHCR_BASE 0x0B1 +#define DCRN_CHPSR_BASE 0x0B4 +#define DCRN_CPMSR_BASE 0x0B8 +#define DCRN_CPMFR_BASE 0x0BA + +#define CHR0_U0EC 0x00000080 /* Select external clock for UART0 */ +#define CHR0_U1EC 0x00000040 /* Select external clock for UART1 */ +#define CHR0_UDIV 0x0000003E /* UART internal clock divisor */ +#define CHR1_CETE 0x00800000 /* CPU external timer enable */ + +#define DCRN_CHPSR_BASE 0x0B4 +#define PSR_PLL_FWD_MASK 0xC0000000 +#define PSR_PLL_FDBACK_MASK 0x30000000 +#define PSR_PLL_TUNING_MASK 0x0E000000 +#define PSR_PLB_CPU_MASK 0x01800000 +#define PSR_OPB_PLB_MASK 0x00600000 +#define PSR_PCI_PLB_MASK 0x00180000 +#define PSR_EB_PLB_MASK 0x00060000 +#define PSR_ROM_WIDTH_MASK 0x00018000 +#define PSR_ROM_LOC 0x00004000 +#define PSR_PCI_ASYNC_EN 0x00001000 +#define PSR_PCI_ARBIT_EN 0x00000400 + +#define IBM_CPM_IIC0 0x80000000 /* IIC interface */ +#define IBM_CPM_PCI 0x40000000 /* PCI bridge */ +#define IBM_CPM_CPU 0x20000000 /* processor core */ +#define IBM_CPM_DMA 0x10000000 /* DMA controller */ +#define IBM_CPM_OPB 0x08000000 /* PLB to OPB bridge */ +#define IBM_CPM_DCP 0x04000000 /* CodePack */ +#define IBM_CPM_EBC 0x02000000 /* ROM/SRAM peripheral controller */ +#define IBM_CPM_SDRAM0 0x01000000 /* SDRAM memory controller */ +#define IBM_CPM_PLB 0x00800000 /* PLB bus arbiter */ +#define IBM_CPM_GPIO0 0x00400000 /* General Purpose IO (??) */ +#define IBM_CPM_UART0 0x00200000 /* serial port 0 */ +#define IBM_CPM_UART1 0x00100000 /* serial port 1 */ +#define IBM_CPM_UIC 0x00080000 /* Universal Interrupt Controller */ +#define IBM_CPM_TMRCLK 0x00040000 /* CPU timers */ +#define IBM_CPM_EMAC0 0x00020000 /* on-chip ethernet MM unit */ +#define DFLT_IBM4xx_PM ~(IBM_CPM_PCI | IBM_CPM_CPU | IBM_CPM_DMA \ + | IBM_CPM_OPB | IBM_CPM_EBC \ + | IBM_CPM_SDRAM0 | IBM_CPM_PLB \ + | IBM_CPM_UIC | IBM_CPM_TMRCLK) + +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_MAL_BASE 0x180 +#define DCRN_OCM0_BASE 0x018 +#define DCRN_PLB0_BASE 0x084 +#define DCRN_PLLMR_BASE 0x0B0 +#define DCRN_POB0_BASE 0x0A0 +#define DCRN_SDRAM0_BASE 0x010 +#define DCRN_UIC0_BASE 0x0C0 +#define UIC0 DCRN_UIC0_BASE + +#include + +#endif /* __ASM_IBM405GPR_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibm405lp.c linuxppc-2.4/arch/ppc/platforms/ibm405lp.c --- linux-2.4.22/arch/ppc/platforms/ibm405lp.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibm405lp.c 2003-06-02 14:37:41.000000000 +1000 @@ -0,0 +1,771 @@ +/* + * arch/ppc/platforms/ibm405lp.c 405LP-specific code + * + * 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 + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * Bishop Brock + * IBM Research, Austin Center for Low-Power Computing + * bcbrock@us.ibm.com + * March, 2002 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Set up the 405LP clock and power management unit for aggressive power + management. + + Briefly, there are 3 CPM "classes": + + Class 1 - Either completely asleep or awake. The "force" state is + equivalent to the "enabled" state. Many Class 1 units are + critical system components and are never power managed. + + Class 2 - Can be enabled for power management, where sleep requests are + made by the peripheral, typically after an inactivity timeout. + When sleeping, critical interfaces remain active, and + awaken the unit whenever it is targeted with a transaction. + + Class 3 - Can be enabled for power management, where sleep requests are + made by the CPM. Power management for these units typically + will require intelligence in a device driver. + + In the current implementation, the "force" bits are only used on Class 1 + devices, and only when the associated driver has the intelligence necessary + to "unforce" the power management state. A previous scheme, which tried to + enable power management based on whether a particular driver was compiled + with the kernel, caused many problems and is never used here. + + Class 2 devices with timeouts are normally initialized for the most + aggressive values. There is no power management benefit of "forcing" Class + 2 devices over letting their inactivity timeouts take effect. Therefore, + after being set up here, Class 2 device drivers don't need to worry about + CPM. + + No Class 3 devices are handled yet. */ + +#ifdef CONFIG_PM +void +ibm405lp_setup_cpm(void) +{ + u32 force = 0; + u32 enable = 0; + dma0_slp_t dma0_slp; + dcp0_cfg_t dcp0_cfg; + ebc0_cfg_t ebc0_cfg; + sdram0_cfg_t sdram0_cfg; + sdram0_pmit_t sdram0_pmit; + sla0_slpmd_t sla0_slpmd; + + /* Initialize the CPM state */ + + mtdcr(DCRN_CPMFR, force); + mtdcr(DCRN_CPMER, enable); + + /* IIC - Class 3 - Not handled yet. The driver should at least be able + to force/unforce itself. */ + + /* CPU - class 2 - There doesn't appear to be a timeout associated with + this, and the exact function is not documented anywhere. It saves a + lot of power, though. I assume this gates core clocks when the CPU + core is asleep, and probably adds a couple of cycles of latency when + the CPU core wakes up. */ + + enable |= IBM_CPM_CPU; + + /* DMA - class 2. Set for the minimum timeout, which is 32 cycles. */ + + dma0_slp.reg = mfdcr(DCRN_SLP); + dma0_slp.fields.sme = 1; + dma0_slp.fields.idu = 0; + mtdcr(DCRN_SLP, dma0_slp.reg); + enable |= IBM_CPM_DMA; + + /* BRG - Class 2. Seems to crash the system when enabled in 405LP Pass + 1 + + DCP (CodePack) - Class 2. The semantics of the sleep delay are not + documented. We'll use 32 (what the heck). */ + + dcp0_cfg.reg = mfdcri(DCRN_DCP0, CFG); + dcp0_cfg.fields.slen = 1; + dcp0_cfg.fields.sldy = 32; + mtdcri(DCRN_DCP0, CFG, dcp0_cfg.reg); + enable |= IBM_CPM_DCP; + + /* EBC - Class 2. Set for minimum timeout, which is 32 cycles. [ I + think this is 32. It may be 64. I don't trust the documentation. ] + */ + + ebc0_cfg.reg = mfdcri(DCRN_EBC0, CFG); + ebc0_cfg.fields.pme = 1; + ebc0_cfg.fields.pmt = 1; + mtdcri(DCRN_EBC0, CFG, ebc0_cfg.reg); + enable |= IBM_CPM_EBC; + + /* SDRAM - Class 2. Set for the minimum 32-cycle timeout. + + The documentation on this core is clear - waking a sleeping SDRAM + controller takes 2 PLB cycles, which is added to the latency of the + memory operation. If someone can prove that this is affecting + performance we can easily back this off. */ + + sdram0_cfg.reg = mfdcri(DCRN_SDRAM0, CFG); + sdram0_cfg.fields.pme = 1; + mtdcri(DCRN_SDRAM0, CFG, sdram0_cfg.reg); + + sdram0_pmit.reg = mfdcri(DCRN_SDRAM0, PMIT); + sdram0_pmit.fields.cnt = 0; + mtdcri(DCRN_SDRAM0, PMIT, sdram0_pmit.reg); + + enable |= IBM_CPM_SDRAM; + + /* PLB - Class 2. Seems to crash the system when enabled in 405LP Pass + 1. + + GPIO - Class 1. This unit is used for many things, and no single + driver controls all GPIO. It's best left unmanaged (it doesn't use + much power anyway). NB: 405LP Pass 1 erratum - forcing PM on GPIO + kills the TPC. + + UART0 - Class 1 + UART1 - Class 1 + + Someone should work on the serial port drivers to enable PM support + for them. Any takers? + + UIC - Class 1 + CPU_TMRCLK - Class 1 + + These system resources are never power managed. */ + + /* SLA - Class 2. Set for the minimum 32-cycle timeout. */ + + sla0_slpmd.reg = mfdcri(DCRN_SLA0, SLPMD); + sla0_slpmd.fields.slen = 1; + sla0_slpmd.fields.slcr = 0; + mtdcri(DCRN_SLA0, SLPMD, sla0_slpmd.reg); + enable |= IBM_CPM_SLA; + + /* CSI - Class 1. + TPC - Class 1. + TDES - Class 1. + + The drivers for these units are power-aware, and manage the device + properly. By default these units are forced off at boot. */ + + force |= IBM_CPM_CSI; + force |= IBM_CPM_TPC; + force |= IBM_CPM_TDES; + + /* Set the CPM state */ + + mtdcr(DCRN_CPMFR, force); + mtdcr(DCRN_CPMER, enable); +} +#endif /* CONFIG_PM */ + +/* This routine is included here because the framebuffer driver needs a way to + tell the system the Pixel clock frequency it needs, regardless of whether + run-time frequency scaling is configured. A hook and a couple of global + variables are always present and will be used by the RTVFS driver if it is + loaded. + + Pixel clock setting is kind of a hack, as the frequency steps available from + the PLB/PixClk divider may be too large to guarantee that we'll hit within + the given limits. We never set the frequency above the upper bound, but due + to quantization may need to set the frequency below the lower bound. So far + it works OK for the panels we've tried. + + In general, the choice of a system clock frequency should be made with + consideration of the LCD panel to be attached, to guarantee a good clock + divider for the Pixel clock regardless of frequency scaling. + + Clock frequencies are in KHz. If pixclk_min or pixclk_max are zero, we set + the lowest possible frequency to conserve energy. */ + +int (*set_pixclk_hook) (unsigned pixclk_min, unsigned pixclk_max) = NULL; +unsigned last_pixclk_min = 0; +unsigned last_pixclk_max = 0; + +EXPORT_SYMBOL(set_pixclk_hook); +EXPORT_SYMBOL(last_pixclk_min); +EXPORT_SYMBOL(last_pixclk_max); + +int +ibm405lp_set_pixclk(unsigned pixclk_min, unsigned pixclk_max) +{ + unsigned divider; + bd_t *bip = (bd_t *) __res; + unsigned plb_khz = bip->bi_busfreq / 1000; + cpc0_cgcr1_t cgcr1; + + if (set_pixclk_hook) { + return (set_pixclk_hook) (pixclk_min, pixclk_max); + } else { + if ((pixclk_min == 0) || (pixclk_max == 0)) + divider = CPC0_DIV_MAX; + else { + divider = plb_khz / pixclk_min; + if (divider == 0) + divider = 1; + if ((divider < CPC0_DIV_MAX) && + ((plb_khz / divider) > pixclk_max)) + divider++; + } + + cgcr1.reg = mfdcr(DCRN_CPC0_CGCR1); + cgcr1.fields.ppxl = CPC0_DIV_ENCODE(divider); + mtdcr(DCRN_CPC0_CGCR1, cgcr1.reg); + + last_pixclk_min = pixclk_min; + last_pixclk_max = pixclk_max; + return 0; + } +} + +/* Somewhat misleading name, as well as the EBC, this sets up the UIC + and CPC ready for PCMCIA operation */ +static void +pccf_ebc_setup(void) +{ + /* Set up EBC bank 4 as per PCCF docs., assuming 66 MHz EBC bus. The + ready timeout is set for 1024 cycles (~ 15 us at 66 MHz), unless + someone else has already set it for 2048. In the event of a + timeout we'll get a Data Machine Check. */ + + unsigned long bits, mask, flags; + + save_flags(flags); + cli(); + + /* Program EBC0_CFG for ready timeout */ + + mtdcr(DCRN_EBC0_CFGADDR, DCRN_EBC0_CFG); + bits = mfdcr(DCRN_EBC0_CFGDATA); + if ((bits & EBC_CFG_RTC) != EBC_CFG_RTC_2048) + mtdcr(DCRN_EBC0_CFGDATA, (bits & ~EBC_CFG_RTC) | EBC_CFG_RTC_1024); + + /* Program EBC bank properties : 32 MB, 16-bit RW bank; + BME = 0, TWT = 22, CSN = 2, OEN = 3, WBN = WBF = 0, TH = 5, + RE = 1, SOR = 0, BEM = 1 */ + + mtdcr(DCRN_EBC0_CFGADDR, DCRN_EBC0_B4CR); + mtdcr(DCRN_EBC0_CFGDATA, (PCCF_4XX_PADDR & 0xfff00000) | 0x000ba000); + mtdcr(DCRN_EBC0_CFGADDR, DCRN_EBC0_B4AP); + mtdcr(DCRN_EBC0_CFGDATA, 0x0b0b0b40); + + /* Program the UIC for active-high, level-triggered interrupts. Note + that the active-low PCMCIA interrupt pin is inverted by the PCCF + macro. */ + + mask = (0x80000000 >> PCCF_4XX_MACRO_IRQ) | + (0x80000000 >> PCCF_4XX_CARD_IRQ); + + bits = mfdcr(DCRN_UIC0_PR); + bits |= mask; + mtdcr(DCRN_UIC0_PR, bits); + + bits = mfdcr(DCRN_UIC0_TR); + bits &= ~mask; + mtdcr(DCRN_UIC0_TR, bits); + + /* Clear CPC0_CR0[PCMD] to enable the PCMCIA controller */ + + mtdcr(DCRN_CPC0_CR0, mfdcr(DCRN_CPC0_CR0) & ~0x00000200); + + restore_flags(flags); +} + +/* Map the PCCF controller's memory windows. + * + * HACK ALERT: Logically this belongs in the pccf_4xx driver itself, + * however that causes problems because it happens so late in + * initialization. We want to use some ISA-ish drivers (notably + * 8390.c) on memory mapped devices by using the + * ioaddr=(memaddr-_IO_BASE) hack. If _IO_BASE is the PCMCIA ISA IO + * space (which we want so PC Card drivers using ISA IO work) but is + * not initialized until the pccf_4xx driver starts, this could well + * be after drivers like 8390 have initialized and computed a fake + * "IO" address which is now incorrect. Putting the ioremap()ing of + * the PCCF macro in the chip/board setup code works around this + * problem. */ +int +ibm405lp_setup_pccf(volatile u16 **vaddr, unsigned long *io_base, + unsigned long *mem_base) +{ + pccf_ebc_setup(); + + *vaddr = ioremap(PCCF_4XX_MACRO_PADDR, PCCF_4XX_MACRO_WINSIZE); + + if (*vaddr == NULL) { + printk(KERN_ERR "pccf_4xx: ioremap macro at 0x%lx failed.\n", + PCCF_4XX_MACRO_PADDR); + return -EBUSY; + } + + printk("ibm405lp_setup_pcmcia: phys addr = %lx, virt addr = %p\n", + PCCF_4XX_MACRO_PADDR, *vaddr); + + *io_base = (unsigned long) ioremap(PCCF_4XX_IO_PADDR, + PCCF_4XX_IO_WINSIZE); + if (*io_base == 0) { + printk(KERN_ERR "pccf_4xx: ioremap io at 0x%lx failed.\n", + PCCF_4XX_IO_PADDR); + return -EBUSY; + } + + *mem_base = (unsigned long) ioremap(PCCF_4XX_MEM_PADDR, + PCCF_4XX_MEM_WINSIZE); + if (*mem_base == 0) { + printk(KERN_ERR "pccf_4xx: ioremap mem at 0x%lx failed.\n", + PCCF_4XX_MEM_PADDR); + return -EBUSY; + } + + return 0; +} + + +/**************************************************************************** + * TODC + ****************************************************************************/ + +/* + * The 405LP includes an MC146818-equivalent core accessed via a DCR + * wrapper. The 405LP does not implement the NVRAM. + */ + +long __init ibm405lp_time_init(void) +{ + static int not_initialized = 1; + + /* Make sure clocks are running */ + if (not_initialized) { + /* Reset the core and ensure it's enabled. */ + mtdcr(DCRN_RTC0_WRAP, 0); /* toggle NRST & NMR */ + mtdcr(DCRN_RTC0_WRAP, 3); + mtdcr(DCRN_RTC0_CR0, 0x60); /* No divider chain, No square wave */ + mtdcr(DCRN_RTC0_CR1, 0x80); /* Disable update cycles/interrupts*/ + mtdcr(DCRN_RTC0_WRAP, 0); /* toggle NRST & NMR */ + mtdcr(DCRN_RTC0_WRAP, 3); + + /* if necessary, set the input clock frequency */ + if ((mfdcr(DCRN_RTC0_CR0) >> 4) != RTC_DVBITS) { + printk(KERN_WARNING "Warning: RTC frequency was incorrect\n"); + mtdcr(DCRN_RTC0_CR0, + ((RTC_DVBITS & 0x7) << 4) | (mfdcr(DCRN_RTC0_CR0) & 0xf)); + } + + mtdcr(DCRN_RTC0_CR1, mfdcr(DCRN_RTC0_CR1) & 0x7f); /* allow updates */ + + not_initialized = 0; + } + + return 0; +} + +unsigned long ibm405lp_get_rtc_time(void) +{ + uint year, mon, day, hour, min, sec; + uint i, pm; + u_char save_control, uip; + + spin_lock(&rtc_lock); + save_control = mfdcr(DCRN_RTC0_CR1); + + for (i=0; i<100000000; i++) { + uip = mfdcr(DCRN_RTC0_CR0); + sec = mfdcr(DCRN_RTC0_SEC) & 0x7f; + min = mfdcr(DCRN_RTC0_MIN) & 0x7f; + hour = mfdcr(DCRN_RTC0_HR) & 0xff; + day = mfdcr(DCRN_RTC0_DOM) & 0x3f; + mon = mfdcr(DCRN_RTC0_MONTH) & 0x1f; + year = mfdcr(DCRN_RTC0_YEAR) & 0xff; + + uip |= mfdcr(DCRN_RTC0_CR0); + if ((uip & RTC_UIP) == 0) break; + } + + spin_unlock(&rtc_lock); + + pm = hour & 0x80; + hour = hour & 0x3f; + + if (((save_control & RTC_DM_BINARY) == 0) || + RTC_ALWAYS_BCD) { + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + } + + + /* 11:59 AM -> 12:00 PM ->-> 1:00 PM ->-> 11:59 PM -> 12:00 AM */ + if (!(save_control & RTC_24H)) { + if (pm && (hour != 12)) + hour += 12; + if (!pm && (hour == 12)) + hour = 0; + } + + year = year + 1900; + if (year < 1970) { + year += 100; + } + + return mktime(year, mon, day, hour, min, sec); +} + +int ibm405lp_set_rtc_time(unsigned long nowtime) +{ + struct rtc_time tm; + u_char save_control, save_freq_select, pm = 0; + + spin_lock(&rtc_lock); + to_tm(nowtime, &tm); + + save_control = mfdcr(DCRN_RTC0_CR1); + save_freq_select = mfdcr(DCRN_RTC0_CR0); + mtdcr(DCRN_RTC0_CR0, save_freq_select | RTC_DIV_RESET2); + + tm.tm_year = (tm.tm_year - 1900) % 100; + + /* 11:59 AM -> 12:00 PM ->-> 1:00 PM ->-> 11:59 PM -> 12:00 AM */ + if (!(save_control & RTC_24H)) { + if (tm.tm_hour == 0) + tm.tm_hour = 12; + else if (tm.tm_hour >= 12) { + pm = 0x80; + if (tm.tm_hour > 12) + tm.tm_hour -= 12; + } + } + + if (((save_control & RTC_DM_BINARY) == 0) || + RTC_ALWAYS_BCD) { + + BIN_TO_BCD(tm.tm_sec); + BIN_TO_BCD(tm.tm_min); + BIN_TO_BCD(tm.tm_hour); + BIN_TO_BCD(tm.tm_mon); + BIN_TO_BCD(tm.tm_mday); + BIN_TO_BCD(tm.tm_year); + } + + tm.tm_hour |= pm; + + mtdcr(DCRN_RTC0_SEC, tm.tm_sec); + mtdcr(DCRN_RTC0_MIN, tm.tm_min); + mtdcr(DCRN_RTC0_HR, tm.tm_hour); + mtdcr(DCRN_RTC0_MONTH, tm.tm_mon); + mtdcr(DCRN_RTC0_DOM, tm.tm_mday); + mtdcr(DCRN_RTC0_YEAR, tm.tm_year); + mtdcr(DCRN_RTC0_WRAP, 0); /* Reset divider chain */ + mtdcr(DCRN_RTC0_WRAP, 3); + mtdcr(DCRN_RTC0_CR0, save_freq_select); + + spin_unlock(&rtc_lock); + return 0; +} + +/* ibm405lp_set_rtc_sqw() + * Set the RTC squarewave frequency and enable SQW output. This is needed to + * drive the APM watchdog. + */ +void ibm405lp_set_rtc_sqw(unsigned long rsbits) +{ + /* set RS field */ + mtdcr(DCRN_RTC0_CR0, (mfdcr(DCRN_RTC0_CR0) & 0xf0) | (rsbits & 0xf)); + + /* set SQWE (enable squarewave output) */ + mtdcr(DCRN_RTC0_CR1, mfdcr(DCRN_RTC0_CR1) | 0x08); +} + +/* The RTC only has a 24-hour alarm capability, so only the hours, minutes and + * seconds fields of the rtc_time struct are used in alarm functions. This + * implementation converts all out-of-range inputs to 'don't cares' (0xff), and + * returns 'don't cares' verbatim. Somewhat inspired by drivers/char/rtc.c + */ + +static inline int +dont_care(uint value) +{ + return (value & 0xc0) == 0xc0; +} + +void ibm405lp_get_rtc_alm_time(struct rtc_time *alm_tm) +{ + uint hour, min, sec, pm; + u_char save_control; + + spin_lock_irq(&rtc_lock); + sec = mfdcr(DCRN_RTC0_SECAL); + min = mfdcr(DCRN_RTC0_MINAL); + hour = mfdcr(DCRN_RTC0_HRAL); + save_control = mfdcr(DCRN_RTC0_CR1); + spin_unlock_irq(&rtc_lock); + + if (!dont_care(hour)) { + pm = hour & 0x80; + hour = hour & 0x3f; + } + + if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + if (!dont_care(sec)) BCD_TO_BIN(sec); + if (!dont_care(min)) BCD_TO_BIN(min); + if (!dont_care(hour)) BCD_TO_BIN(hour); + } + + /* 11:59 AM -> 12:00 PM ->-> 1:00 PM ->-> 11:59 PM -> 12:00 AM */ + if (!dont_care(hour) && !(save_control & RTC_24H)) { + if (pm && (hour != 12)) + hour += 12; + if (!pm && (hour == 12)) + hour = 0; + } + + alm_tm->tm_hour = hour; + alm_tm->tm_min = min; + alm_tm->tm_sec = sec; +} + +void ibm405lp_set_rtc_alm_time(struct rtc_time *alm_tm) +{ + uint hour, min, sec, pm = 0; + u_char save_control; + + hour = alm_tm->tm_hour; + min = alm_tm->tm_min; + sec = alm_tm->tm_sec; + + if (hour > 23) hour = 0xff; + if (min > 59) min = 0xff; + if (sec > 59) sec = 0xff; + + spin_lock_irq(&rtc_lock); + + save_control = mfdcr(DCRN_RTC0_CR1); + + /* 11:59 AM -> 12:00 PM ->-> 1:00 PM ->-> 11:59 PM -> 12:00 AM */ + if (!dont_care(hour) && !(save_control & RTC_24H)) { + if (hour == 0) + hour = 12; + else if (hour >= 12) { + pm = 0x80; + if (hour > 12) + hour -= 12; + } + } + + if (((save_control & RTC_DM_BINARY) == 0) || RTC_ALWAYS_BCD) { + if (!dont_care(sec)) BIN_TO_BCD(sec); + if (!dont_care(min)) BIN_TO_BCD(min); + if (!dont_care(hour)) BIN_TO_BCD(hour); + } + + mtdcr(DCRN_RTC0_HRAL, hour | pm); + mtdcr(DCRN_RTC0_MINAL, min); + mtdcr(DCRN_RTC0_SECAL, sec); + + spin_unlock_irq(&rtc_lock); +} + +/**************************************************************************** + * APM Unit PIC + ****************************************************************************/ + +/* The 405LP "APM" unit implements a hierarchical interrupt controller. This + controller is buried in the APM unit, and is not part of the UIC. Three + special "wakeup" interrupts and the RTC interrupt are handled here. These + interrupts and this controller are special for several reasons: + + 1) They are the only interrupts that can wake the system from an + APM-controlled low-power state. + + 2) The APM DCRs actually function at the RTC frequency. Writes to these + registers do not take effect for several RTC cycles, and must be done with + the 'mtdcr_interlock()' protocol as they all have 'valid' bits. + + 3) The function of this unit is predictable, but difficult to understand. + + In this implementation, RTC and wakeup IRQ handlers will attach to the APM + interrupt using the shared interrupt protocol. The individual handlers are + responsible for setting up their polarity and triggers and clearing the + interrupt conditions they service, using the routines defined here. This + seemed simpler than going into ppc4xx_pic.c and implementing a new type of + cascaded IRQ service, esp. given that that this structure is unique to the + 405LP and has such an unusual specification. + + Hardware Notes: Wakeup input trigger and level conditions are programmable + just like in the UIC, and the "wakeup" interrupts can be used for any + purpose. Unfortunately there's no way to determine the input level of a + "wakeup" input other than by generating interrupt status. The APM interrupt + controller ORs the enabled status of the 4 interrupts it controls and + presents a single, active-high, level-triggered interrupt to the UIC. This + signal is also used as the wakeup indication when the device is asleep. + + The function of the APM0_IER and APM0_ISR are reversed from their usage in + the UIC. Rather than IER masking status in ISR, status for _all_ bits in + ISR only appears when _any_ bit in IER is set. The interrupt signal is the + correct reduced OR of IER & ISR, however. Furthermore, the ISR will only + generate new status if the final value written to it is 0 after any status + clearing operations. + + The RTC0_CR1 and RTC0_ISR registers will be cleared during unpowered sleep + states (power-down, standby, hibernate). The device will still generate RTC + alarm interrupts correctly though, so the clearing must take place on power + up. */ + +void +ibm405lp_apm_dcr_delay(void) +{ + udelay(153); /* 5 RTC cycles at 32768 Hz */ +} + +void +ibm405lp_apm_irq_enable(unsigned apm_irq) +{ + u32 ier = mfdcr(DCRN_APM0_IER); + + mtdcr_interlock(DCRN_APM0_IER, ier | (1 << (31 - apm_irq)), + APM0_IRQ_MASK); + ibm405lp_apm_dcr_delay(); +} + +void +ibm405lp_apm_irq_disable(unsigned apm_irq) +{ + u32 ier = mfdcr(DCRN_APM0_IER); + + mtdcr_interlock(DCRN_APM0_IER, ier & ~(1 << (31 - apm_irq)), + APM0_IRQ_MASK); + ibm405lp_apm_dcr_delay(); +} + +void +ibm405lp_apm_irq_ack(unsigned apm_irq) +{ + mtdcr_interlock(DCRN_APM0_ISR, 1 << (31 - apm_irq), APM0_IRQ_MASK); + ibm405lp_apm_dcr_delay(); + + mtdcr(DCRN_APM0_ISR, 0); + ibm405lp_apm_dcr_delay(); +} + +int +ibm405lp_apm_irq_status(unsigned apm_irq) +{ + return mfdcr(DCRN_APM0_ISR) & mfdcr(DCRN_APM0_IER) & + (1 << (31 - apm_irq)); +} + +/* Setup should be called with the APM IRQ disabled. Since changing parameters + may cause status to be asserted, it is always ack'ed before returning. */ + +void +ibm405lp_apm_irq_setup(unsigned apm_irq, unsigned trigger, unsigned polarity) +{ + u32 itr, ipr; + + itr = mfdcr(DCRN_APM0_ITR) | ((trigger ? 1 : 0) << (31 - apm_irq)); + mtdcr_interlock(DCRN_APM0_ITR, itr, APM0_IRQ_MASK); + ipr = mfdcr(DCRN_APM0_IPR) | ((polarity ? 1 : 0) << (31 - apm_irq)); + mtdcr_interlock(DCRN_APM0_IPR, ipr, APM0_IRQ_MASK); + ibm405lp_apm_dcr_delay(); + ibm405lp_apm_irq_ack(apm_irq); +} + +/* The RTC interrupt is always level-triggered, active high. Others are board + dependent. */ + +void __init +ibm405lp_setup_apm_pic(void) +{ + mtdcr_interlock(DCRN_APM0_IER, 0, APM0_IRQ_MASK); + ibm405lp_apm_dcr_delay(); + ibm405lp_apm_irq_setup(APM0_IRQ_RTC, 0, 1); +} + +/* + * OCP stuff definition + */ +struct ocp_def core_ocp[] __initdata = { + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_OPB, + .index = 0, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = UART0_IO_BASE, + .irq = UART0_INT, + .pm = IBM_CPM_UART0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = UART1_IO_BASE, + .irq = UART1_INT, + .pm = IBM_CPM_UART1 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .paddr = IIC0_BASE, + .irq = IIC0_IRQ, + .pm = IBM_CPM_IIC0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .paddr = GPIO0_BASE, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0 + }, + + { .vendor = OCP_VENDOR_INVALID + } +}; + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibm405lp.h linuxppc-2.4/arch/ppc/platforms/ibm405lp.h --- linux-2.4.22/arch/ppc/platforms/ibm405lp.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibm405lp.h 2003-06-02 14:37:41.000000000 +1000 @@ -0,0 +1,883 @@ +/* + * include/asm-ppc/platforms/ibm405lp.h 405LP-specific definitions + * + * 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 + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * Bishop Brock + * IBM Research, Austin Center for Low-Power Computing + * bcbrock@us.ibm.com + * March, 2002 + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBM405LP_H__ +#define __ASM_IBM405LP_H__ + +#include +#include + +/* See beech.c for a concise diagram of the Beech physical memory map. */ + +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) +#define PVR_405LP 0x41F10000 +#define PVR_405LP_1_0 (PVR_405LP | 0x0890) +#define PVR_405LP_1_1 (PVR_405LP | 0x0991) + +/* Machine-specific register naming for the 4xx processors is a mess. It seems + that everyone had a different idea on how to prefix/abbreviate/configure the + DCR numbers and MMIO addresses. I'm no different! For the 405LP we have + defined all of the DCRs and MMIO address consistently with their names as + documented in the official IBM hardware manual for the processor. + + DCRs are all given a DCRN_ prefix, which seems to be the most + common consistent naming scheme in old code (although the official IBM DCR + names are so unique that there's really little need for the DCRN_). + + At the end of the DCR defines several synonyms are defined for backwards + compatibility, but all new code specific to the 405LP uses the consistent + names. + + Version 07/24/02 1.1 - Armin + added default pm define +*/ + +/***************************************************************************** + * Directly accessed DCRs + *****************************************************************************/ + +/* DCRs used for Indirect Access */ + +#define DCRN_SDRAM0_CFGADDR 0x010 /* Memory Ctlr. DCR Address Register */ +#define DCRN_SDRAM0_CFGDATA 0x011 /* Memory Ctlr. DCR Data Register */ +#define DCRN_EBC0_CFGADDR 0x012 /* Peripheral Ctlr. DCR Address Register */ +#define DCRN_EBC0_CFGDATA 0x013 /* Peripheral Ctlr. DCR Data Register */ +#define DCRN_SLA0_CFGADDR 0x0e0 /* Speech Label Accel. DCR Address Reg. */ +#define DCRN_SLA0_CFGDATA 0x0e1 /* Speech Label Accel. DCR Data Reg. */ +#define DCRN_LCD0_CFGADDR 0x3c8 /* LCD Ctlr. DCR Address Reg. */ +#define DCRN_LCD0_CFGDATA 0x3c9 /* LCD Ctlr. DCR Data Reg. */ + +/* On-chip Buses */ + +#define DCRN_PLB0_BESR 0x084 /* PLB Bus Error Status Register */ +#define DCRN_PLB0_BEAR 0x086 /* PLB Bus Error Address Register */ +#define DCRN_PLB0_ACR 0x087 /* PLB Arbiter Control Register */ +#define DCRN_POB0_BESR0 0x0a0 /* PLB to OPB Bus Error Status Register 0 */ +#define DCRN_POB0_BEAR 0x0a2 /* PLB to OPB Bus Error Address Register */ +#define DCRN_POB0_BESR1 0x0a4 /* PLB to OPB Bus Error Status Register 1 */ + +/* Clocking and Chip Control */ + +#define DCRN_CPC0_PLLMR 0x0b0 /* PLL Mode Register */ +#define DCRN_CPC0_CGCR0 0x0b1 /* Clock Generation Control Register 0 */ +#define DCRN_CPC0_CGCR1 0x0b2 /* Clock Generation Control Register 1 */ +#define DCRN_CPC0_CR0 0x0b5 /* Chip Control Register 0 */ +#define DCRN_CPC0_CR1 0x0b4 /* Chip Control Register 1 */ +#define DCRN_CPC0_PLBAPR 0x0b6 /* PLB Arbiter Priority Register */ +#define DCRN_CPC0_JTAGID 0x0b7 /* JTAG ID Register */ + +/* Clock and Power Management */ + +#define DCRN_CPMSR_BASE 0x0b8 /* CPM Status Register */ +#define DCRN_CPMFR_BASE 0x0ba /* CPM Force Register */ + +/* Universal Interrupt Controller */ + +#define DCRN_UIC0_SR 0x0c0 /* UIC Status Register */ +#define DCRN_UIC0_ER 0x0c2 /* UIC Enable Register */ +#define DCRN_UIC0_CR 0x0c3 /* UIC Critical Register */ +#define DCRN_UIC0_PR 0x0c4 /* UIC Polarity Register */ +#define DCRN_UIC0_TR 0x0c5 /* UIC Triggering Register */ +#define DCRN_UIC0_MSR 0x0c6 /* UIC Masked Status Register */ +#define DCRN_UIC0_VR 0x0c7 /* UIC Vector Register */ +#define DCRN_UIC0_VCR 0x0c8 /* UIC Vector Configuration Register */ + +/* Real-time Clock */ + +#define DCRN_RTC0_SEC 0x140 /* RTC Seconds Register */ +#define DCRN_RTC0_SECAL 0x141 /* RTC Seconds Alarm Register */ +#define DCRN_RTC0_MIN 0x142 /* RTC Minutes Register */ +#define DCRN_RTC0_MINAL 0x143 /* RTC Minutes Alarm Register */ +#define DCRN_RTC0_HR 0x144 /* RTC Hours Register */ +#define DCRN_RTC0_HRAL 0x145 /* RTC Hours Alarm Register */ +#define DCRN_RTC0_DOW 0x146 /* RTC Day of Week Register */ +#define DCRN_RTC0_DOM 0x147 /* RTC Date of Month Register */ +#define DCRN_RTC0_MONTH 0x148 /* RTC Month Register */ +#define DCRN_RTC0_YEAR 0x149 /* RTC Year Register */ +#define DCRN_RTC0_CR0 0x14a /* RTC "A" Register */ +#define DCRN_RTC0_CR1 0x14b /* RTC "B" Register */ +#define DCRN_RTC0_CR2 0x14c /* RTC "C" Register */ +#define DCRN_RTC0_CR3 0x14d /* RTC "D" Register */ +#define DCRN_RTC0_CEN 0x14e /* RTC Century Register */ +#define DCRN_RTC0_WRAP 0x150 /* RTC Wrapper */ + +/* Advanced Power Management Controller */ + +#define DCRN_APM0_ISR 0x160 /* APM Interrupt Status Register */ +#define DCRN_APM0_IER 0x162 /* APM Interrupt Enable Register */ +#define DCRN_APM0_IPR 0x163 /* APM Interrupt Polarity Register */ +#define DCRN_APM0_ITR 0x164 /* APM Interrupt Trigger Register */ +#define DCRN_APM0_CFG 0x165 /* APM Configuration Register */ +#define DCRN_APM0_SR 0x166 /* APM Status Register */ +#define DCRN_APM0_ID 0x167 /* APM Revision ID Register */ + +/* Triple DES Controller */ + +#define DCRN_TDES0_ADDR 0x180 /* TDES OPB Slave Base Address */ +#define DCRN_TDES0_CFG 0x181 /* TDES OPB Slave Configuration */ +#define DCRN_TDES0_STAT 0x182 /* TDES Status */ +#define DCRN_TDES0_ID 0x183 /* TDES Core ID */ + +/* LCD Controller */ + +#define DCRN_LCD0_CFG 0x3c0 /* LCD Configuration Register */ +#define DCRN_LCD0_ICR 0x3c1 /* LCD Interrupt Control Register */ +#define DCRN_LCD0_ISR 0x3c2 /* LCD Interrupt Status Register */ +#define DCRN_LCD0_IMR 0x3c3 /* LCD Interrupt Mask Register */ + +/***************************************************************************** + * Indirectly accessed DCRs. Note that unlike direct-access DCRs whose numbers + * must be hard-coded into the instruction, indirect-access DCR numbers can be + * computed. + *****************************************************************************/ + +/* Offsets for SDRAM Controler Registers */ + +#define DCRN_SDRAM0_BESR0 0x00 /* Bus Error Syndrome Register 0 */ +#define DCRN_SDRAM0_BESR1 0x08 /* Bus Error Syndrome Register 1 */ +#define DCRN_SDRAM0_BEAR 0x10 /* Bus Error Address Register */ +#define DCRN_SDRAM0_CFG 0x20 /* Memory Controller Options 1 */ +#define DCRN_SDRAM0_STATUS 0x24 /* SDRAM controller status */ +#define DCRN_SDRAM0_RTR 0x30 /* Refresh Timer Register */ +#define DCRN_SDRAM0_PMIT 0x34 /* Power Management Idle Timer */ +#define DCRN_SDRAM0_B0CR 0x40 /* Memory Bank 0 Configuration */ +#define DCRN_SDRAM0_B1CR 0x44 /* Memory Bank 1 Configuration */ +#define DCRN_SDRAM0_B2CR 0x48 /* Memory Bank 2 Configuration */ +#define DCRN_SDRAM0_B3CR 0x4c /* Memory Bank 3 Configuration */ +#define DCRN_SDRAM0_TR 0x80 /* Sdram Timing Register 1 */ +#define DCRN_SDRAM0_ECCCFG 0x94 /* ECC Configuration */ +#define DCRN_SDRAM0_ECCESR 0x98 /* ECC Error Status Register */ + +#define SDRAM0_BANKS 4 +#define DCRN_SDRAM0_BnCR(bank) (0x40 + (4 * (bank))) + +/* Offsets for External Bus Controller Registers */ + +#define DCRN_EBC0_B0CR 0x00 /* Peripheral Bank 0 Configuration Register */ +#define DCRN_EBC0_B1CR 0x01 /* Peripheral Bank 1 Configuration Register */ +#define DCRN_EBC0_B2CR 0x02 /* Peripheral Bank 2 Configuration Register */ +#define DCRN_EBC0_B3CR 0x03 /* Peripheral Bank 3 Configuration Register */ +#define DCRN_EBC0_B4CR 0x04 /* Peripheral Bank 4 Configuration Register */ +#define DCRN_EBC0_B5CR 0x05 /* Peripheral Bank 5 Configuration Register */ +#define DCRN_EBC0_B6CR 0x06 /* Peripheral Bank 6 Configuration Register */ +#define DCRN_EBC0_B7CR 0x07 /* Peripheral Bank 7 Configuration Register */ +#define DCRN_EBC0_B0AP 0x10 /* Peripheral Bank 0 Access Parameters */ +#define DCRN_EBC0_B1AP 0x11 /* Peripheral Bank 1 Access Parameters */ +#define DCRN_EBC0_B2AP 0x12 /* Peripheral Bank 2 Access Parameters */ +#define DCRN_EBC0_B3AP 0x13 /* Peripheral Bank 3 Access Parameters */ +#define DCRN_EBC0_B4AP 0x14 /* Peripheral Bank 4 Access Parameters */ +#define DCRN_EBC0_B5AP 0x15 /* Peripheral Bank 5 Access Parameters */ +#define DCRN_EBC0_B6AP 0x16 /* Peripheral Bank 6 Access Parameters */ +#define DCRN_EBC0_B7AP 0x17 /* Peripheral Bank 7 Access Parameters */ +#define DCRN_EBC0_BEAR 0x20 /* Periperal Bus Error Address Register */ +#define DCRN_EBC0_BESR0 0x21 /* Peripheral Bus Error Status Register 0 */ +#define DCRN_EBC0_BESR1 0x22 /* Peripheral Bus Error Status Register 0 */ +#define DCRN_EBC0_CFG 0x23 /* External Peripheral Control Register */ + +#define EBC0_BANKS 8 +#define DCRN_EBC0_BnCR(bank) (bank) +#define DCRN_EBC0_BnAP(bank) (0x10 + (bank)) + +#define EBC_CFG_RTC 0x38000000 +#define EBC_CFG_RTC_16 0x00000000 +#define EBC_CFG_RTC_32 0x08000000 +#define EBC_CFG_RTC_64 0x10000000 +#define EBC_CFG_RTC_128 0x18000000 +#define EBC_CFG_RTC_256 0x20000000 +#define EBC_CFG_RTC_512 0x28000000 +#define EBC_CFG_RTC_1024 0x30000000 +#define EBC_CFG_RTC_2048 0x38000000 + +/* Offsets for LCD Controller DCRs */ + +#define DCRN_LCD0_DER 0x80010000 /* Display Enable Regsiter */ +#define DCRN_LCD0_DCFG 0x80010010 /* Display Configuration Register */ +#define DCRN_LCD0_DSR 0x80010040 /* Display Status Register */ +#define DCRN_LCD0_FRDR 0x80010080 /* Dither and Frame Rate Modulation Reg. */ +#define DCRN_LCD0_SDR 0x800100c0 /* Signal Delay Register */ +#define DCRN_LCD0_ADSR 0x80010100 /* Active Display Size Register */ +#define DCRN_LCD0_TDSR 0x80010104 /* Total Display Size Register */ +#define DCRN_LCD0_FPLCR 0x80010140 /* FPLINE Control Register */ +#define DCRN_LCD0_FPLOR 0x80010144 /* FPLINE Offset Register */ +#define DCRN_LCD0_FPFCR 0x80010148 /* FPFRAME Control Register */ +#define DCRN_LCD0_FPFOR 0x8001014c /* FPFRAME Control Register */ +#define DCRN_LCD0_FPSCR 0x80010150 /* FPSHIFT Control Register */ +#define DCRN_LCD0_FPDRR 0x80010158 /* FPDRDY Control Register */ +#define DCRN_LCD0_FPDCR 0x80010160 /* FPDATA Control Register */ +#define DCRN_LCD0_PFBFR 0x80010800 /* Pixel and Frame Buffer Format Reg. */ +#define DCRN_LCD0_PFR 0x80011000 /* Pixel Format Register */ +#define DCRN_LCD0_FBBAR 0x80011008 /* Frame Buffer Base Address Register */ +#define DCRN_LCD0_STRIDE 0x8001100c /* Stride Register */ +#define DCRN_LCD0_PAR 0x80011800 /* Palette Access Registers Base */ +#define DCRN_LCD0_CER 0x80012000 /* Cursor Enable Register */ +#define DCRN_LCD0_CBAR 0x80012008 /* Cursor Base Address Register */ +#define DCRN_LCD0_CLR 0x8001200c /* Cursor Location Register */ +#define DCRN_LCD0_CC0R 0x80012010 /* Cursor Color 0 */ +#define DCRN_LCD0_CC1R 0x80012014 /* Cursor Color 1 */ + +#define LCD0_PAR_REGS 256 +#define DCRN_LCD0_PARn(n) (DCRN_LCD0_PAR + (4 * (n))) + +/* Offsets for Decompression Controller DCRs */ + +#define DCRN_DCP0_ITOR0 0x00 /* Index Table Origin Register 0 */ +#define DCRN_DCP0_ITOR1 0x01 /* Index Table Origin Register 1 */ +#define DCRN_DCP0_ITOR2 0x02 /* Index Table Origin Register 2 */ +#define DCRN_DCP0_ITOR3 0x03 /* Index Table Origin Register 3 */ +#define DCRN_DCP0_ADDR0 0x04 /* Address Decode Definition Register 0 */ +#define DCRN_DCP0_ADDR1 0x05 /* Address Decode Definition Register 1 */ +#define DCRN_DCP0_CFG 0x40 /* Decompression Controller Cfg. Register */ +#define DCRN_DCP0_ID 0x41 /* Decompression Controller ID Register */ +#define DCRN_DCP0_VER 0x42 /* Decompression Controller Version Register */ +#define DCRN_DCP0_PLBBEAR 0x50 /* Bus Error Address Register (PLB) */ +#define DCRN_DCP0_MEMBEAR 0x51 /* Bus Error Address Register (EBC/SDRAM) */ +#define DCRN_DCP0_ESR 0x52 /* Bus Error Status Register 0 (Masters 0-3) */ + +#define DCRN_DCP0_RAMn(n) (0x400 + (n)) /* Decompression Decode Table Entries + 0x400-0x5FF Low 16-bit decode table + 0x600-0x7FF High 16-bit decode table + */ + +/* Offsets for Speech Label Accelerator DCRs */ + +#define DCRN_SLA0_CR 0x00 /* SLA Control Register */ +#define DCRN_SLA0_SR 0x01 /* SLA Status Register */ +#define DCRN_SLA0_BESR 0x02 /* SLA Bus Error Status Register */ +#define DCRN_SLA0_BEAR 0x03 /* SLA Bus Error Address Register */ +#define DCRN_SLA0_UADDR 0x04 /* SLA PLB Upper Address Register */ +#define DCRN_SLA0_GMBA 0x05 /* SLA General Indirect Memory Base Address */ +#define DCRN_SLA0_GMLL 0x06 /* SLA General Indirect Memory Link List */ +#define DCRN_SLA0_AMBA 0x07 /* SLA Atom Memory Base Address Register */ +#define DCRN_SLA0_ACBA 0x08 /* SLA Accumulator Base Address Register */ +#define DCRN_SLA0_DIBA 0x09 /* SLA Done Indication Base Address Register */ +#define DCRN_SLA0_GPOFF 0x0A /* SLA General Indirect Pass Offset Register */ +#define DCRN_SLA0_SLPMD 0x0B /* SLA Sleep Mode Control Register */ +#define DCRN_SLA0_ID 0x0C /* SLA ID Register */ +#define DCRN_SLA0_GMLLR 0x0D /* SLA General Indirect Memory Link List Reset */ + +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_UIC0_BASE 0x0C0 + +#define UIC0 DCRN_UIC0_BASE + +#define DCRN_CHCR0 DCRN_CPC0_CR0 + +#undef NR_UICS +#define NR_UICS 1 + +/* More memory-mapped I/O bases, etc., esp. for OCP, that should be moved + elsewhere. */ + +#define IIC0_BASE 0xEF600500 +#define OPB0_BASE 0xEF600600 +#define GPIO0_BASE 0xEF600700 + +#define IIC0_IRQ 2 + +/**************************************************************************** + * MMIO Addresses + ***************************************************************************/ + +/* Touch Panel/PWM Controller */ + +#define TPC0_IO_BASE 0xef600a00 + +#define TPC_CR 0x00 /* TPC Command Register */ +#define TPC_PCRX 0x04 /* TPC Precharge Count Register X1 */ +#define TPC_DCRX 0x08 /* TPC Discharge Count Register X1 */ +#define TPC_PCRY 0x0c /* TPC Precharge Count Register Y1 */ +#define TPC_DCRY 0x10 /* TPC Discharge Count Register Y1 */ +#define TPC_RRX 0x14 /* TPC Read Register X1 */ +#define TPC_RRY 0x18 /* TPC Read Register Y1 */ +#define TPC_SRX 0x1c /* TPC Status Register X1 */ +#define TPC_SRY 0x20 /* TPC Status Register Y1 */ + +/* Triple-DES Controller */ + +#define TDES0_IO_BASE 0xef600b00 + +/* The PCMCIA controller driver 4xx_pccf.c is responsible for the EBC setup of + PCMCIA. Externally, EBC bank selects 3..7 take on PCMCIA functions when + PCMCIA is enabled. */ + +#define PCCF_4XX_PADDR (0xf0000000UL) +#define PCCF_4XX_SIZE (32 * 1024 * 1024) +#define PCCF_4XX_MACRO_IRQ UIC_IRQ_EIR5 +#define PCCF_4XX_CARD_IRQ UIC_IRQ_EIR6 + + +/***************************************************************************** + * CPM bits for the 405LP. Field names are as documented in the 405LP manual. + * Backwards-compatible synonyms appear at the end. + *****************************************************************************/ + +#define CPM_BITMASK(i) (((unsigned)0x80000000) >> i) + +#define IBM_CPM_IIC CPM_BITMASK(0) /* IIC Interface */ +#define IBM_CPM_CPU CPM_BITMASK(1) /* Processor Core */ +#define IBM_CPM_DMA CPM_BITMASK(3) /* DMA Controller */ +#define IBM_CPM_BRG CPM_BITMASK(4) /* PLB to OPB Bridge */ +#define IBM_CPM_DCP CPM_BITMASK(5) /* CodePack */ +#define IBM_CPM_EBC CPM_BITMASK(6) /* ROM/SRAM Peripheral Controller */ +#define IBM_CPM_SDRAM CPM_BITMASK(7) /* SDRAM memory controller */ +#define IBM_CPM_PLB CPM_BITMASK(8) /* PLB bus arbiter */ +#define IBM_CPM_GPIO CPM_BITMASK(9) /* General Purpose IO (??) */ +#define IBM_CPM_UART0 CPM_BITMASK(10) /* Serial Port 0 */ +#define IBM_CPM_UART1 CPM_BITMASK(11) /* Serial Port 1 */ +#define IBM_CPM_UIC CPM_BITMASK(12) /* Universal Interrupt Controller */ +#define IBM_CPM_CPU_TMRCLK CPM_BITMASK(13) /* CPU Timers */ +#define IBM_CPM_SLA CPM_BITMASK(14) /* Speech Label Accelerator */ +#define IBM_CPM_CSI CPM_BITMASK(15) /* CODEC Serial Interface */ +#define IBM_CPM_TPC CPM_BITMASK(16) /* Touch Panel Controller */ +#define IBM_CPM_TDES CPM_BITMASK(18) /* Triple DES */ + +#define IBM_CPM_GPIO0 IBM_CPM_GPIO /* ppc4xx_setup.c */ +#define IBM_CPM_IIC0 IBM_CPM_IIC /* ppc4xx_setup.c */ +#define IBM_CPM_IIC1 0 /* ppc4xx_setup.c */ + +#define DFLT_IBM4xx_PM 0 /* for now until we get a better hable on this one - armin */ + +/***************************************************************************** + * UIC IRQ ordinals for the 405LP. IRQ bit names are as documented in the + * 405LP manual (except for reserved fields). Backwards-compatible synonyms + * appear at the end. + *****************************************************************************/ + +#define UIC_IRQ_U0 0 /* UART0 */ +#define UIC_IRQ_U1 1 /* UART1 */ +#define UIC_IRQ_IIC 2 /* IIC */ +#define UIC_IRQ_EM 3 /* EBC ??? */ +#define UIC_IRQ_IRQ4 4 /* Reserved */ +#define UIC_IRQ_D0 5 /* DMA Channel 0 */ +#define UIC_IRQ_D1 6 /* DMA Channel 1 */ +#define UIC_IRQ_D2 7 /* DMA Channel 2 */ +#define UIC_IRQ_D3 8 /* DMA Channel 3 */ +#define UIC_IRQ_IRQ9 9 /* Reserved */ +#define UIC_IRQ_IRQ10 10 /* Reserved */ +#define UIC_IRQ_IRQ11 11 /* Reserved */ +#define UIC_IRQ_IRQ12 12 /* Reserved */ +#define UIC_IRQ_IRQ13 13 /* Reserved */ +#define UIC_IRQ_IRQ14 14 /* Reserved */ +#define UIC_IRQ_IRQ15 15 /* Reserved */ +#define UIC_IRQ_IRQ16 16 /* Reserved */ +#define UIC_IRQ_EC 17 /* ECC Correctable Error ??? */ +#define UIC_IRQ_TPX 18 /* Touch Panel X */ +#define UIC_IRQ_TPY 19 /* Touch Panel Y */ +#define UIC_IRQ_SLA 20 /* SLA Interrupt */ +#define UIC_IRQ_CSI 21 /* CSI Interrupt */ +#define UIC_IRQ_LCD 22 /* LCD Interrupt */ +#define UIC_IRQ_RTC 23 /* RTC Interrupt */ +#define UIC_IRQ_APM 24 /* APM Interrupt */ +#define UIC_IRQ_EIR0 25 /* External IRQ 0 */ +#define UIC_IRQ_EIR1 26 /* External IRQ 1 */ +#define UIC_IRQ_EIR2 27 /* External IRQ 2 */ +#define UIC_IRQ_EIR3 28 /* External IRQ 3 */ +#define UIC_IRQ_EIR4 29 /* External IRQ 4 */ +#define UIC_IRQ_EIR5 30 /* External IRQ 5 */ +#define UIC_IRQ_EIR6 31 /* External IRQ 6 */ + +/* Serial port definitions are per-board. */ + +#ifndef __ASSEMBLY__ + +#include +#include +#include +#include + +/**************************************************************************** + * DCR type structures and field definitions for DCRs manipulated by the 405LP + * Linux port + ****************************************************************************/ + +/* APM0_CFG - APM Configuration Register */ + +/* Three bits were added in 405LP 1.1. The new defs. *should* work for 1.0 if + we're careful - and we really shouldn't be using 1.0 for APM anyway. */ + +typedef union { + u32 reg; + struct { + unsigned int rsvd:14; + unsigned int rtcmd:2; /* RTC mode - Don't Change it! */ + unsigned int rst:1; /* Reset after Cryo sleep */ + unsigned int isp:1; /* Initiate Sleep */ + unsigned int ewt:1; /* Enable Watchdog Timer */ + unsigned int sm:2; /* Sleep Mode */ + unsigned int iica:3; /* I2C Address (low-order 3 bits) */ + unsigned int psc:1; /* Power Select Control */ + unsigned int cdiv:6; /* IIC Clock Divider */ + unsigned int v:1; /* Valid bit */ + } fields; +} apm0_cfg_t; + +#define APM0_CFG_MASK 0xfffc0000 /* AND to clear all non-reserved fields */ + +/* DPM can only set the power control bit and initiate sleep bit. + The initiate sleep bit is included so that we can be sure it's clear + whenever changing voltages. */ + +#define APM0_CFG_DPM_MASK 0xffffbf7f + +#define APM0_CFG_SM_CLOCK_SUSPEND 0 +#define APM0_CFG_SM_POWER_DOWN 1 +#define APM0_CFG_SM_CRYO_STANDBY 2 + +/* APM0_SR - APM Status Register */ + +typedef union { + u32 reg; + struct { + unsigned int rsvd0:20; + unsigned int cdet:1; /* Clock Detect */ + unsigned int en:1; /* APM Enable Indicator */ + unsigned int pfr:1; /* Power Fail Reset? */ + unsigned int rset:1; /* Processor Reset by APM? */ + unsigned int rsrt:1; /* Restart Successful? */ + unsigned int sdwn:1; /* Shutdown Complete */ + unsigned int sigm:1; /* Signature mismatch */ + unsigned int pap:1; /* Previous APM phase */ + unsigned int rsvd1:3; + unsigned int v:1; /* Valid Bit */ + } fields; +} apm0_sr_t; + +#define APM0_SR_MASK 0xfffff00e /* AND to clear all non-reserved fields */ + +/* CPC0_PLLMR - PLL Mode Register */ + +typedef union { + u32 reg; + struct { + unsigned int pmul:5; /* PLL Multiplier */ + unsigned int pdiv:5; /* PLL Divider */ + unsigned int tun:1; /* PLL Tuning Control */ + unsigned int bps:1; /* Bypass SysClk through PLL Divider */ + unsigned int rsvd0:8; /* Reserved */ + unsigned int db2:1; /* Divide VCO by 2 Select */ + unsigned int csel:2; /* PLL Clock Output Select */ + unsigned int rsvd1:8; /* Reserved */ + unsigned int v:1; /* Valid bit */ + } fields; +} cpc0_pllmr_t; + +#define CPC0_PLLMR_MASK 0x000ff1fe /* AND to clear all non-reserved fields */ + +/* The PLL multiplier/divider are always multiples of 4. */ + +#define CPC0_PLLMR_MULDIV_ENCODE(n) ((((unsigned)(n)) / 4) - 1) +#define CPC0_PLLMR_MULDIV_DECODE(n) (((n) + 1) * 4) +#define CPC0_PLLMR_MULDIV_MAX 128 + +#define CPC0_PLLMR_TUN_HIGH 0x1 /* High-band tuning */ +#define CPC0_PLLMR_TUN_LOW 0x0 /* Low-band tuning */ + +#define CPC0_PLLMR_CSEL_REFCLK 0 /* System Reference Clock */ +#define CPC0_PLLMR_CSEL_PLLVCO 1 /* PLL VCO */ +#define CPC0_PLLMR_CSEL_RTC 2 /* RTC */ +#define CPC0_PLLMR_CSEL_RSVD 3 /* Reserved */ + +/* CPC0_CGCR0 - Clock Generation and Control Register 0 */ + +typedef union { + u32 reg; + struct { + unsigned int pcp:5; /* Proc. Core/PLB Clock Divisor */ + unsigned int pcsc:5; /* Proc. Core/SysClkOut Divisor */ + unsigned int pcu:5; /* Proc. Core/UARTSerClk Clock Div. */ + unsigned int u0cs:1; /* UART0 Clock Select */ + unsigned int u1cs:1; /* UART1 Clock Select */ + unsigned int scsel:2; /* SysClkOut Select */ + unsigned int rsvd:13; /* Reserved */ + } fields; +} cpc0_cgcr0_t; + +#define CPC0_CGCR0_MASK 0x00001fff /* AND to clear all non-reserved fields */ + +#define CPC0_CGCR0_SCSEL_OFF 0 /* SysClkOut driven low (low power) */ +#define CPC0_CGCR0_SCSEL_CPU 1 /* Select CPU clock as SysClkOut */ +#define CPC0_CGCR0_SCSEL_PLB 2 /* SysClkOut is PLB Sample Cycle */ +#define CPC0_CGCR0_SCSEL_OPB 3 /* SysClkOut is OPB Sample Cycle */ + +/* CPC0_CGCR1 - Clock Generation and Control Register 1 */ + +typedef union { + u32 reg; + struct { + unsigned int po:5; /* PLB/OPB Clock Divisor */ + unsigned int pext:5; /* PLB/External Clock Divisor */ + unsigned int ppxl:5; /* PLB/LCD Pixel Clock Divisor */ + unsigned int csel:2; /* PerClk Select */ + unsigned int rsvd:15; /* Reserved */ + } fields; +} cpc0_cgcr1_t; + +#define CPC0_CGCR1_MASK 0x00007fff /* AND to clear all non-reserved fields */ + +/* 5-bit clock dividers are directly encoded, except that an encoding of 0 + indicates divide-by-32. */ + +#define CPC0_DIV_MAX 32 +#define CPC0_DIV_VALID(n) (((n) > 0) && ((n) <= CPC0_DIV_MAX)) +#define CPC0_DIV_ENCODE(n) (((unsigned)(n) >= CPC0_DIV_MAX) ? 0 : (unsigned)(n)) +#define CPC0_DIV_DECODE(n) (((n) == 0) ? CPC0_DIV_MAX : (n)) + +#define CPC0_CGCR1_CSEL_OFF 0 /* PerClk driven low (low power) */ +#define CPC0_CGCR1_CSEL_PERCLK 1 /* Select PerClk */ +#define CPC0_CGCR1_CSEL_PLBCLK 2 /* Select PLB clock */ +#define CPC0_CGCR1_CSEL_OPBCLK 3 /* Select OPB clock */ + +/* CPC0_CR0 - Chip Control Register 0 */ + +typedef union { + u32 reg; + struct { + unsigned int rsvd0:7; /* Reserved */ + unsigned int ssr:1; /* SDRAM Self-Refresh on Sleep Req. */ + unsigned int gpms:2; /* GPIO Pin Muxing Select */ + unsigned int u0pms:2; /* UART0 Pin Muxing Select */ + unsigned int u1pms:2; /* UART1 Pin Muxing Select */ + unsigned int ipms:2; /* IIC Pin Muxing Select */ + unsigned int cpms:2; /* CSI Pin Muxing Select */ + unsigned int tpms:2; /* TPC Pin Muxing Select */ + unsigned int irpms:2; /* IRQ Pin Muxing Select */ + unsigned int pcmd:1; /* PCMCIA Mode Disable */ + unsigned int u0dte:1; /* UART0 DMA Transmit Channel Enable */ + unsigned int u0rde:1; /* UART0 DMA Receive Channel Enable */ + unsigned int u0dce:1; /* UART0 DMA CLear on Enable */ + unsigned int rsvd1:6; /* Reserved */ + } fields; +} cpc0_cr0_t; + +#define CPC0_CR0_MASK 0xfe00003f /* AND to clear all non-reserved fields */ + +/* CPC0_CR1 - Chip Control Register 1 */ + +typedef union { + u32 reg; + struct { + unsigned int rsvd0:28; /* Reserved */ + unsigned int tbsed:1; /* TB. Src. in Edge Detect Mode */ + unsigned int edmd:1; /* TB. Src. Edge Detect Mode Disable */ + unsigned int rsvd1:2; /* Reserved */ + } fields; +} cpc0_cr1_t; + +#define CPC0_CR1_MASK 0xfffffff3 /* AND to clear all non-reserved fields */ + +/* DCP0_CFG - DCP Configuration Register */ + +typedef union { + u32 reg; + struct { + unsigned int rsvd0:18; + unsigned int sldy:10; /* Sleep Delay */ + unsigned int slen:1; /* Sleep Enable */ + unsigned int cdb:1; /* Clear Decompression Buffer */ + unsigned int rsvd1:1; + unsigned int ikb:1; /* Enable Decompression */ + } fields; +} dcp0_cfg_t; + +#define DCP0_CFG_MASK 0xffffc002 /* AND to clear all non-reserved fields */ + +/* DMA0_SLP - DMA Sleep Mode Register */ + +typedef union { + u32 reg; + struct { + unsigned idu:5; /* Idle Timer Upper */ + unsigned rsvd0:5; + unsigned sme:1; /* Sleep Mode Enable */ + unsigned rsvd1:21; + } fields; +} dma0_slp_t; + +#define DMA0_SLP_MASK 0x07dfffff /* AND to clear all non-reserved fields */ + +/* EBC0_BnAP - EBC Bank Access Parameters */ + +typedef union { + u32 reg; + struct { + unsigned bme:1; /* Burst Mode Enable */ + unsigned twt:8; /* Transfer Wait (non-burst) */ + unsigned rsvd0:3; + unsigned csn:2; /* Chip Select On Timing */ + unsigned oen:2; /* Output Enable On Timing */ + unsigned wbn:2; /* Write Byte Enable On Timing */ + unsigned wbf:2; /* Write Byte Enable Off Timing */ + unsigned th:3; /* Transfer Hold */ + unsigned re:1; /* Ready Enable */ + unsigned sor:1; /* Sample On Ready */ + unsigned bem:1; /* Byte Enable Mode */ + unsigned pen:1; /* Parity Enable */ + unsigned rsvd1:5; + } fields; +} ebc0_bnap_t; + +#define EBC0_BnAP_MASK 0x0070001f /* AND to clear all non-reserved fields */ + +/* EBC0_BnCR - EBC Bank Configuration Registers */ + +typedef union { + u32 reg; + struct { + unsigned bas:12; /* Base Address */ + unsigned bs:3; /* Bank Size */ + unsigned bu:2; /* Bank Usage */ + unsigned bw:2; /* Bank Width */ + unsigned rsvd:13; + } fields; +} ebc0_bncr_t; + +#define EBC0_BnCR_MASK 0x00001fff /* AND to clear all non-reserved fields */ + +#define EBC0_BnCR_BS_1MB 0 +#define EBC0_BnCR_BS_2MB 1 +#define EBC0_BnCR_BS_4MB 2 +#define EBC0_BnCR_BS_8MB 3 +#define EBC0_BnCR_BS_16MB 4 +#define EBC0_BnCR_BS_32MB 5 +#define EBC0_BnCR_BS_64MB 6 +#define EBC0_BnCR_BS_128MB 7 + +#define EBC0_BnCR_BU_R 1 +#define EBC0_BnCR_BU_W 2 +#define EBC0_BnCR_BU_RW 3 + +#define EBC0_BnCR_BW_8 0 +#define EBC0_BnCR_BW_16 1 +#define EBC0_BnCR_BW_32 2 + +/* EBC0_CFG -EBC Configuration Register */ + +typedef union { + u32 reg; + struct { + unsigned ebtc:1; /* External Bus Three State Control */ + unsigned ptd:1; /* Device-paced Time-out Disable */ + unsigned rtc:3; /* Ready Timeout Count */ + unsigned rsvd0:4; + unsigned cstc:1; /* Chip Select Three State Control */ + unsigned bpf:2; /* Burst Prefetch */ + unsigned rsvd1:2; + unsigned pme:1; /* Power Management Enable */ + unsigned pmt:5; /* Power Management Timer */ + unsigned rsvd2:12; + } fields; +} ebc0_cfg_t; + +#define EBC0_CFG_MASK 0x078c0fff /* AND to clear all non-reserved fields */ + +#define EBC0_CFG_RTC_16 0 +#define EBC0_CFG_RTC_32 1 +#define EBC0_CFG_RTC_64 2 +#define EBC0_CFG_RTC_128 3 +#define EBC0_CFG_RTC_256 4 +#define EBC0_CFG_RTC_512 5 +#define EBC0_CFG_RTC_1024 6 +#define EBC0_CFG_RTC_2048 7 + +/* SDRAM0_CFG - SDRAM Controller Configuration Register */ + +typedef union { + u32 reg; + struct { + unsigned int dce:1; /* SDRAM Controller Enable */ + unsigned int sre:1; /* Self-Refresh Enable */ + unsigned int pme:1; /* Power Management Enable */ + unsigned int rsvd0:1; + unsigned int regen:1; /* Registered Memory Enable */ + unsigned int drw:2; /* SDRAM Width */ + unsigned int brpf:2; /* Burst Read Prefetch Granularity */ + unsigned int rsvd1:1; + unsigned int emdulr:1; /* Enable Memory Data Unless Read */ + unsigned int rsvd2:21; + } fields; +} sdram0_cfg_t; + +#define SDRAM0_CFG_MASK 0x106fffff /* AND to clear all non-reserved fields */ + +#define SDRAM0_CFG_BRPF_16 1 +#define SDRAM0_CFG_BRPF_32 2 + +/* SDRAM0_PMIT - SDRAM Power Management Idle Timer */ + +typedef union { + u32 reg; + struct { + unsigned int cnt:5; /* Cycle Count Before Sleep Request */ + unsigned int rsvd:27; + } fields; +} sdram0_pmit_t; + +#define SDRAM0_PMIT_MASK 0x07ffffff /* AND to clear all non-reserved fields */ + +/* SDRAM0_RTR - Refresh timer register */ + +typedef union { + u32 reg; + struct { + unsigned rsvd0:2; + unsigned iv:11; + unsigned rsvd1:19; + } fields; +} sdram0_rtr_t; + +#define SDRAM0_RTR_MASK 0xc007ffff /* AND to clear non-reserved fields */ + +#define SDRAM0_RTR_IV(n) (((n) & 0x3ff8) >> 2) + +/* SDRAM0_TR - SDRAM Timing Register */ + +typedef union { + u32 reg; + struct { + unsigned int rsvd0:7; + unsigned int casl:2; /* CAS Latency */ + unsigned int rsvd1:3; + unsigned int pta:2; /* Precharge-to-activate */ + unsigned int ctp:2; /* Read/Write to Precharge */ + unsigned int ldf:2; /* Command Leadoff */ + unsigned int rsvd2:9; + unsigned int rfta:3; /* Refresh-to-Activate */ + unsigned int rcd:2; /* RAS-CAS Delay */ + } fields; +} sdram0_tr_t; + +#define SDRAM0_TR_MASK 0xfe703fe0 /* AND to clear non-reserved fields */ + +#define SDRAM0_TR_ENCODE(n) ((n) - 1) +#define SDRAM0_TR_ENCODE_RFTA(n) ((n) - 4) + +/* SLA0_SLPMD - SLA Sleep Mode Control Register */ + +typedef union { + u32 reg; + struct { + unsigned slcr:5; /* Sleep Counter */ + unsigned rsvd0:5; + unsigned slen:1; /* Sleep Mode Enable */ + unsigned rsvd1:21; + } fields; +} sla0_slpmd_t; + +#define SLA0_SLPMD_MASK 0x07dfffff /* AND to clear all non-reserved fields */ + +/* these defines are for the DV bits of RTC0_CR0 */ +#define RTC_DVBITS_4MHZ 0 /* 4.194304 MHz */ +#define RTC_DVBITS_1MHZ 1 /* 1.048576 MHz */ +#define RTC_DVBITS_33KHZ 2 /* 32.768 kHz */ + +/* Several direct-write DCRs on the 405LP have an interlock requirement, + implemented by a "valid" bit in the low-order bit. This routine handles the + handshaking for these registers, by + + 1) Rewriting the current value with the valid bit clear; + 2) Rewriting the new value with the valid bit clear; + 3) Rewriting the new value with the valid bit set. + + The mask is a mask with 1s in every reserved bit position. + + NB: This routine always writes the register with the valid bit set, + regardless of the valid bit setting in the 'new' parameter. + + Unfortunately this must be a macro to work (due to mtdcr()). + + Note that for APM registers, it takes multiple RTC clock cycles for the DCR + writes to take effect. Any time delays after writes to APM are the + resonsibility of the caller. +*/ + +#define mtdcr_interlock(dcrn, new, mask) \ +do { \ + u32 __old, __new; \ + \ + __old = mfdcr(dcrn); \ + mtdcr(dcrn, __old & 0xfffffffe); \ + __new = ((__old & (mask)) | ((new) & ~(mask))) & 0xfffffffe; \ + mtdcr(dcrn, __new); \ + mtdcr(dcrn, __new | 1); \ +} while (0) + +/**************************************************************************** + * Power Managament Routines + ****************************************************************************/ + +int ibm405lp_set_pixclk(unsigned pixclk_min, unsigned pixclk_max); +int ibm405lp_setup_pccf(volatile u16 **vaddr, unsigned long *io_base, + unsigned long *mem_base); +void ibm405lp_setup_cpm(void); + + +extern int (*set_pixclk_hook) (unsigned pixclk_min, unsigned pixclk_max); +extern unsigned last_pixclk_min; +extern unsigned last_pixclk_max; + +extern long ibm405lp_time_init(void); +extern unsigned long ibm405lp_get_rtc_time(void); +extern int ibm405lp_set_rtc_time(unsigned long nowtime); +extern void ibm405lp_set_rtc_sqw(unsigned long rsbits); +extern void ibm405lp_set_rtc_alm_time(struct rtc_time *alm_time); +extern void ibm405lp_get_rtc_alm_time(struct rtc_time *alm_time); + +/* APM0 interrupt routines are in ibm405lp.c */ + +#define APM0_IRQ_MASK 0xffffffe0 /* AND to clear all non-reserved fields */ + +#define APM0_IRQ_WUI0 27 +#define APM0_IRQ_WUI1 28 +#define APM0_IRQ_WUI2 29 +#define APM0_IRQ_RTC 30 + +void ibm405lp_apm_dcr_delay(void); +#define ibm405lp_rtc_dcr_delay() ibm405lp_apm_dcr_delay(); +void ibm405lp_apm_irq_ack(unsigned apm_irq); +void ibm405lp_apm_irq_setup(unsigned apm_irq, + unsigned trigger, unsigned polarity); +void ibm405lp_apm_irq_enable(unsigned apm_irq); +void ibm405lp_apm_irq_disable(unsigned apm_irq); +int ibm405lp_apm_irq_status(unsigned apm_irq); +void ibm405lp_setup_apm_pic(void); + +#endif /* __ASSEMBLY__ */ + +#include + +#endif /* __ASM_IBM405LP_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibm405lp_pm.c linuxppc-2.4/arch/ppc/platforms/ibm405lp_pm.c --- linux-2.4.22/arch/ppc/platforms/ibm405lp_pm.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibm405lp_pm.c 2003-06-02 14:37:41.000000000 +1000 @@ -0,0 +1,1234 @@ +/* ibm405lp_pm.c: Power management for the 405LP + * + * Copyright (C) 2002, 2003 Bishop Brock, Hollis Blanchard, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + **************************************************************************** + * + * Please see/update documentation in Documentation/powerpc/405LP-sleep.txt + */ + +#include +#include +#include + +#include /* printk */ +#include /* isdigit */ + +#include /* UART_LCR, etc */ +#include +#include /* ctl_table */ +#include /* udelay */ + +#include /* Task queue stuff */ + +#include /* get_user */ +#include /* mtdcr */ +#include /* get_tbu/l, tb_ticks_per_jiffy */ +#include /* ppc_md */ +#include /* ptep stuff */ +#include /* [dis|en]able_irq() */ + +#include /* mtdcr_interlock */ +#include /* struct wakeup_info */ + + +#define WATCHDOG_CYCLES 64 /* number of RTC SQW cycles */ +#define PREFIX "405LP APM: " /* for printk */ + +/* eventually disable MSR:EE and MSR:CE . Currently CE is 0 kernel-wide. */ +#define save_and_crit_cli save_and_cli + +/* BIN_TO_BCD is stupid; can only use it on variables */ +#define BIN2BCD(val) ((((val)/10)<<4) + (val)%10) +#define BCD2BIN(val) (((val)&15) + ((val)>>4)*10) + +/* inspiration from arch/arm/mach-sa1100/pm.c */ +#define SPR_SAVE(array, reg) \ + array[SLEEP_SAVE_##reg] = mfspr(SPRN_##reg) +#define SPR_RESTORE(array, reg) \ + mtspr(SPRN_##reg, array[SLEEP_SAVE_##reg]) +#define DCR_SAVE(array, reg) \ + array[SLEEP_SAVE_##reg] = mfdcr(DCRN_##reg) +#define DCR_RESTORE(array, reg) \ + mtdcr(DCRN_##reg, array[SLEEP_SAVE_##reg]) +#define DCRI_SAVE(array, base, reg) \ + array[SLEEP_SAVE_##base##_##reg] = mfdcri(DCRN_##base, reg) +#define DCRI_RESTORE(array, base, reg) \ + mtdcri(DCRN_##base, reg, array[SLEEP_SAVE_##base##_##reg]) + +enum apm_sleep_type { + clocksuspend, + powerdown, + suspend, + hibernate, + standby, + failsafe_powerdown, +}; + +const char *sleep_type_name[] = { + [clocksuspend] = "clock-suspend", + [powerdown] = "power-down", + [suspend] = "suspend", + [hibernate] = "hibernate", + [standby] = "standby", + [failsafe_powerdown] = "failsafe-power-down", +}; + +/* from head_4xx.S */ +extern unsigned long ibm405lp_wakeup_info; + +/* from ibm405lp_pmasm.S */ +unsigned long ibm405lp_asm_suspend(unsigned long apm0_cfg, + unsigned long sleep_delay, + struct ibm405lp_wakeup_info *wakeup_info); +unsigned long ibm405lp_pm_command(unsigned long apm0_cfg, + unsigned long sleep_delay); +void do_rw_debug(void); +void do_rw_debug2(void); + +#ifdef CONFIG_405LP_PM_BUTTON +static void pm_button_handler(unsigned long mode); +static void __init pm_button_init(void); +#else +static inline void pm_button_handler(unsigned long mode) {} +static inline void pm_button_init(void) {} +#endif /* CONFIG_405LP_PM_BUTTON */ + +static int rtc_freq_hz; /* RTC input clock, in Hz */ +static int cdiv; +static enum apm_sleep_type sleep_mode = suspend; +static int attempts; /* debugging */ +static int watchdog = 1; + +enum { + /* SPRs */ + SLEEP_SAVE_DAC1, + SLEEP_SAVE_DAC2, + SLEEP_SAVE_DBCR0, + SLEEP_SAVE_DBCR1, + SLEEP_SAVE_DCWR, + SLEEP_SAVE_DVC1, + SLEEP_SAVE_DVC2, + SLEEP_SAVE_EVPR, + SLEEP_SAVE_IAC1, + SLEEP_SAVE_IAC2, + SLEEP_SAVE_IAC3, + SLEEP_SAVE_IAC4, + SLEEP_SAVE_SGR, + SLEEP_SAVE_SLER, + SLEEP_SAVE_SPRG0, + SLEEP_SAVE_SPRG1, + SLEEP_SAVE_SPRG2, + SLEEP_SAVE_SPRG3, + SLEEP_SAVE_SPRG4, + SLEEP_SAVE_SPRG5, + SLEEP_SAVE_SPRG6, + SLEEP_SAVE_SPRG7, + SLEEP_SAVE_SRR0, + SLEEP_SAVE_SRR1, + SLEEP_SAVE_SRR2, + SLEEP_SAVE_SRR3, + SLEEP_SAVE_SU0R, + + /* DCRs */ + SLEEP_SAVE_PLB0_ACR, + SLEEP_SAVE_CPC0_PLBAPR, + SLEEP_SAVE_CPMER, + SLEEP_SAVE_CPMFR, + SLEEP_SAVE_UIC0_ER, + SLEEP_SAVE_UIC0_CR, + SLEEP_SAVE_UIC0_PR, + SLEEP_SAVE_UIC0_TR, + + SLEEP_SAVE_EBC0_B0AP, + SLEEP_SAVE_EBC0_B1AP, + SLEEP_SAVE_EBC0_B2AP, + SLEEP_SAVE_EBC0_B3AP, + SLEEP_SAVE_EBC0_B4AP, + SLEEP_SAVE_EBC0_B5AP, + SLEEP_SAVE_EBC0_B6AP, + SLEEP_SAVE_EBC0_B7AP, + SLEEP_SAVE_EBC0_B0CR, + SLEEP_SAVE_EBC0_B1CR, + SLEEP_SAVE_EBC0_B2CR, + SLEEP_SAVE_EBC0_B3CR, + SLEEP_SAVE_EBC0_B4CR, + SLEEP_SAVE_EBC0_B5CR, + SLEEP_SAVE_EBC0_B6CR, + SLEEP_SAVE_EBC0_B7CR, + SLEEP_SAVE_EBC0_CFG, + + SLEEP_SAVE_RTC0_CR1, +}; + +#ifdef CONFIG_SERIAL_CONSOLE + +#if defined(CONFIG_UART0_TTYS0) +#define CONSOLE_IO_BASE UART0_IO_BASE +#elif defined(CONFIG_UART0_TTYS1) +#define CONSOLE_IO_BASE UART1_IO_BASE +#else +#error "Unexpected configuration" +#endif /* CONFIG_UART0_TTYS0 */ + +/* A Hack to save/restore the UART state of the serial console device until(?) + we have some kind of ordering mechanism for device save/restore. The serial + console must be saved/restored last/first to avoid losing messages during + the suspend/resume process. */ + +enum { + Ulcr, Umcr, Uscr, Uier, Udll, Udlm +}; + +static u8 Uregs[6]; +static void *Ubase; + +static void suspend_console(void) +{ + Ubase = ioremap((long)CONSOLE_IO_BASE, 8); + + if (Ubase) { + Uregs[Ulcr] = readb(Ubase+3); + writeb(Uregs[Ulcr] & 0x7f, Ubase+3); /* DLAB=0 */ + Uregs[Umcr] = readb(Ubase+4); + Uregs[Uscr] = readb(Ubase+7); + Uregs[Uier] = readb(Ubase+1); + writeb(Uregs[Ulcr] | 0x80, Ubase+3); /* DLAB=1 */ + Uregs[Udll] = readb(Ubase+0); + Uregs[Udlm] = readb(Ubase+1); + writeb(Uregs[Ulcr], Ubase+3); /* DLAB=orig */ + } else { + printk(KERN_ERR "save_console: ioremap failed\n"); + } +} + +static void resume_console(void) +{ + if (Ubase) { + writeb(Uregs[Ulcr] & 0x7f, Ubase+3); /* DLAB=0 */ + writeb(Uregs[Umcr], Ubase+4); + writeb(Uregs[Uscr], Ubase+7); + writeb(Uregs[Uier], Ubase+1); + writeb(0x07, Ubase+2); + writeb(0x00, Ubase+2); + (void)readb(Ubase+0); + writeb(0x81, Ubase+2); + writeb(Uregs[Ulcr] | 0x80, Ubase+3); /* DLAB=1 */ + writeb(Uregs[Udll], Ubase+0); + writeb(Uregs[Udlm], Ubase+1); + writeb(Uregs[Ulcr], Ubase+3); /* DLAB=orig */ + iounmap(Ubase); + Ubase = NULL; + } +} +#else /* CONFIG_SERIAL_CONSOLE */ + +#define suspend_console() do {} while (0) +#define resume_console() do {} while (0) + +#endif /* CONFIG_SERIAL_CONSOLE */ + +/* The assembly code that puts us to sleep needs to spin for at least 5 + (divided) RTC cycles after issuing the final APM0_CFG command in order to + know that it has taken effect. */ + +unsigned +sleep_delay(apm0_cfg_t cfg) +{ + unsigned loops; + unsigned cdiv_adj = ((cfg.fields.cdiv == 0) ? 1 : cfg.fields.cdiv * 2); + + loops = (5 * loops_per_jiffy * HZ * cdiv_adj) / rtc_freq_hz; + return loops; +} + +static void +save_sprs(unsigned long *sleep_save) +{ + SPR_SAVE(sleep_save, DAC1); + SPR_SAVE(sleep_save, DAC2); + SPR_SAVE(sleep_save, DBCR0); + SPR_SAVE(sleep_save, DBCR1); + SPR_SAVE(sleep_save, DCWR); + SPR_SAVE(sleep_save, DVC1); + SPR_SAVE(sleep_save, DVC2); + SPR_SAVE(sleep_save, IAC1); + SPR_SAVE(sleep_save, IAC2); + SPR_SAVE(sleep_save, IAC3); + SPR_SAVE(sleep_save, IAC4); + SPR_SAVE(sleep_save, SGR); + SPR_SAVE(sleep_save, SLER); + SPR_SAVE(sleep_save, SPRG0); + SPR_SAVE(sleep_save, SPRG1); + SPR_SAVE(sleep_save, SPRG2); + SPR_SAVE(sleep_save, SPRG3); + SPR_SAVE(sleep_save, SPRG4); + SPR_SAVE(sleep_save, SPRG5); + SPR_SAVE(sleep_save, SPRG6); + SPR_SAVE(sleep_save, SPRG7); + SPR_SAVE(sleep_save, SRR0); + SPR_SAVE(sleep_save, SRR1); + SPR_SAVE(sleep_save, SRR2); + SPR_SAVE(sleep_save, SRR3); + SPR_SAVE(sleep_save, SU0R); +} + +static void +restore_sprs(unsigned long *sleep_save) +{ + SPR_RESTORE(sleep_save, DAC1); + SPR_RESTORE(sleep_save, DAC2); + SPR_RESTORE(sleep_save, DBCR0); + SPR_RESTORE(sleep_save, DBCR1); + SPR_RESTORE(sleep_save, DCWR); + SPR_RESTORE(sleep_save, DVC1); + SPR_RESTORE(sleep_save, DVC2); + SPR_RESTORE(sleep_save, IAC1); + SPR_RESTORE(sleep_save, IAC2); + SPR_RESTORE(sleep_save, IAC3); + SPR_RESTORE(sleep_save, IAC4); + SPR_RESTORE(sleep_save, SGR); + SPR_RESTORE(sleep_save, SLER); + SPR_RESTORE(sleep_save, SPRG0); + SPR_RESTORE(sleep_save, SPRG1); + SPR_RESTORE(sleep_save, SPRG2); + SPR_RESTORE(sleep_save, SPRG3); + SPR_RESTORE(sleep_save, SPRG4); + SPR_RESTORE(sleep_save, SPRG5); + SPR_RESTORE(sleep_save, SPRG6); + SPR_RESTORE(sleep_save, SPRG7); + SPR_RESTORE(sleep_save, SRR0); + SPR_RESTORE(sleep_save, SRR1); + SPR_RESTORE(sleep_save, SRR2); + SPR_RESTORE(sleep_save, SRR3); + SPR_RESTORE(sleep_save, SU0R); +} + +static void +save_dcrs(unsigned long *sleep_save) +{ + DCRI_SAVE(sleep_save, EBC0, B0AP); + DCRI_SAVE(sleep_save, EBC0, B1AP); + DCRI_SAVE(sleep_save, EBC0, B2AP); + DCRI_SAVE(sleep_save, EBC0, B3AP); + DCRI_SAVE(sleep_save, EBC0, B4AP); + DCRI_SAVE(sleep_save, EBC0, B5AP); + DCRI_SAVE(sleep_save, EBC0, B6AP); + DCRI_SAVE(sleep_save, EBC0, B7AP); + DCRI_SAVE(sleep_save, EBC0, B0CR); + DCRI_SAVE(sleep_save, EBC0, B1CR); + DCRI_SAVE(sleep_save, EBC0, B2CR); + DCRI_SAVE(sleep_save, EBC0, B3CR); + DCRI_SAVE(sleep_save, EBC0, B4CR); + DCRI_SAVE(sleep_save, EBC0, B5CR); + DCRI_SAVE(sleep_save, EBC0, B6CR); + DCRI_SAVE(sleep_save, EBC0, B7CR); + DCRI_SAVE(sleep_save, EBC0, CFG); + + DCR_SAVE(sleep_save, PLB0_ACR); + DCR_SAVE(sleep_save, CPC0_PLBAPR); + DCR_SAVE(sleep_save, CPMER); + DCR_SAVE(sleep_save, CPMFR); + DCR_SAVE(sleep_save, UIC0_ER); + DCR_SAVE(sleep_save, UIC0_CR); + DCR_SAVE(sleep_save, UIC0_PR); + DCR_SAVE(sleep_save, UIC0_TR); + + DCR_SAVE(sleep_save, RTC0_CR1); +} + +static void +restore_dcrs(unsigned long *sleep_save) +{ + DCRI_RESTORE(sleep_save, EBC0, B0AP); + DCRI_RESTORE(sleep_save, EBC0, B1AP); + DCRI_RESTORE(sleep_save, EBC0, B2AP); + DCRI_RESTORE(sleep_save, EBC0, B3AP); + DCRI_RESTORE(sleep_save, EBC0, B4AP); + DCRI_RESTORE(sleep_save, EBC0, B5AP); + DCRI_RESTORE(sleep_save, EBC0, B6AP); + DCRI_RESTORE(sleep_save, EBC0, B7AP); + DCRI_RESTORE(sleep_save, EBC0, B0CR); + DCRI_RESTORE(sleep_save, EBC0, B1CR); + DCRI_RESTORE(sleep_save, EBC0, B2CR); + DCRI_RESTORE(sleep_save, EBC0, B3CR); + DCRI_RESTORE(sleep_save, EBC0, B4CR); + DCRI_RESTORE(sleep_save, EBC0, B5CR); + DCRI_RESTORE(sleep_save, EBC0, B6CR); + DCRI_RESTORE(sleep_save, EBC0, B7CR); + DCRI_RESTORE(sleep_save, EBC0, CFG); + + DCR_RESTORE(sleep_save, PLB0_ACR); + DCR_RESTORE(sleep_save, CPC0_PLBAPR); + DCR_RESTORE(sleep_save, CPMER); + DCR_RESTORE(sleep_save, CPMFR); + DCR_RESTORE(sleep_save, UIC0_ER); + DCR_RESTORE(sleep_save, UIC0_CR); + DCR_RESTORE(sleep_save, UIC0_PR); + DCR_RESTORE(sleep_save, UIC0_TR); + + DCR_RESTORE(sleep_save, RTC0_CR1); + + /* Clear all pending external interrupt status, as any edge-triggered + status is bogus. All true level interrupts, including the APM + interrupt that may have awoken the system will remain asserted. */ + + mtdcr(DCRN_UIC0_SR, 0xffffffff); + + /* Restore CPM power management to its boot-time configuration. Much + of this will be redone when the CPM registers are reloaded and + device drivers restore their states, however doing this here + guarantees that all obscure corners of the chip are properly power + managed even if they have no save/restore code. */ + + ibm405lp_setup_cpm(); +} + +/* do_suspend() + * Called for all modes except powerdown modes. The clock-suspend is special + * since it doesn't save/restore state, but it needs other things in here, + * e.g., timebase save/restore. + */ +static int do_suspend(apm0_cfg_t apm0_cfg) +{ + u64 timebase; + struct ibm405lp_wakeup_info my_wakeup_info = { + .magic = IBM405LP_WAKEUP_MAGIC, + }; + unsigned long *sleep_save; + u32 pre_rtc_time, rtc_secs_elapsed; + int ret; + + /* remember what time it is */ + timebase = ((u64)get_tbu() << 32) | get_tbl(); + pre_rtc_time = ppc_md.get_rtc_time(); + + if (apm0_cfg.fields.sm == APM0_CFG_SM_CLOCK_SUSPEND) { + ibm405lp_apm_dcr_delay(); + ret = ibm405lp_pm_command(apm0_cfg.reg, sleep_delay(apm0_cfg)); + } else { + sleep_save = (unsigned long *)__get_free_page(GFP_ATOMIC); /* irqs are disabled */ + if (sleep_save == NULL) { + printk(KERN_ERR PREFIX "__get_free_page failed!\n"); + return -ENOMEM; + } + + /* SPRs are saved/restored by software even in standby mode. + In standby mode, the states of all DCRs will be preserved, + except for APM0_CFG and the SDRAM controller registers + touched by the firmware. */ + + save_sprs(sleep_save); + if (apm0_cfg.fields.sm == APM0_CFG_SM_POWER_DOWN) { + suspend_console(); + save_dcrs(sleep_save); + } else { + BUG(); /* not currently supported */ + } + + /* the sleep call */ + ibm405lp_apm_dcr_delay(); + ret = ibm405lp_asm_suspend(apm0_cfg.reg, + sleep_delay(apm0_cfg), + &my_wakeup_info); + /* wakeup() (called by the firmware on wake) returns here */ + + restore_sprs(sleep_save); + if (apm0_cfg.fields.sm == APM0_CFG_SM_POWER_DOWN) { + restore_dcrs(sleep_save); + resume_console(); + } + + /* free our memory */ + free_page((unsigned long)sleep_save); + } + + /* Update timebase. We're stuck with 1-second accuracy from the RTC. */ + rtc_secs_elapsed = ppc_md.get_rtc_time() - pre_rtc_time; + printk(KERN_DEBUG PREFIX "%u seconds elapsed\n", rtc_secs_elapsed); + timebase += rtc_secs_elapsed * HZ * tb_ticks_per_jiffy; + set_tb(timebase >> 32, (unsigned long)timebase); + + /* ?? Should jiffies be adjusted here ?? */ + + return ret; +} + +/* approximate the time required for cryo scan out + scan in */ +static unsigned long calc_rtc_sqw(int cdiv, int rtc_freq) +{ + const int data_bits = 36400; /* bits of data transferred (max 64Kbits) */ + int iic_freq; + int transfer_ms; /* time in ms to complete transfer */ + unsigned long rtc_rs; + + iic_freq = rtc_freq / (2 * cdiv); + transfer_ms = 2 * data_bits * 1000 / iic_freq; + transfer_ms = 2 * transfer_ms; /* double it to be safe */ + rtc_rs = __ilog2((transfer_ms * 0x10000) / (WATCHDOG_CYCLES * 1000)); + + return rtc_rs; +} + +static int ibm405lp_suspend(enum apm_sleep_type mode) +{ + apm0_cfg_t apm0_cfg; + unsigned long flags; + int retval; + + printk(KERN_DEBUG PREFIX "attempt #%i\n", ++attempts); + + save_and_crit_cli(flags); /* disable critical exceptions too */ + + apm0_cfg.reg = mfdcr(DCRN_APM0_CFG); + apm0_cfg.fields.v = 0; + apm0_cfg.fields.isp = 1; + + pm_button_handler(1); + + /* magic - do not touch */ + mtdcr_interlock(DCRN_APM0_SR, 0xffffffff, 0); /* vital: leave a 1 in SR */ + ibm405lp_apm_dcr_delay(); + /* end magic */ + + printk(KERN_DEBUG PREFIX "IER = 0x%.8x\n", mfdcr(DCRN_APM0_IER)); + printk(KERN_DEBUG PREFIX "ISR = 0x%.8x\n", mfdcr(DCRN_APM0_ISR)); + + switch (mode) { + case clocksuspend: + apm0_cfg.fields.sm = APM0_CFG_SM_CLOCK_SUSPEND; + printk(KERN_DEBUG PREFIX "clocksuspend, CFG = 0x%.8x\n", + apm0_cfg.reg); + retval = do_suspend(apm0_cfg); + break; + + case failsafe_powerdown: + mtdcr_interlock(DCRN_APM0_IER, 0, APM0_IRQ_MASK); + case powerdown: + if (!(ppc_md.power_off)) { + printk(KERN_ERR PREFIX + "I don't know how to power off this" + "device - aborting"); + retval = -ENODEV; + } else { + apm0_cfg.fields.sm = APM0_CFG_SM_POWER_DOWN; + printk(KERN_DEBUG PREFIX + "%s, CFG = 0x%.8x\n", + sleep_type_name[mode], + apm0_cfg.reg); + mtdcr(DCRN_RTC0_CEN,mfdcr(DCRN_RTC0_CEN) | IBM405LP_POWERDOWN_REBOOT); + ibm405lp_apm_dcr_delay(); + ppc_md.power_off(); + } + break; + + case suspend: + apm0_cfg.fields.sm = APM0_CFG_SM_POWER_DOWN; + printk(KERN_DEBUG PREFIX "suspend, CFG = 0x%.8x\n", apm0_cfg.reg); + mtdcr(DCRN_RTC0_CEN,mfdcr(DCRN_RTC0_CEN) | IBM405LP_POWERDOWN_SUSPEND); + retval = do_suspend(apm0_cfg); + mtdcr(DCRN_RTC0_CEN,mfdcr(DCRN_RTC0_CEN) & ~IBM405LP_POWERDOWN_SUSPEND); + break; + + case hibernate: + apm0_cfg.fields.sm = APM0_CFG_SM_POWER_DOWN; + printk(KERN_DEBUG PREFIX "hibernate, CFG = 0x%.8x\n", apm0_cfg.reg); + mtdcr(DCRN_RTC0_CEN,mfdcr(DCRN_RTC0_CEN) | IBM405LP_POWERDOWN_HIBERNATE); + retval = do_suspend(apm0_cfg); + mtdcr(DCRN_RTC0_CEN,mfdcr(DCRN_RTC0_CEN) & ~IBM405LP_POWERDOWN_HIBERNATE); + break; + + case standby: + apm0_cfg.fields.sm = APM0_CFG_SM_CRYO_STANDBY; + apm0_cfg.fields.rst = 1; + + /* set cdiv such that I2C freq is ~100 KHz */ + if (cdiv == 0) { + if (rtc_freq_hz < 100000) { + apm0_cfg.fields.cdiv = 0; + } else { + apm0_cfg.fields.cdiv = rtc_freq_hz / (100000 * 2); + apm0_cfg.fields.cdiv++; /* round up to be safe */ + } + } else { + /* user override */ + apm0_cfg.fields.cdiv = cdiv; + } + + /* enable cryo watchdog if user requested it */ + if (watchdog) { + ibm405lp_set_rtc_sqw(calc_rtc_sqw(cdiv, rtc_freq_hz)); + apm0_cfg.fields.ewt = 1; + } + + printk(KERN_DEBUG PREFIX "standby, CFG = 0x%.8x\n", apm0_cfg.reg); + retval = do_suspend(apm0_cfg); + break; + } + + printk(KERN_DEBUG PREFIX "IER = 0x%.8x\n", mfdcr(DCRN_APM0_IER)); + printk(KERN_DEBUG PREFIX "ISR = 0x%.8x\n", mfdcr(DCRN_APM0_ISR)); + + pm_button_handler(2); + + restore_flags(flags); + + return retval; /* success */ +} + +/* Device suspensions/resumptions are done here with interrupts enabled (?), + before moving to the hard-core suspension code in ibm405lp_suspend. */ + +static int suspend_handler(enum apm_sleep_type mode) +{ + apm0_sr_t sr; + int retval; + + /* If the APM unit is disabled any suspend commands will be ignored. + This is a hard disable that is strapped at APM reset - there is no + way for software to re-enable it. */ + + sr.reg = mfdcr(DCRN_APM0_SR); + if (!sr.fields.en) { + printk(KERN_ERR PREFIX + "The 405LP APM unit is hardware-disabled, " + "therefore all suspend commands are ignored\n"); + return -ENODEV; + } + + switch (sleep_mode) { + + case powerdown: + case failsafe_powerdown: + /* No need to suspend anything, we're rebooting. */ + sr.reg = ibm405lp_suspend(mode); + break; + + case standby: + + printk(KERN_WARNING PREFIX + "No 'standby' operating point is " + "defined for this platform.\n"); + printk(KERN_WARNING PREFIX + "Using 'clock-suspend' mode instead of " + "'standby'.\n"); + mode = clocksuspend; + + case clocksuspend: + /* FIX ME: These should have a quickie clock-stop suspend, but + for now fall through and do the full suspend. */ + + case suspend: + case hibernate: + /* Suspend all state to RAM and prepare for system + power-down. */ + + /* suspend devices here */ + sr.reg = ibm405lp_suspend(mode); + /* resume devices here */ + break; + } + printk(KERN_DEBUG "APM0_SR = 0x%.8x at resume\n", sr.reg); + return 0; /* success */ +} + + +static int suspend_proc_handler(ctl_table *ctl, int write, struct file * filp, + void *buffer, size_t *lenp) +{ + return suspend_handler(sleep_mode); +} + +/* whose BRILLIANT idea was it to have the sysctl "strategy" routine NOT + * called when accessed via /proc ? That renders it useless AFAICS. + * + * allow only valid modes to be written + */ +static int parse_mode(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + size_t len; + + if (!table->maxlen || !*lenp || (filp->f_pos && !write)) { + *lenp = 0; + return 0; + } + + if (write) { + char string[table->maxlen]; + int i; + + len = *lenp; + if (len > table->maxlen) + len = table->maxlen; + + if (copy_from_user(string, buffer, len)) + return -EFAULT; + + for (i = 0; i < table->maxlen; i++) { + /* Chop the string off at first nul or \n */ + if ( (i < len) && + ( (string[i] == '\n') || (string[i] == '\0') ) ) + len = i; + + /* Clear the tail of the buffer */ + if (i >= len) + string[i] = '\0'; + } + + for (i = 0; i < ARRAY_SIZE(sleep_type_name); i++) { + const char *name = sleep_type_name[i]; + + if (name && (strncmp(string, name, len) == 0)) { + sleep_mode = i; + filp->f_pos += *lenp; + return 0; + } + } + + /* Didn't find it */ + return -EINVAL; + } else { + const char *name = sleep_type_name[sleep_mode]; + + len = strlen(name); + + if (len > table->maxlen) + len = table->maxlen; + if (len > *lenp) + len = *lenp; + if (len > 0) + if (copy_to_user(buffer, name, len)) + return -EFAULT; + if (len < *lenp) { + if (put_user('\n', ((char *) buffer) + len)) + return -EFAULT; + len++; + } + *lenp = len; + filp->f_pos += len; + } + + return 0; +} + +/* Implementation of the power management alarm function. This implementation + assumes that the PM alarm is the only RTC interrupt in use. Global interrupt + disable is not used due to the long latencies of APM/RTC register + manipulations. Interrupt latencies for RTC and wakeup interrupts will be in + the 100s of uS. */ + +enum alarm_status { clear, armed, expired } volatile alarm_status; + +static spinlock_t pm_alarm_lock = SPIN_LOCK_UNLOCKED; + +static void +_clear_alarm(enum alarm_status status) +{ + mtdcr(DCRN_RTC0_CR1, mfdcr(DCRN_RTC0_CR1) & ~RTC_AIE); + ibm405lp_rtc_dcr_delay(); + mfdcr(DCRN_RTC0_CR2); /* Clear status */ + ibm405lp_apm_irq_ack(APM0_IRQ_RTC); + ibm405lp_apm_irq_disable(APM0_IRQ_RTC); + alarm_status = status; +} + +static void +clear_alarm(enum alarm_status status) +{ + spin_lock(&pm_alarm_lock); + disable_irq(UIC_IRQ_APM); + + _clear_alarm(status); + + enable_irq(UIC_IRQ_APM); + spin_unlock(&pm_alarm_lock); +} + +static void +set_alarm(struct rtc_time *t) +{ + spin_lock(&pm_alarm_lock); + disable_irq(UIC_IRQ_APM); + + ibm405lp_set_rtc_alm_time(t); + mtdcr(DCRN_RTC0_CR1, mfdcr(DCRN_RTC0_CR1) | RTC_AIE); + ibm405lp_rtc_dcr_delay(); + mfdcr(DCRN_RTC0_CR2); /* Clear status */ + ibm405lp_rtc_dcr_delay(); + ibm405lp_apm_irq_enable(APM0_IRQ_RTC); + ibm405lp_apm_irq_ack(APM0_IRQ_RTC); + alarm_status = armed; + + enable_irq(UIC_IRQ_APM); + spin_unlock(&pm_alarm_lock); +} + +static void +pm_alarm_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + if (ibm405lp_apm_irq_status(APM0_IRQ_RTC)) { + _clear_alarm(expired); + printk(PREFIX + "RTC alarm interrupt " + "[ISR: 0x%02x IER: 0x%02x].\n", + mfdcr(DCRN_APM0_ISR), mfdcr(DCRN_APM0_IER)); + } +} + +static int +alarm_status_string(char *s, int len) +{ + struct rtc_time t; + + if (len < 17) + return 0; + + spin_lock_irq(&pm_alarm_lock); + ibm405lp_get_rtc_alm_time(&t); + spin_unlock_irq(&pm_alarm_lock); + + return sprintf(s, "%02d:%02d:%02d %s\n", + (t.tm_hour < 24) ? t.tm_hour : 99, + (t.tm_min < 60) ? t.tm_min : 99, + (t.tm_sec < 60) ? t.tm_sec : 99, + (alarm_status == armed) ? "armed" : + (alarm_status == expired) ? "expired" : + "clear"); + +} + +static int pm_alarm_handler(ctl_table *table, int write, struct file *filp, + void *buffer, size_t *lenp) +{ + size_t len; + char *s, *tok, *whitespace = " \t\r\n"; + struct rtc_time alm_time; + + if (!table->maxlen || !*lenp || (filp->f_pos && !write)) { + *lenp = 0; + return 0; + } + + if (write) { + char string[table->maxlen]; + + len = *lenp; + if (len >= table->maxlen) + len = table->maxlen - 1; + + if (copy_from_user(string, buffer, len)) + return -EFAULT; + string[len] = '\0'; + + s = string + strspn(string, whitespace); + tok = strsep(&s, whitespace); + + if (strcmp(tok, "clear") == 0) { + clear_alarm(clear); + return 0; + } else if ((strlen(tok) == 8) && + (tok[2] == ':') && (tok[5] == ':')) { + s = tok; + tok = strsep(&s, ":"); + alm_time.tm_hour = simple_strtoul(tok, NULL, 10); + tok = strsep(&s, ":"); + alm_time.tm_min = simple_strtoul(tok, NULL, 10); + alm_time.tm_sec = simple_strtoul(s, NULL, 10); + set_alarm(&alm_time); + return 0; + } else { + return -EINVAL; + } + } else { + char string[table->maxlen]; + + len = alarm_status_string(string, table->maxlen); + + if (len > *lenp) + len = *lenp; + if (len > 0) + if (copy_to_user(buffer, string, len)) + return -EFAULT; + *lenp = len; + filp->f_pos += len; + + return 0; + } +} + +static struct ctl_table pm_table[] = +{ + { + .ctl_name = PM_405LP_SLEEP_CMD, + .procname = "suspend", + .mode = 0200, /* write-only to trigger sleep */ + .proc_handler = &suspend_proc_handler, + }, + { + .ctl_name = PM_405LP_SLEEP_MODE, + .procname = "mode", + .maxlen = 16, + .mode = 0644, + .proc_handler = &parse_mode, + }, + { + .ctl_name = PM_405LP_SLEEP_ALARM, + .procname = "pm_alarm", + .maxlen = 18, + .mode = 0644, + .proc_handler = &pm_alarm_handler, + }, + { + .ctl_name = PM_405LP_SLEEP_DEBUG_CDIV, + .procname = "cdiv", + .data = &cdiv, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + { + .ctl_name = PM_405LP_SLEEP_DEBUG_WATCHDOG, + .procname = "watchdog", + .data = &watchdog, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec, + }, + {0} +}; + +static struct ctl_table pm_dir_table[] = +{ + { + .ctl_name = CTL_PM_405LP, + .procname = "sleep", + .mode = 0555, + .child = pm_table, + }, + {0} +}; + +#ifdef CONFIG_405LP_PM_BUTTON + +/**************************************************************************** + * APM Power Button + *****************************************************************************/ + +/* Implementation of a generic, kernel-level power button driver for a button + attached to the 405LP APM unit. Pressing and releasing the button will + cause the system to suspend, using the current 'sleep_mode'. Pressing the + button on a sleeping system will awaken it. If the button is pressed + continuously for 5 seconds, the system will enter a failsafe_powerdown state + with the APM wakeup mechanism disabled. This will require an APM reset to + restart the system. The button works correctly in conjunction with all + other mechanisms that can put the device to sleep and wake it back up. */ + +#define BTN_MAX (5 * HZ) /* After 5 sec. press, force powerdown */ +#define BTN_DELAY (HZ / 10) /* Poll 10 times per second to debounce */ + +enum pm_button_state { running, pressing, press_debouncing, initializing, + init_debouncing, failsafe, suspended }; + +static struct pm_button_struct { + enum pm_button_state state; /* State of button */ + unsigned start; /* Start of press in jiffies */ + struct timer_list timer; /* For debounce/advanced function */ +} pm_button; + +static struct tq_struct pm_button_task; + +static void +button_suspend(void *data __attribute__((unused))) +{ + suspend_handler(sleep_mode); +} + +static void +button_powerdown(void *data __attribute__((unused))) +{ + suspend_handler(failsafe_powerdown); +} + +static void +pm_button_timer(void) +{ + pm_button.timer.expires = jiffies + BTN_DELAY; + add_timer(&pm_button.timer); +} + +/* This routine is written this way to work around limitations in the + specification of the APM interrupt controller. The only way to observe the + state of the wakeup pin is to generate status, and the only way to see the + status is to enable the interrupt. This routine is always called with the + IBM405LP_PM_IRQ disabled [ an invariant of the pm_button_handler() ], and + quickly manipulates the enabled status of the IRQ to check on the status. + Note that this may also generate status in the UIC. However, since the IRQ + is disabled in the APM controller when the pm_button_irq() is later invoked, + this bogus UIC status will have no effect other than a little overhead. */ + +static int +pm_button_get_status_and_ack(void) +{ + int status; + + disable_irq(UIC_IRQ_APM); + ibm405lp_apm_irq_enable(IBM405LP_PM_IRQ); + status = ibm405lp_apm_irq_status(IBM405LP_PM_IRQ); + ibm405lp_apm_irq_disable(IBM405LP_PM_IRQ); + if (status) + ibm405lp_apm_irq_ack(IBM405LP_PM_IRQ); + enable_irq(UIC_IRQ_APM); + return status; +} + +static void +pm_button_handler(unsigned long mode) +{ + switch (mode) { + + case 0: + + /* Called from initialization, IRQ handler and timer to handle + button pressing/debouncing. */ + + switch (pm_button.state) { + + case running: + + /* The button is pressed for the first time in a + running system. */ + + pm_button.state = pressing; + pm_button.start = jiffies; + ibm405lp_apm_irq_disable(IBM405LP_PM_IRQ); + + /* Fall through */ + + case pressing: + case press_debouncing: + + /* The user is pressing the button to suspend the + system. */ + + if (pm_button_get_status_and_ack()) { + + if ((jiffies - pm_button.start) > BTN_MAX) { + + /* Failsafe powerdown. The APM IRQ will _not_ + be re-enabled. */ + + printk(KERN_CRIT PREFIX + "pm_button: Forcing fail-safe " + "power down\n"); + pm_button.state = failsafe; + INIT_TQUEUE(&pm_button_task, + button_powerdown, 0); + schedule_task(&pm_button_task); + break; + } else { + pm_button.state = pressing; + } + } else if (pm_button.state == press_debouncing) { + printk(PREFIX "pm_button: suspending\n"); + pm_button.state = suspended; + INIT_TQUEUE(&pm_button_task, + button_suspend, 0); + schedule_task(&pm_button_task); + break; + } else { + pm_button.state = press_debouncing; + } + printk(PREFIX "pm_button: Polling\n"); + pm_button_timer(); + break; + + case suspended: + + /* Resume from suspend */ + + ibm405lp_apm_irq_disable(IBM405LP_PM_IRQ); + pm_button.state = initializing; + + /* Keep goin' */ + + case initializing: + case init_debouncing: + + /* Driver initialization, and resume from suspend. The + APM0_IER bit for this PM IRQ is disabled here. The + user must let go of the button before we activate + the IRQ (again). */ + + if (pm_button_get_status_and_ack()) { + pm_button.state = initializing; + } else if (pm_button.state == init_debouncing) { + pm_button.state = running; + ibm405lp_apm_irq_enable(IBM405LP_PM_IRQ); + break; + } else { + pm_button.state = init_debouncing; + } + printk(PREFIX "Please let go of the button!\n"); + pm_button_timer(); + break; + + case failsafe: + + /* This is a bug */ + break; + } + return; + + case 1: + + /* Called from ibm405lp_suspend() with interrupts and the PM + IRQ disabled, prior to suspend. Arm the button for + wakeup. */ + + switch (pm_button.state) { + default: /* Many bizarre cases can cause this, e.g. /proc + suspend while pressing the button. */ + printk(KERN_WARNING PREFIX + "Unusual condition encountered by " + " pm_button_handler(1). State = [%d].\n", + pm_button.state); + case suspended: /* Suspended from button press */ + case running: /* Suspended from /proc interface */ + ibm405lp_apm_irq_enable(IBM405LP_PM_IRQ); + case failsafe: /* Force powerdown without APM wakeup */ + pm_button.state = suspended; + break; + } + del_timer_sync(&pm_button.timer); + return; + + case 2: + + /* Called from ibm405lp_suspend() with interrupts disabled upon + resume. If the power button did not cause the wakeup (maybe + it was RTC or another wakeup), set the state to 'running', + which arms the button to suspend the machine again. */ + + if (!ibm405lp_apm_irq_status(IBM405LP_PM_IRQ)) + pm_button.state = running; + return; + } +} + +static void +pm_button_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + if (ibm405lp_apm_irq_status(IBM405LP_PM_IRQ)) { + if (pm_button.state == suspended) + printk(PREFIX "pm_button: Resume from suspend\n"); + else if (pm_button.state != running) + printk(KERN_CRIT PREFIX + "Bug detected by pm_button_irq() [%d].\n", + pm_button.state + /* But let's not crash the system, maybe + everything will work out OK :-) */); + pm_button_handler(0); + } +} + +static void __init +pm_button_init(void) +{ + int err; + + printk(PREFIX "IBM405LP PM Button Handler\n"); + + ibm405lp_apm_irq_disable(IBM405LP_PM_IRQ); + ibm405lp_apm_irq_setup(IBM405LP_PM_IRQ, 0, IBM405LP_PM_POLARITY); + + if ((err = request_irq(UIC_IRQ_APM, pm_button_irq, SA_SHIRQ, + "ibm405lp_pm_button", NULL))) { + printk(KERN_ERR PREFIX + "Request power button IRQ %d failed (%d)\n", + UIC_IRQ_APM, err); + return; + } + + pm_button.state = initializing; + init_timer(&pm_button.timer); + pm_button.timer.function = pm_button_handler; + pm_button_handler(0); +} +#endif /* CONFIG_405LP_PM_BUTTON */ + +/* + * Initialize power interface + */ +static int __init ibm405lp_pm_init(void) +{ + apm0_cfg_t apm0_cfg; + int err; + + /* Since CDIV determines the speed of the entire APM unit, the + * delay required after an mtdcr (before it takes effect) is + * dependent on it. We don't want to have to wait very long + * at all, so we *always* zero CDIV (causing the APM to run at + * RTC frequency), both at boot and in wakeup code. + */ + apm0_cfg.reg = mfdcr(DCRN_APM0_CFG) & 0xfffffffe; + apm0_cfg.fields.cdiv = 0; + mtdcr_interlock(DCRN_APM0_CFG, apm0_cfg.reg, 0xfffc0000); + + /* APM0_ID wasn't updated from 405LP 1.0 -> 1.1, so is + * probably useless (use PVR instead). OTOH it would be nice + * if it was used. :( + */ + printk(KERN_INFO PREFIX "APM version %x\n", mfdcr(DCRN_APM0_ID)); + + /* we need to know the RTC frequency in a few places, so read + * it here */ + switch ((mfdcr(DCRN_RTC0_CR0) >> 4) & 0x7) { + /* check DV0, DV1, DV3 */ + case RTC_DVBITS_4MHZ: + rtc_freq_hz = 4194304; + break; + case RTC_DVBITS_33KHZ: + rtc_freq_hz = 32768; + break; + default: + printk(KERN_ERR PREFIX + "unknown RTC clock rate! defaulting to 1048576 Hz\n"); + /* fall through */ + case RTC_DVBITS_1MHZ: + rtc_freq_hz = 1048576; + break; + } + /* we need to clear the shutdown flags from the RTC Century field in + case the firmware forgot to. */ + mtdcr(DCRN_RTC0_CEN, + mfdcr(DCRN_RTC0_CEN) & ~(IBM405LP_POWERDOWN_SUSPEND || + IBM405LP_POWERDOWN_HIBERNATE || + IBM405LP_POWERDOWN_REBOOT)); + + register_sysctl_table(pm_dir_table, 1); + + pm_button_init(); + + clear_alarm(clear); + if ((err = request_irq(UIC_IRQ_APM, pm_alarm_irq, SA_SHIRQ, + "ibm405lp_pm_alarm", NULL))) { + printk(KERN_ERR PREFIX + "Request PM Alarm IRQ %d failed (%d)\n", + UIC_IRQ_APM, err); + return -ENODEV; + } + + return 0; +} + +__initcall(ibm405lp_pm_init); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibm405lp_pm.h linuxppc-2.4/arch/ppc/platforms/ibm405lp_pm.h --- linux-2.4.22/arch/ppc/platforms/ibm405lp_pm.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibm405lp_pm.h 2003-06-02 14:37:41.000000000 +1000 @@ -0,0 +1,55 @@ +#ifndef IBM405LP_PM_H +#define IBM405LP_PM_H + +#include + +#ifndef __ASSEMBLY__ + +/* sysctl numbers */ + +/* top-level number for the 405LP sleep sysctl()s + * + * This is picked at random out of thin air, hoping that it won't + * clash with someone. That's really ugly, but appears to be + * "standard" practice (!?). Oh well, with any luck we can throw + * these away and replace them with sysfs parameters, in the + * not-too-distant future... + */ +#define CTL_PM_405LP 0xbc17 + +/* sleep sysctls */ +enum +{ + PM_405LP_SLEEP_CMD=1, + PM_405LP_SLEEP_MODE=2, + PM_405LP_SLEEP_ALARM=3, + PM_405LP_SLEEP_DEBUG_CDIV=4, + PM_405LP_SLEEP_DEBUG_WATCHDOG=5 +}; + +/* Used to tell firmware where to return control to Linux on + * wake. Currently only the first two words are used by firmware; the + * rest are Linux convenience. + */ +struct ibm405lp_wakeup_info { + /* physical address of wakeup function */ + void (*wakeup_func_phys)(unsigned long apm0_cfg, + unsigned long apm0_sr); + u32 magic; + + /* private to Linux: */ + unsigned long wakeup_stack_phys; /* physical stack pointer */ +}; + +#endif /* __ASSEMBLY__ */ + +#define IBM405LP_WAKEUP_MAGIC (0x31415926) + +/* These values are ORed into RTC0_CEN before APM power-down modes as a + * signal to the firmware as to which type of wakeup is required. */ + +#define IBM405LP_POWERDOWN_REBOOT 0x00 /* Reboot the system */ +#define IBM405LP_POWERDOWN_SUSPEND 0x40 /* Suspend-to-RAM */ +#define IBM405LP_POWERDOWN_HIBERNATE 0x80 /* Hibernate to device 0 */ + +#endif /* IBM405LP_PM_H */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibm405lp_pmasm.S linuxppc-2.4/arch/ppc/platforms/ibm405lp_pmasm.S --- linux-2.4.22/arch/ppc/platforms/ibm405lp_pmasm.S 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibm405lp_pmasm.S 2003-06-02 14:37:41.000000000 +1000 @@ -0,0 +1,473 @@ +/* + * arch/ppc/platforms/ibm405lp_pmasm.S 405lp-specific assembler routines + * to support power management + * + * Copyright (C) 2002, Bishop Brock & Hollis Blanchard, IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License. + * + * Bishop Brock + * IBM Research, Austin Center for Low-Power Computing + * bcbrock@us.ibm.com + * March, 2002 + */ + +#include +#include +#include /* KERNELBASE */ +#include /* LG_L1_CACHE_LINE_SIZE */ +#include +#include +#include +#include + +#define GPR2_SAVE 0 +#define GPR13_SAVE 4 +#define GPR14_SAVE 8 +#define GPR15_SAVE 12 +#define GPR16_SAVE 16 +#define GPR17_SAVE 20 +#define GPR18_SAVE 24 +#define GPR19_SAVE 28 +#define GPR20_SAVE 32 +#define GPR21_SAVE 36 +#define GPR22_SAVE 40 +#define GPR23_SAVE 44 +#define GPR24_SAVE 48 +#define GPR25_SAVE 52 +#define GPR26_SAVE 56 +#define GPR27_SAVE 60 +#define GPR28_SAVE 64 +#define GPR29_SAVE 68 +#define GPR30_SAVE 72 +#define GPR31_SAVE 76 +#define CR_SAVE 80 +#define LR_SAVE 84 +#define SP_SAVE 88 +#define CCR0_SAVE 92 +#define TCR_SAVE 96 +#define PIT_SAVE 100 +#define MSR_SAVE 104 +#define CPC0_CR0_SAVE 108 +#define CPC0_CR1_SAVE 112 +#define ICCR_SAVE 116 +#define DCCR_SAVE 120 +#define EVPR_SAVE 124 +#define PID_SAVE 128 +#define ZPR_SAVE 132 +#define TLB_SAVE 136 /* 64 entries*3 words = 768 bytes */ +#define SLEEP_STACK_BYTES 904 + + .macro mtdcr_interlock, dcr, value, temp + + mfdcr \temp, \dcr + clrrwi \temp, \temp, 1 /* write current without valid bit */ + mtdcr \dcr, \temp + + clrrwi \temp, \value, 1 + mtdcr \dcr, \temp /* write new without valid bit */ + + ori \temp, \value, 1 + mtdcr \dcr, \temp /* write new with valid bit */ + + .endm + +/* The routines in this section execute code prefetched into the I-Cache. It + * is imperative that no TLB misses occur during the prefetching, since ICBT is + * a no-op if it would cause a TLB miss. Therefore all entry points are forced + * to occupy the same physical page. By the time the critical entry point is + * reached (ibm405lp_pm_command()) all I/D misses will have happened. + */ + + .text + .align PAGE_SHIFT + +/* ibm405lp_asm_suspend() + * r3 = command, passed to apm + * r4 = delay, passed to apm + * r5 = pointer to struct ibm405lp_wakeup_info (must save physical + * stack pointer) + * + * code specific to powerdown (rather than clock-freeze); calls 'apm' + * (below). + * + * LR, and r3/r4 are saved to r29-r31 because we call flush_dcache_all. LR + * must be preserved because if a wakeup condition (interrupt) already exists + * at the time of the sleep command, 'apm' could simply fall through and + * return here, at which point 'powerdown' would need to return to its + * caller. + */ + +_GLOBAL(ibm405lp_asm_suspend) + subi r1, r1, SLEEP_STACK_BYTES /* new stack frame */ + + /* wakeup_info->wakeup_sp_phys = phys(r1) */ + tophys(r9,r1) + stw r9, WAKEUP_SP_OFFSET(r5) + + /* wakeup_info->wakeup_func_phys = phys(wakeup) */ + lis r9, wakeup@h + ori r9, r9, wakeup@l + tophys(r9,r9) + stw r9, WAKEUP_FUNC_OFFSET(r5) + + /* save non-volatile GPRs + LR onto stack (calling C function will + * reload volatiles including CTR and XER, so we can ignore them). + * r1 (stack pointer) is handled elsewhere. + */ + stw r2, GPR2_SAVE(r1) + stw r13, GPR13_SAVE(r1) + stw r14, GPR14_SAVE(r1) + stw r15, GPR15_SAVE(r1) + stw r16, GPR16_SAVE(r1) + stw r17, GPR17_SAVE(r1) + stw r18, GPR18_SAVE(r1) + stw r19, GPR19_SAVE(r1) + stw r20, GPR20_SAVE(r1) + stw r21, GPR21_SAVE(r1) + stw r22, GPR22_SAVE(r1) + stw r23, GPR23_SAVE(r1) + stw r24, GPR24_SAVE(r1) + stw r25, GPR25_SAVE(r1) + stw r26, GPR26_SAVE(r1) + stw r27, GPR27_SAVE(r1) + stw r28, GPR28_SAVE(r1) + stw r29, GPR29_SAVE(r1) + stw r30, GPR30_SAVE(r1) + stw r31, GPR31_SAVE(r1) + + /* rescue from flush_dcache_all call below */ + mflr r29 + mr r31, r3 + mr r30, r4 + + /* save entire TLB to stack + * TLB related CPU state is saved/restored + * r9 = TLB index (+= 1 per iteration) + * r10 = save address (+= 3 per iteration) + */ + mfspr r9, SPRN_PID + stw r9, PID_SAVE(r1) + mfspr r9, SPRN_ZPR + stw r9, ZPR_SAVE(r1) + + li r9, 64 + addi r10, r1, TLB_SAVE-4 + mtctr r9 +save_tlbe: + subi r9, r9, 1 + tlbrelo r11, r9 + stwu r11, 4(r10) + tlbrehi r11, r9 + stwu r11, 4(r10) + mfspr r11, SPRN_PID + stwu r11, 4(r10) + bdnz save_tlbe + + lwz r9, PID_SAVE(r1) + mtspr SPRN_PID, r9 + isync + + /* save CR, LR, MSR, virtual SP */ + mfcr r9 + stw r9, CR_SAVE(r1) + mflr r9 + stw r9, LR_SAVE(r1) /* restored into SRR0 */ + mfmsr r9 + stw r9, MSR_SAVE(r1) /* restored into SRR1 */ + stw r1, SP_SAVE(r1) + + /* save EVPR (we need it before rfi in wakeup) */ + mfspr r9, SPRN_EVPR + stw r9, EVPR_SAVE(r1) + + /* save timer regs: CCR0, TCR, PIT, CPC0_CR1 */ + mfspr r9, SPRN_CCR0 + stw r9, CCR0_SAVE(r1) + mfspr r9, SPRN_TCR + stw r9, TCR_SAVE(r1) + mfdcr r9, DCRN_CPC0_CR0 + stw r9, CPC0_CR0_SAVE(r1) + mfdcr r9, DCRN_CPC0_CR1 + stw r9, CPC0_CR1_SAVE(r1) + mfspr r9, SPRN_PIT + stw r9, PIT_SAVE(r1) + + /* save storage attribute regs: ICCR, DCCR */ + mfspr r9, SPRN_ICCR + stw r9, ICCR_SAVE(r1) + mfspr r9, SPRN_DCCR + stw r9, DCCR_SAVE(r1) + + /* save physical struct ptr (r5) to &wakeup_info (0xfc) */ + + tophys(r10,r5) + /* disable MSR:DR */ + mfmsr r12 + rlwinm r11, r12, 0, ~MSR_DR + mtmsr r11 + /* do the write */ + lis r9, ibm405lp_wakeup_info@h + ori r9, r9, ibm405lp_wakeup_info@l + tophys(r9,r9) + stw r10, 0(r9) + /* restore MSR:DR */ + mtmsr r12 + + eieio + + /* flush dcache to RAM or we lose all that data */ + bl flush_dcache_all /* leaves low addresses in cache! */ + lis r4, ibm405lp_wakeup_info@h + ori r4, r4, ibm405lp_wakeup_info@l + dcbf r0, r4 /* flush that low-mem pointer */ + + sync /* force instruction completion */ + + /* continue on to ibm405lp_pm_command */ + mr r3, r31 + mr r4, r30 + bl ibm405lp_pm_command + + /* if we return to here, powerdown failed. try to recover. */ + + /* r29-r31 are the nonvolatiles altered above */ + mtlr r29 + lwz r29, GPR29_SAVE(r1) + lwz r30, GPR30_SAVE(r1) + lwz r31, GPR31_SAVE(r1) + addi r1, r1, SLEEP_STACK_BYTES /* surrender stack frame */ + blr + + +/* ibm405lp_pm_command(command, sleep_delay) + * + * Final shutdown (for clock-suspend, power-standby, and cryo modes). + * Interrupts must be disabled because we put SDRAM into self-refresh. + */ +_GLOBAL(ibm405lp_pm_command) + /* disable instruction prefetching to prevent PLB transactions while + * we're trying to sleep */ + mfspr r7, SPRN_CCR0 + rlwinm r9, r7, 0, 22, 19 /* mask off PFC, PFNC */ + mtspr SPRN_CCR0, r9 + + /* Touch the rest of this function into the I-cache */ + lis r9, load_icache@h + ori r9, r9, load_icache@l + li r10, ((load_icache_end-load_icache-1)>>LG_L1_CACHE_LINE_SIZE)+1 + mtctr r10 +3: + icbt r0, r9 + addi r9, r9, 32 /* next cacheline */ + bdnz 3b + sync /* force instruction completion */ + + b load_icache + + .align LG_L1_CACHE_LINE_SIZE +load_icache: + /* Start SDRAM self-refresh. + + We also set the EMDULR bit to force a valid value on the data bus, + which reduces standby leakage. Unless this bit is set here we + observe HUGE standby leakage currents. Whether or not EMDULR is set + during normal operation is a matter of PM policy. */ + + li r9, DCRN_SDRAM0_CFG + mtdcr DCRN_SDRAM0_CFGADDR, r9 + mfdcr r8, DCRN_SDRAM0_CFGDATA /* R8 = Original SDRAM0_CFG */ + oris r9, r8, 0x4020 + mtdcr DCRN_SDRAM0_CFGDATA, r9 /* Start self-refresh + EMDULR */ + + /* Poll until self-refresh status */ + li r9, DCRN_SDRAM0_STATUS + mtdcr DCRN_SDRAM0_CFGADDR, r9 +1: + mfdcr r9, DCRN_SDRAM0_CFGDATA + andis. r9, r9, 0x4000 + beq 1b + /* now in SDRAM self-refresh mode */ + +#ifdef CONFIG_ARCTIC2 + /* If this is a suspend-to-RAM mode, clamp down the CKE lines by + bringing APM0_CFG[PSC] low at power-down. */ + mfdcr r9, DCRN_RTC0_CEN + andi. r9, r9, IBM405LP_POWERDOWN_SUSPEND + beq 2f + rlwinm r3, r3, 0, 25, 23 +2: +#endif + + /* final sleep command */ + mtctr r4 + mtdcr_interlock DCRN_APM0_CFG, r3, r10 + bdnz+ . + + /* back from sleep; Clear the cryo command, disable the APM watchdog + and restore CDIV == 0 */ + rlwinm r3, r3, 0, 18, 16 /* clear ISP */ + rlwinm r3, r3, 0, 19, 17 /* clear EWT */ + rlwinm r3, r3, 0, 31, 24 /* clear CDIV */ +#ifdef CONFIG_ARCTIC2 + /* Unclamp the CKE lines by bringing APM0_CFG[PSC] high. */ + ori r3, r3, 0x0080 +#endif + mtdcr_interlock DCRN_APM0_CFG, r3, r10 + mtctr r4 + bdnz . + + /* re-enable the SDRAM */ + li r9, DCRN_SDRAM0_CFG + mtdcr DCRN_SDRAM0_CFGADDR, r9 + mtdcr DCRN_SDRAM0_CFGDATA, r8 /* remove self-refresh */ + + /* Poll until status clear */ + li r9, DCRN_SDRAM0_STATUS + mtdcr DCRN_SDRAM0_CFGADDR, r9 +3: + mfdcr r9, DCRN_SDRAM0_CFGDATA + andis. r9, r9, 0x4000 + bne 3b +load_icache_end: + /* restore CCR0 from r7 where we left it */ + mtspr SPRN_CCR0, r7 + + mfdcr r3, DCRN_APM0_SR /* return status */ + blr + +/* wakeup() + * - The registers restored here are sensitive in some way and must be + * restored in assembly. All others are saved/restored in the calling C + * code. + * - the following MSR bits are assumed disabled: ME CE EE IR DR + * Really no exceptions can be handled until this code completes... + * - the I- and D-caches are assumed to have been invalidated + */ +wakeup: + /* clear ISP, disable the APM watchdog and restore CDIV == 0 */ + mfdcr r9, DCRN_APM0_CFG + rlwinm r9, r9, 0, 18, 16 /* clear ISP */ + rlwinm r9, r9, 0, 19, 17 /* clear EWT */ + rlwinm r9, r9, 0, 31, 24 /* clear CDIV */ + mtdcr_interlock DCRN_APM0_CFG, r9, r10 + + /* load stack pointer */ + lis r9, ibm405lp_wakeup_info@h + ori r9, r9, ibm405lp_wakeup_info@l + tophys(r9,r9) /* r9 = &ibm405lp_wakeup_info */ + lwz r10, 0(r9) /* r10 = *ibm405lp_wakeup_info */ + lwz r1, WAKEUP_SP_OFFSET(r10) /* restore stack ptr */ + + /* zero ibm405lp_wakeup_info */ + lis r10, 0 + stw r10, 0(r9) + + /* restore ICCR/DCCR */ + /* do this before we load up the I-cache to modify CCR0 (below) */ + lwz r9, ICCR_SAVE(r1) /* load old ICCR */ + mtspr SPRN_ICCR, r9 /* set ICCR */ + lwz r9, DCCR_SAVE(r1) /* load old DCCR */ + mtspr SPRN_DCCR, r9 /* set DCCR */ + isync /* discard prefetches */ + + /* Touch the CCR0 part of this function into the I-cache */ + lis r9, reload_CCR0@h + ori r9, r9, reload_CCR0@l + li r10, ((reload_CCR0_end-reload_CCR0-1)>>LG_L1_CACHE_LINE_SIZE)+1 + mtctr r10 +4: + icbt r0, r9 + addi r9, r9, 32 /* next cacheline */ + bdnz 4b + isync + b reload_CCR0 + + .align LG_L1_CACHE_LINE_SIZE +reload_CCR0: + /* now that we're in the icache, restore CCR0 */ + lwz r5, CCR0_SAVE(r1) + mtspr SPRN_CCR0, r5 + isync +reload_CCR0_end: + + /* restore timers: CPC0_CR1, TCR, PIT */ + lwz r5, CPC0_CR0_SAVE(r1) + mtdcr DCRN_CPC0_CR0, r5 + lwz r5, CPC0_CR1_SAVE(r1) + mtdcr DCRN_CPC0_CR1, r5 + lwz r5, TCR_SAVE(r1) + mtspr SPRN_TCR, r5 + lwz r6, PIT_SAVE(r1) + andis. r5, r5, 0x0040 /* check TCR Auto Reload Enable bit */ + /* if ARE=0, restore saved PIT because it was a one-time timer */ + beq- reload_PIT + /* otherwise, ignore saved PIT and reload tb_ticks_per_jiffy */ + lis r5, tb_ticks_per_jiffy@h + ori r5, r5, tb_ticks_per_jiffy@l + tophys(r5,r5) + lwz r6, 0(r5) /* load tb_ticks_per_jiffy */ +reload_PIT: + mtspr SPRN_PIT, r6 + + /* restore entire TLB from stack */ + /* Afterwords, restore TLB related CPU state */ + /* r9 = TLB index (+= 1 per iteration) */ + /* r10 = save address (+= 3 per iteration) */ + li r9, 64 + addi r10, r1, TLB_SAVE-4 + mtctr r9 +restore_tlbe: + subi r9, r9, 1 + lwzu r11, 4(r10) + tlbwelo r11, r9 + lwzu r11, 4(r10) + lwzu r12, 4(r10) /* have to load PID before tlbwe */ + mtspr SPRN_PID, r12 + tlbwehi r11, r9 + bdnz restore_tlbe + + lwz r9, PID_SAVE(r1) + mtspr SPRN_PID, r9 + lwz r9, ZPR_SAVE(r1) + mtspr SPRN_ZPR, r9 + isync + + /* load SRR0/SRR1 */ + lwz r9, LR_SAVE(r1) /* load LR */ + mtsrr0 r9 /* prepare to jump back to caller */ + lwz r9, MSR_SAVE(r1) /* load MSR */ + mtsrr1 r9 /* prepare new MSR */ + + /* restore EVPR so our exceptions go to the right place when we rfi */ + lwz r9, EVPR_SAVE(r1) + mtspr SPRN_EVPR, r9 + + /* restore non-volatile regs */ + lwz r2, GPR2_SAVE(r1) + lwz r13, GPR13_SAVE(r1) + lwz r14, GPR14_SAVE(r1) + lwz r15, GPR15_SAVE(r1) + lwz r16, GPR16_SAVE(r1) + lwz r17, GPR17_SAVE(r1) + lwz r18, GPR18_SAVE(r1) + lwz r19, GPR19_SAVE(r1) + lwz r20, GPR20_SAVE(r1) + lwz r21, GPR21_SAVE(r1) + lwz r22, GPR22_SAVE(r1) + lwz r23, GPR23_SAVE(r1) + lwz r24, GPR24_SAVE(r1) + lwz r25, GPR25_SAVE(r1) + lwz r26, GPR26_SAVE(r1) + lwz r27, GPR27_SAVE(r1) + lwz r28, GPR28_SAVE(r1) + lwz r29, GPR29_SAVE(r1) + lwz r30, GPR30_SAVE(r1) + lwz r31, GPR31_SAVE(r1) + lwz r1, SP_SAVE(r1) /* restore virtual stack ptr */ + /* no more r1 loads/stores until after rfi! */ + addi r1, r1, SLEEP_STACK_BYTES /* surrender stack frame */ + mr r3, r4 /* return APM0_SR from firmware */ + + rfi /* jump to 'powerdown' caller */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibm440gp.c linuxppc-2.4/arch/ppc/platforms/ibm440gp.c --- linux-2.4.22/arch/ppc/platforms/ibm440gp.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibm440gp.c 2003-05-26 09:40:53.000000000 +1000 @@ -0,0 +1,216 @@ +/* + * arch/ppc/platforms/ibm440gp.c + * + * PPC440GP I/O descriptions + * + * Matt Porter + * Armin Kuster + * + * Copyright 2002 MontaVista Software Inc. + * + * Eugene Surovegin or + * Copyright (c) 2003 Zultys Technologies + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ebony.h" + +phys_addr_t fixup_bigphys_addr(phys_addr_t addr, phys_addr_t size) +{ + phys_addr_t page_4gb = 0; + + /* + * Trap the least significant 32-bit portions of an + * address in the 440's 36-bit address space. Fix + * them up with the appropriate ERPN + */ + if ((addr >= PPC440_IO_LO) && (addr < PPC440_IO_HI)) + page_4gb = PPC440_IO_PAGE; + else if ((addr >= PPC440_PCICFG_LO) && (addr < PPC440_PCICFG_HI)) + page_4gb = PPC440_PCICFG_PAGE; + else if ((addr >= PPC440_PCIMEM_LO) && (addr < PPC440_PCIMEM_HI)) + page_4gb = PPC440_PCIMEM_PAGE; + + return (page_4gb | addr); +}; + +static struct ocp_func_emac_data ibm440gp_emac0_def = { + .zmii_idx = 0, /* ZMII device index */ + .zmii_mux = 0, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 0, /* MAL rx channel number */ + .mal_tx1_chan = 0, /* MAL tx channel 1 number */ + .mal_tx2_chan = 1, /* MAL tx channel 2 number */ + .wol_irq = BL_MAC_WOL, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ +}; + +static struct ocp_func_emac_data ibm440gp_emac1_def = { + .zmii_idx = 0, /* ZMII device index */ + .zmii_mux = 1, /* ZMII input of this EMAC */ + .mal_idx = 0, /* MAL device index */ + .mal_rx_chan = 1, /* MAL rx channel number */ + .mal_tx1_chan = 2, /* MAL tx channel 1 number */ + .mal_tx2_chan = 3, /* MAL tx channel 2 number */ + .wol_irq = BL_MAC_WOL1, /* WOL interrupt number */ + .mdio_idx = -1, /* No shared MDIO */ +}; + +static struct ocp_func_mal_data ibm440gp_mal0_def = { + .num_tx_chans = 2*EMAC_NUMS, /* Number of TX channels */ + .num_rx_chans = EMAC_NUMS, /* Number of RX channels */ +}; + +struct ocp_def core_ocp[] __initdata = { +/* { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_OPB, + .index = 0, + .paddr = OPB_BASE_START, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, +*/ { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = PPC440GP_UART0_ADDR, + .irq = UART0_INT, + .pm = IBM_CPM_UART0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = PPC440GP_UART1_ADDR, + .irq = UART1_INT, + .pm = IBM_CPM_UART1 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .index = 0, + .paddr = PPC440GP_IIC0_ADDR, + .irq = IIC0_IRQ, + .pm = IBM_CPM_IIC0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_IIC, + .index = 1, + .paddr = PPC440GP_IIC1_ADDR, + .irq = IIC1_IRQ, + .pm = IBM_CPM_IIC1 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_GPIO, + .paddr = PPC440GP_GPIO0_ADDR, + .irq = OCP_IRQ_NA, + .pm = IBM_CPM_GPIO0 + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_MAL, + .paddr = OCP_PADDR_NA, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + .additions = &ibm440gp_mal0_def, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_ZMII, + .paddr = PPC440GP_ZMII_ADDR, + .irq = OCP_IRQ_NA, + .pm = OCP_CPM_NA, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 0, + .paddr = PPC440GP_EMAC0_ADDR, + .irq = BL_MAC_ETH0, + .pm = OCP_CPM_NA, + .additions = &ibm440gp_emac0_def, + }, + { .vendor = OCP_VENDOR_IBM, + .function = OCP_FUNC_EMAC, + .index = 1, + .paddr = PPC440GP_EMAC1_ADDR, + .irq = BL_MAC_ETH1, + .pm = OCP_CPM_NA, + .additions = &ibm440gp_emac1_def, + }, + { .vendor = OCP_VENDOR_INVALID + } + }; + +#if defined(EMAC_NUMS) && EMAC_NUMS > 0 +/* NOTE: This is defined in ppc4xx_setup.c too, for some reasons, + * we don't use this file for 440. This should be fixed either + * way to avoid this ugly duplication. --BenH. + */ +u32 emac_phy_map[EMAC_NUMS]; +EXPORT_SYMBOL(emac_phy_map); +#endif + +/* + * Calculate 440GP clocks + */ +void ibm440gp_get_clocks(struct ibm440gp_clocks* p, unsigned int sys_clk, + unsigned int ser_clk) +{ + u32 cpc0_sys0 = mfdcr(DCRN_CPC0_SYS0); + u32 cpc0_cr0 = mfdcr(DCRN_CPC0_CR0); + u32 opdv, epdv; + + if (cpc0_sys0 & 0x2){ + /* Bypass system PLL */ + p->cpu = p->plb = sys_clk; + } + else { + u32 fbdv, fwdva, fwdvb, m, vco; + + fbdv = (cpc0_sys0 >> 18) & 0x0f; + if (!fbdv) + fbdv = 16; + + fwdva = 8 - ((cpc0_sys0 >> 15) & 0x7); + fwdvb = 8 - ((cpc0_sys0 >> 12) & 0x7); + + /* Feedback path */ + if (cpc0_sys0 & 0x00000080){ + /* PerClk */ + m = fwdvb * opdv * epdv; + } + else { + /* CPU clock */ + m = fbdv * fwdva; + } + vco = sys_clk * m; + p->cpu = vco / fwdva; + p->plb = vco / fwdvb; + } + + opdv = ((cpc0_sys0 >> 10) & 0x3) + 1; + epdv = ((cpc0_sys0 >> 8) & 0x3) + 1; + + p->opb = p->plb / opdv; + p->ebc = p->opb / epdv; + + if (cpc0_cr0 & 0x00400000){ + /* External UART clock */ + p->uart = ser_clk; + } + else { + /* Internal UART clock */ + u32 uart_div = ((cpc0_cr0 >> 16) & 0x1f) + 1; + p->uart = p->plb / uart_div; + } +} + diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibm440gp.h linuxppc-2.4/arch/ppc/platforms/ibm440gp.h --- linux-2.4.22/arch/ppc/platforms/ibm440gp.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibm440gp.h 2003-05-26 09:40:53.000000000 +1000 @@ -0,0 +1,122 @@ +/* + * arch/ppc/platforms/ibm440gp.h + * + * PPC440GP definitions + * + * Roland Dreier + * + * Copyright 2002 Roland Dreier + * + * 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 file contains code that was originally in the files ibm440.h + * and ebony.h, which were written by Matt Porter of MontaVista Software Inc. + * + * Version 1.2 07/22/02 - Armin + * Added default power managment settings + */ + +#ifdef __KERNEL__ +#ifndef __PPC_PLATFORMS_IBM440GP_H +#define __PPC_PLATFORMS_IBM440GP_H + +#include + +#define EMAC_NUMS 2 +#define UART_NUMS 2 +#define ZMII_NUMS 1 +#define IIC_NUMS 2 +#define IIC0_IRQ 2 +#define IIC1_IRQ 3 +#define GPIO_NUMS 1 + +/* UART location */ +#define PPC440GP_UART0_ADDR 0x0000000140000200 +#define PPC440GP_UART1_ADDR 0x0000000140000300 + +/* EMAC location */ +#define PPC440GP_EMAC0_ADDR 0x0000000140000800 +#define PPC440GP_EMAC1_ADDR 0x0000000140000900 +#define PPC440GP_EMAC_SIZE 0x70 + +/* EMAC IRQ's */ +#define BL_MAC_WOL 61 /* WOL */ +#define BL_MAC_WOL1 63 /* WOL */ +#define BL_MAL_SERR 32 /* MAL SERR */ +#define BL_MAL_TXDE 33 /* MAL TXDE */ +#define BL_MAL_RXDE 34 /* MAL RXDE */ +#define BL_MAL_TXEOB 10 /* MAL TX EOB */ +#define BL_MAL_RXEOB 11 /* MAL RX EOB */ +#define BL_MAC_ETH0 60 /* MAC */ +#define BL_MAC_ETH1 62 /* MAC */ + +/* ZMII location */ +#define PPC440GP_ZMII_ADDR 0x0000000140000780 +#define PPC440GP_ZMII_SIZE 0x0c + +/* I2C location */ +#define PPC440GP_IIC0_ADDR 0x40000400 +#define PPC440GP_IIC1_ADDR 0x40000500 + +/* GPIO location */ +#define PPC440GP_GPIO0_ADDR 0x0000000140000700 + +/* Clock and Power Management */ +#define IBM_CPM_IIC0 0x80000000 /* IIC interface */ +#define IBM_CPM_IIC1 0x40000000 /* IIC interface */ +#define IBM_CPM_PCI 0x20000000 /* PCI bridge */ +#define IBM_CPM_CPU 0x02000000 /* processor core */ +#define IBM_CPM_DMA 0x01000000 /* DMA controller */ +#define IBM_CPM_BGO 0x00800000 /* PLB to OPB bus arbiter */ +#define IBM_CPM_BGI 0x00400000 /* OPB to PLB bridge */ +#define IBM_CPM_EBC 0x00200000 /* External Bux Controller */ +#define IBM_CPM_EBM 0x00100000 /* Ext Bus Master Interface */ +#define IBM_CPM_DMC 0x00080000 /* SDRAM peripheral controller */ +#define IBM_CPM_PLB 0x00040000 /* PLB bus arbiter */ +#define IBM_CPM_SRAM 0x00020000 /* SRAM memory controller */ +#define IBM_CPM_PPM 0x00002000 /* PLB Performance Monitor */ +#define IBM_CPM_UIC1 0x00001000 /* Universal Interrupt Controller */ +#define IBM_CPM_GPIO0 0x00000800 /* General Purpose IO (??) */ +#define IBM_CPM_GPT 0x00000400 /* General Purpose Timers */ +#define IBM_CPM_UART0 0x00000200 /* serial port 0 */ +#define IBM_CPM_UART1 0x00000100 /* serial port 1 */ +#define IBM_CPM_UIC0 0x00000080 /* Universal Interrupt Controller */ +#define IBM_CPM_TMRCLK 0x00000040 /* CPU timers */ + +#define DFLT_IBM4xx_PM ~(IBM_CPM_UIC | IBM_CPM_UIC1 | IBM_CPM_CPU \ + | IBM_CPM_EBC | IBM_CPM_SRAM | IBM_CPM_BGO \ + | IBM_CPM_EBM | IBM_CPM_PLB | IBM_CPM_OPB \ + | IBM_CPM_TMRCLK | IBM_CPM_DMA | IBM_CPM_PCI) + +/* + * Serial port defines + */ +#define RS_TABLE_SIZE 2 + +#include + +/* + * All clocks are in Hz + */ +struct ibm440gp_clocks { + unsigned int cpu; /* CPUCoreClk */ + unsigned int plb; /* PLBClk */ + unsigned int opb; /* OPBClk */ + unsigned int ebc; /* PerClk */ + unsigned int uart; +}; + +/* + * Please, refer to the Figure 13.1 in 440GP user manual + * + * if internal UART clock is used, ser_clk is ignored + */ +void ibm440gp_get_clocks(struct ibm440gp_clocks*, unsigned int sys_clk, + unsigned int ser_clk); + + +#endif /* __PPC_PLATFORMS_IBM440GP_H */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibmnp405h.c linuxppc-2.4/arch/ppc/platforms/ibmnp405h.c --- linux-2.4.22/arch/ppc/platforms/ibmnp405h.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibmnp405h.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,75 @@ +/* + * + * Copyright 2000-2002 MontaVista Software Inc. + * Completed implementation. + * Current maintainer + * Armin Kuster akuster@mvista.com + * + * Module name: ibmnp405h.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: 01/02/2002 - armin + * initial release + * + * : 04/07/02 - Armin + * removed eth1 & 2, Duh! ratio not 1:1 its 1:4 + * + * : 04/18/02 - Armin + * removed mal1 define + * + * : 05/06/02 - Armin + * converted to core_ocp[]; + * + * : 07/06/02 - Armin + * added OCP_IRQ_MUL to define EMAC + * and addes PM reg + * removed irq_resource + * added BL_MAC_ETHx to core_ocp + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +struct ocp_def core_ocp[] = { + {UART, UART0_IO_BASE, UART0_INT, IBM_CPM_UART0}, + {UART, UART1_IO_BASE, UART1_INT, IBM_CPM_UART1}, + {IIC, IIC0_BASE, IIC0_IRQ, IBM_CPM_IIC0}, + {GPIO, GPIO0_BASE, OCP_IRQ_NA, IBM_CPM_GPIO0}, + {PCI, PCIL0_BASE, OCP_IRQ_NA, IBM_CPM_PCI}, + {OPB, OPB0_BASE, OCP_IRQ_NA, IBM_CPM_OPB}, + {EMAC, EMAC0_BASE, BL_MAC_ETH0, IBM_CPM_EMAC0}, + {EMAC, EMAC1_BASE, BL_MAC_ETH1, IBM_CPM_EMAC1}, + {EMAC, EMAC2_BASE, BL_MAC_ETH2, IBM_CPM_EMAC2}, + {EMAC, EMAC3_BASE, BL_MAC_ETH3, IBM_CPM_EMAC3}, + {ZMII, ZMII0_BASE, OCP_IRQ_NA, OCP_CPM_NA}, + {OCP_NULL_TYPE, 0x0, OCP_IRQ_NA, OCP_CPM_NA}, + +}; diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibmnp405h.h linuxppc-2.4/arch/ppc/platforms/ibmnp405h.h --- linux-2.4.22/arch/ppc/platforms/ibmnp405h.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibmnp405h.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,222 @@ +/* + * ibmnp405h.h + * + * This was dirived from the ibm405gp.h and other previus works in ppc4xx.h + * + * Current maintainer + * Armin Kuster akuster@mvista.com + * Jan, 2002 + * + * + * Copyright 2002 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Version 1.0 (02/01/03) - A. Kuster + * Initial version + * + * Version 1.1 02/01/17 - A. Kuster + * moved common ofsets to ibm405.h + * + * Version 1.2 04/18/02 - Armin + * added pci defines from ibm405gp so it can build + * added UIC #define fixes + * + * Version 1.3 05/07/02 - Armin + * removed IIC_DFNS and some of the *_NUMS + * + * Version 1.3 05-29-02 - Armin + * removed PSR_PCI_ARBIT_EN * friends , not needed + * + * Version 1.4 07/22/02 - Armin + * Added default power managment setting + * + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBMNP405H_H__ +#define __ASM_IBMNP405H_H__ + +#include +#include + +/* ibm405.h at bottom of this file */ + +#define PPC405_PCI_CONFIG_ADDR 0xeec00000 +#define PPC405_PCI_CONFIG_DATA 0xeec00004 +#define PPC405_PCI_PHY_MEM_BASE 0x80000000 /* hose_a->pci_mem_offset */ + /* setbat */ +#define PPC405_PCI_MEM_BASE PPC405_PCI_PHY_MEM_BASE /* setbat */ +#define PPC405_PCI_PHY_IO_BASE 0xe8000000 /* setbat */ +#define PPC405_PCI_IO_BASE PPC405_PCI_PHY_IO_BASE /* setbat */ + +#define PPC405_PCI_LOWER_MEM 0x00000000 /* hose_a->mem_space.start */ +#define PPC405_PCI_UPPER_MEM 0xBfffffff /* hose_a->mem_space.end */ +#define PPC405_PCI_LOWER_IO 0x00000000 /* hose_a->io_space.start */ +#define PPC405_PCI_UPPER_IO 0x0000ffff /* hose_a->io_space.end */ + +#define PPC405_ISA_IO_BASE PPC405_PCI_IO_BASE + +#define PPC4xx_PCI_IO_ADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_ADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_ADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_ADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +/* serial port defines */ +#define RS_TABLE_SIZE 4 + +#define UART0_INT 0 +#define UART1_INT 1 +#define PCIL0_BASE 0xEF400000 +#define UART0_IO_BASE 0xEF600300 +#define UART1_IO_BASE 0xEF600400 +#define IIC0_BASE 0xEF600500 +#define OPB0_BASE 0xEF600600 +#define GPIO0_BASE 0xEF600700 +#define EMAC0_BASE 0xEF600800 +#define EMAC1_BASE 0xEF600900 +#define EMAC2_BASE 0xEF600a00 +#define EMAC3_BASE 0xEF600b00 +#define ZMII0_BASE 0xEF600C10 +#define BL_MAC_WOL 41 /* WOL */ +#define BL_MAL_SERR 45 /* MAL SERR */ +#define BL_MAL_TXDE 46 /* MAL TXDE */ +#define BL_MAL_RXDE 47 /* MAL RXDE */ +#define BL_MAL_TXEOB 17 /* MAL TX EOB */ +#define BL_MAL_RXEOB 18 /* MAL RX EOB */ +#define BL_MAC_ETH0 37 /* MAC */ +#define BL_MAC_ETH1 38 /* MAC */ +#define BL_MAC_ETH2 39 /* MAC */ +#define BL_MAC_ETH3 40 /* MAC */ + +#define EMAC_NUMS 4 +#define IIC0_IRQ 2 + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[e][i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base:(u8 *) UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) +#endif + +/* DCR defines */ +/* ------------------------------------------------------------------------- */ + +#define DCRN_CHCR_BASE 0x0F1 +#define DCRN_CHPSR_BASE 0x0B4 +#define DCRN_CPMSR_BASE 0x0BA +#define DCRN_CPMFR_BASE 0x0B9 +#define DCRN_CPMER_BASE 0x0B8 + +/* CPM Clocking & Power Mangement defines */ +#define IBM_CPM_PCI 0x40000000 /* PCI */ +#define IBM_CPM_EMAC2 0x20000000 /* EMAC 2 MII */ +#define IBM_CPM_EMAC3 0x04000000 /* EMAC 3 MII */ +#define IBM_CPM_EMAC0 0x00800000 /* EMAC 0 MII */ +#define IBM_CPM_EMAC1 0x00100000 /* EMAC 1 MII */ +#define IBM_CPM_EMMII 0 /* Shift value for MII */ +#define IBM_CPM_EMRX 1 /* Shift value for recv */ +#define IBM_CPM_EMTX 2 /* Shift value for MAC */ +#define IBM_CPM_UIC1 0x00020000 /* Universal Interrupt Controller */ +#define IBM_CPM_UIC0 0x00010000 /* Universal Interrupt Controller */ +#define IBM_CPM_CPU 0x00008000 /* processor core */ +#define IBM_CPM_EBC 0x00004000 /* ROM/SRAM peripheral controller */ +#define IBM_CPM_SDRAM0 0x00002000 /* SDRAM memory controller */ +#define IBM_CPM_GPIO0 0x00001000 /* General Purpose IO (??) */ +#define IBM_CPM_HDLC 0x00000800 /* HDCL */ +#define IBM_CPM_TMRCLK 0x00000400 /* CPU timers */ +#define IBM_CPM_PLB 0x00000100 /* PLB bus arbiter */ +#define IBM_CPM_OPB 0x00000080 /* PLB to OPB bridge */ +#define IBM_CPM_DMA 0x00000040 /* DMA controller */ +#define IBM_CPM_IIC0 0x00000010 /* IIC interface */ +#define IBM_CPM_UART0 0x00000002 /* serial port 0 */ +#define IBM_CPM_UART1 0x00000001 /* serial port 1 */ +/* this is the default setting for devices put to sleep when booting */ + +#define DFLT_IBM4xx_PM ~(IBM_CPM_UIC0 | IBM_CPM_UIC1 | IBM_CPM_CPU \ + | IBM_CPM_EBC | IBM_CPM_SDRAM0 | IBM_CPM_PLB \ + | IBM_CPM_OPB | IBM_CPM_TMRCLK | IBM_CPM_DMA \ + | IBM_CPM_EMAC0 | IBM_CPM_EMAC1 | IBM_CPM_EMAC2 \ + | IBM_CPM_EMAC3 | IBM_CPM_PCI) + +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_MAL_BASE 0x180 +#define DCRN_OCM0_BASE 0x018 +#define DCRN_PLB0_BASE 0x084 +#define DCRN_PLLMR_BASE 0x0B0 +#define DCRN_POB0_BASE 0x0A0 +#define DCRN_SDRAM0_BASE 0x010 +#define DCRN_UIC0_BASE 0x0C0 +#define DCRN_UIC1_BASE 0x0D0 +#define DCRN_CPC0_EPRCSR 0x0F3 + +#define UIC0_UIC1NC 30 /* UIC1 non-critical interrupt */ +#define UIC0_UIC1CR 31 /* UIC1 critical interrupt */ + +#define CHR1_CETE 0x00000004 /* CPU external timer enable */ +#define UIC0 DCRN_UIC0_BASE +#define UIC1 DCRN_UIC1_BASE + +#undef NR_UICS +#define NR_UICS 2 +#define UIC_CASCADE_MASK 0x0003 /* bits 30 & 31 */ + +/* EMAC DCRN's FIXME: armin */ +#define DCRN_MALRXCTP2R(base) ((base) + 0x42) /* Channel Rx 2 Channel Table Pointer */ +#define DCRN_MALRXCTP3R(base) ((base) + 0x43) /* Channel Rx 3 Channel Table Pointer */ +#define DCRN_MALTXCTP4R(base) ((base) + 0x24) /* Channel Tx 4 Channel Table Pointer */ +#define DCRN_MALTXCTP5R(base) ((base) + 0x25) /* Channel Tx 5 Channel Table Pointer */ +#define DCRN_MALTXCTP6R(base) ((base) + 0x26) /* Channel Tx 6 Channel Table Pointer */ +#define DCRN_MALTXCTP7R(base) ((base) + 0x27) /* Channel Tx 7 Channel Table Pointer */ +#define DCRN_MALRCBS2(base) ((base) + 0x62) /* Channel Rx 2 Channel Buffer Size */ +#define DCRN_MALRCBS3(base) ((base) + 0x63) /* Channel Rx 3 Channel Buffer Size */ + +#include + +#endif /* __ASM_IBMNP405H_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibmnp405l.c linuxppc-2.4/arch/ppc/platforms/ibmnp405l.c --- linux-2.4.22/arch/ppc/platforms/ibmnp405l.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibmnp405l.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,70 @@ +/* + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Current maintainer + * Armin Kuster akuster@mvista.com + * + * Module name: ibmnp405l.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: 12/26/2001 - armin + * initial release + * + * : 04/07/02 - armin + * removed irq_resource eth1 & 2 , Duh!, ratio not 1:1 its 1:2 + * + * : 04/19/02 - Todd + * add shared/private flag to irq_resource + * + * : 05/06/02 - Armin + * converted to core_ocp[]; + * + * : 07/04/02 - Armin + * changed EMAC irq definition to OCP_IRQ_MUL + * and added PM reg definitions + * and removed shared flag, handler & name + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct ocp_def core_ocp[] = { + {UART, UART0_IO_BASE, UART0_INT, IBM_CPM_UART0}, + {UART, UART1_IO_BASE, UART1_INT, IBM_CPM_UART1}, + {IIC, IIC0_BASE, IIC0_IRQ, IBM_CPM_IIC0}, + {GPIO, GPIO0_BASE, OCP_IRQ_NA, IBM_CPM_GPIO0}, + {OPB, OPB0_BASE, OCP_IRQ_NA, IBM_CPM_OPB}, + {EMAC, EMAC0_BASE, BL_MAC_ETH0, IBM_CPM_EMAC0}, + {EMAC, EMAC1_BASE, BL_MAC_ETH1, IBM_CPM_EMAC1}, + {ZMII, ZMII0_BASE, OCP_IRQ_NA, OCP_CPM_NA}, + {OCP_NULL_TYPE, 0x0, OCP_IRQ_NA, OCP_CPM_NA}, + +}; diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibmnp405l.h linuxppc-2.4/arch/ppc/platforms/ibmnp405l.h --- linux-2.4.22/arch/ppc/platforms/ibmnp405l.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibmnp405l.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,176 @@ +/* + * ibmnp405l.h + * + * This was dirived from the ibm405gp.h and other previus works in ppc4xx.h + * + * Current maintainer + * Armin Kuster akuster@mvista.com + * Oct, 2001 + * + * + * Copyright 2001 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Version 1.0 (01/10/10) - A. Kuster + * Initial version + * + * Version 1.1 (02/01/16) - A. Kuster + * Cleaned out common 405B3 core stuff + * + * Version 1.2 (02/02/06) - A. Kuster + * Removed PCI definitions, changed some dcr addrs + * and bit map for the CETE, PM bit maps + * + * Version 1.3 (03/29/02) - A. Kuster + * added I2c support + * + * Version 1.4 (05/07/02) - Armin + * removed IIC_PORT_DFNS and some of the *_NUMS + * + * Version 1.5 (07/22/02) - Armin + * added default power management settings + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBMNP405L_H__ +#define __ASM_IBMNP405L_H__ + +#include +#include + +/* serial port defines */ +#define RS_TABLE_SIZE 2 + +#define UART0_INT 0 +#define UART1_INT 1 + +#define UART0_IO_BASE 0xEF600300 +#define UART1_IO_BASE 0xEF600400 +#define IIC0_BASE 0xEF600500 +#define OPB0_BASE 0xEF600600 +#define GPIO0_BASE 0xEF600700 +#define EMAC0_BASE 0xEF600800 +#define EMAC1_BASE 0xEF600900 +#define ZMII0_BASE 0xEF600C10 +#define BL_MAC_WOL 41 /* WOL */ +#define BL_MAL_SERR 45 /* MAL SERR */ +#define BL_MAL_TXDE 46 /* MAL TXDE */ +#define BL_MAL_RXDE 47 /* MAL RXDE */ +#define BL_MAL_TXEOB 17 /* MAL TX EOB */ +#define BL_MAL_RXEOB 18 /* MAL RX EOB */ +#define BL_MAC_ETH0 37 /* MAC */ +#define BL_MAC_ETH1 38 /* MAC */ + +#define EMAC_NUMS 2 + +#define IIC0_IRQ 2 + +#undef NR_UICS +#define NR_UICS 2 +#define UIC_CASCADE_MASK 0x0003 /* bits 30 & 31 */ + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[e][i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base:(u8 *) UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_DEBUG_IO_BASE UART1_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) +#endif + +/* DCR defines */ +/* ------------------------------------------------------------------------- */ + +#define DCRN_CHCR_BASE 0x0F1 +#define DCRN_CHPSR_BASE 0x0B4 +#define DCRN_CPMSR_BASE 0x0BA +#define DCRN_CPMFR_BASE 0x0B9 +#define DCRN_CPMER_BASE 0x0B8 + +#define IBM_CPM_EMAC0 0x00800000 /* on-chip ethernet MM unit */ +#define IBM_CPM_EMAC1 0x00100000 /* EMAC 1 MII */ +#define IBM_CPM_UIC1 0x00020000 /* Universal Interrupt Controller */ +#define IBM_CPM_UIC0 0x00010000 /* Universal Interrupt Controller */ +#define IBM_CPM_CPU 0x00008000 /* processor core */ +#define IBM_CPM_EBC 0x00004000 /* ROM/SRAM peripheral controller */ +#define IBM_CPM_SDRAM0 0x00002000 /* SDRAM memory controller */ +#define IBM_CPM_GPIO0 0x00001000 /* General Purpose IO (??) */ +#define IBM_CPM_HDLC 0x00000800 /* HDCL */ +#define IBM_CPM_TMRCLK 0x00000400 /* CPU timers */ +#define IBM_CPM_PLB 0x00000100 /* PLB bus arbiter */ +#define IBM_CPM_OPB 0x00000080 /* PLB to OPB bridge */ +#define IBM_CPM_DMA 0x00000040 /* DMA controller */ +#define IBM_CPM_IIC0 0x00000010 /* IIC interface */ +#define IBM_CPM_UART0 0x00000002 /* serial port 0 */ +#define IBM_CPM_UART1 0x00000001 /* serial port 1 */ +#define DFLT_IBM4xx_PM ~(IBM_CPM_UIC0 | IBM_CPM_UIC1 | IBM_CPM_CPU \ + | IBM_CPM_EBC | IBM_CPM_SDRAM0 | IBM_CPM_PLB \ + | IBM_CPM_OPB | IBM_CPM_TMRCLK | IBM_CPM_DMA \ + | IBM_CPM_EMAC0 | IBM_CPM_EMAC1) + +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_MAL_BASE 0x180 +#define DCRN_MAL1_BASE 0x200 +#define DCRN_OCM0_BASE 0x018 +#define DCRN_PLB0_BASE 0x084 +#define DCRN_PLLMR_BASE 0x0F0 +#define DCRN_POB0_BASE 0x0A0 +#define DCRN_SDRAM0_BASE 0x010 +#define DCRN_UIC0_BASE 0x0C0 +#define DCRN_UIC1_BASE 0x0D0 +#define DCRN_CPC0_EPRCSR 0x0F3 + +#define UIC0_UIC1NC 30 /* UIC1 non-critical interrupt */ +#define UIC0_UIC1CR 31 /* UIC1 critical interrupt */ + +#define CHR1_CETE 0x00000004 /* CPU external timer enable */ +#define UIC0 DCRN_UIC0_BASE +#define UIC1 DCRN_UIC1_BASE + +#define SDRAM_CFG 0x20 +#define SDRAM0_ECCCFG 0x94 +#define SDRAM_NO_ECC 0x10000000 +#include + +#endif /* __ASM_IBMNP405L_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibmnp4gs.c linuxppc-2.4/arch/ppc/platforms/ibmnp4gs.c --- linux-2.4.22/arch/ppc/platforms/ibmnp4gs.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibmnp4gs.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,52 @@ +/* + * + * Copyright 2000-2002 MontaVista Software Inc. + * Completed implementation. + * Current maintainer + * Armin Kuster akuster@mvista.com + * + * Module name: ibmnp4gs.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: 02/29/2002 - armin + * initial release + * + * : 05/06/02 - Armin + * converted to core_ocp[]; + * + * : 7/05/02 - Armin + * added OCP_IRQ_MUL to define EMAC + * and added PM register + * + */ + +#include +#include +#include + +struct ocp_def core_ocp[] = { + {UART, UART0_IO_BASE, UART0_INT, IBM_CPM_UART0}, + {PCI, PCIL0_BASE, OCP_IRQ_NA, IBM_CPM_PCI}, + {OCP_NULL_TYPE, 0x0, OCP_IRQ_NA, OCP_CPM_NA}, + +}; diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibmnp4gs.h linuxppc-2.4/arch/ppc/platforms/ibmnp4gs.h --- linux-2.4.22/arch/ppc/platforms/ibmnp4gs.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibmnp4gs.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,169 @@ +/* + * ibmnp405gs.h + * + * Armin Kuster akuster@mvista.com + * + * + * Copyright 2002 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Version 1.0 (Feb, 2002) - A. Kuster + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBMNP4GS_H__ +#define __ASM_IBMNP4GS_H__ + +#include +#include + +/* ibm405.h at bottom of this file */ + +/* PCI + * PCI Bridge config reg definitions + * see 17-19 of manual + */ + +#define PPC405_PCI_CONFIG_ADDR 0xeec00000 +#define PPC405_PCI_CONFIG_DATA 0xeec00004 + +#define PPC405_PCI_PHY_MEM_BASE 0x80000000 /* hose_a->pci_mem_offset */ + /* setbat */ +#define PPC405_PCI_MEM_BASE PPC405_PCI_PHY_MEM_BASE /* setbat */ +#define PPC405_PCI_PHY_IO_BASE 0xe8000000 /* setbat */ +#define PPC405_PCI_IO_BASE PPC405_PCI_PHY_IO_BASE /* setbat */ + +#define PPC405_PCI_LOWER_MEM 0x80000000 /* hose_a->mem_space.start */ +#define PPC405_PCI_UPPER_MEM 0xBfffffff /* hose_a->mem_space.end */ +#define PPC405_PCI_LOWER_IO 0x00000000 /* hose_a->io_space.start */ +#define PPC405_PCI_UPPER_IO 0x0000ffff /* hose_a->io_space.end */ + +#define PPC405_ISA_IO_BASE PPC405_PCI_IO_BASE + +#define PPC4xx_PCI_IO_PADDR ((uint)PPC405_PCI_PHY_IO_BASE) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE ((uint)64*1024) +#define PPC4xx_PCI_CFG_PADDR ((uint)PPC405_PCI_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE ((uint)4*1024) +#define PPC4xx_PCI_LCFG_PADDR ((uint)0xef400000) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE ((uint)4*1024) +#define PPC4xx_ONB_IO_PADDR ((uint)0xef600000) +#define PPC4xx_ONB_IO_VADDR PPC4xx_ONB_IO_PADDR +#define PPC4xx_ONB_IO_SIZE ((uint)4*1024) + +#define PCI_CONFIG_ADDR_MASK 0x7F000000 +#define PCI_CONFIG_CYCLE_ENABLE 0x80000000 +#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits [htype 0 only] */ +#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 +#define PCI_CONFIG_ADDR_MASK 0x7F000000 +#define PCI_CONFIG_CYCLE_ENABLE 0x80000000 +#define PCI_BASE_ADDRESS_MEM_CARD1 0x80000000 +#define PCI_BASE_ADDRESS_MEM_CARD2 0x90000000 +#define PPC405_UART0_INT 1 +#define PPC_405RAINIER2_IO_PAGE ((void*)0xe8001000) +#define PPC_405RAINIER1_IO_PAGE ((void*)0xe8002000) +#define PPC405_UART0_IO_BASE 0x300 /* mostly use (rainier_io_page+0x300) */ +#define RAINIER_IO_PAGE_INTERPOSER_PADDR 0xe8000000 +#define RAINIER_IO_PAGE_INTERPOSER_VADDR RAINIER_IO_PAGE_INTERPOSER_PADDR +#define RAINIER_IO_PAGE_PCI_PADDR 0xeec00000 +#define RAINIER_IO_PAGE_PCI_VADDR RAINIER_IO_PAGE_PCI_PADDR + +/* serial port defines */ +#define RS_TABLE_SIZE 1 + +#define UART0_INT 1 + +#define PCIL0_BASE 0xEF400000 +#define UART0_IO_BASE PPC_405RAINIER1_IO_PAGE + PPC405_UART0_IO_BASE + +#define UART_NUMS 1 +#define BD_EMAC_ADDR(e,i) bi_enetaddr[i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: (u8 *)UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) +#endif + +/* DCR defines */ +#define DCRN_CHCR_BASE 0x0B1 +#define DCRN_CHPSR_BASE 0x0B4 +#define DCRN_CPMSR_BASE 0x0B8 +#define DCRN_CPMFR_BASE 0x0BA + +#define PSR_PLL_FWD_MASK 0xC0000000 +#define PSR_PLL_FDBACK_MASK 0x30000000 +#define PSR_PLL_TUNING_MASK 0x0E000000 +#define PSR_PLB_CPU_MASK 0x01800000 +#define PSR_OPB_PLB_MASK 0x00600000 +#define PSR_PCI_PLB_MASK 0x00180000 +#define PSR_EB_PLB_MASK 0x00060000 +#define PSR_ROM_WIDTH_MASK 0x00018000 +#define PSR_ROM_LOC 0x00004000 +#define PSR_PCI_ASYNC_EN 0x00001000 +#define PSR_PCI_ARBIT_EN 0x00000400 + +#define IBM_CPM_IIC0 0x80000000 /* IIC interface */ +#define IBM_CPM_PCI 0x40000000 /* PCI bridge */ +#define IBM_CPM_CPU 0x20000000 /* processor core */ +#define IBM_CPM_DMA 0x10000000 /* DMA controller */ +#define IBM_CPM_BRG 0x08000000 /* PLB to OPB bridge */ +#define IBM_CPM_DCP 0x04000000 /* CodePack */ +#define IBM_CPM_EBC 0x02000000 /* ROM/SRAM peripheral controller */ +#define IBM_CPM_SDRAM0 0x01000000 /* SDRAM memory controller */ +#define IBM_CPM_PLB 0x00800000 /* PLB bus arbiter */ +#define IBM_CPM_GPIO0 0x00400000 /* General Purpose IO (??) */ +#define IBM_CPM_UART0 0x00200000 /* serial port 0 */ +#define IBM_CPM_UART1 0x00100000 /* serial port 1 */ +#define IBM_CPM_UIC 0x00080000 /* Universal Interrupt Controller */ +#define IBM_CPM_TMRCLK 0x00040000 /* CPU timers */ + +#define DFLT_IBM4xx_PM 0 + +#define DCRN_DMA0_BASE 0x100 +#define DCRN_DMA1_BASE 0x108 +#define DCRN_DMA2_BASE 0x110 +#define DCRN_DMA3_BASE 0x118 +#define DCRNCAP_DMA_SG 1 /* have DMA scatter/gather capability */ +#define DCRN_DMASR_BASE 0x120 +#define DCRN_EBC_BASE 0x012 +#define DCRN_DCP0_BASE 0x014 +#define DCRN_MAL_BASE 0x180 +#define DCRN_OCM0_BASE 0x018 +#define DCRN_PLB0_BASE 0x084 +#define DCRN_PLLMR_BASE 0x0B0 +#define DCRN_POB0_BASE 0x0A0 +#define DCRN_SDRAM0_BASE 0x010 +#define DCRN_UIC0_BASE 0x0C0 +#define UIC0 DCRN_UIC0_BASE + +#include + +#endif /* __ASM_IBMNP4GS_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibmstb3.c linuxppc-2.4/arch/ppc/platforms/ibmstb3.c --- linux-2.4.22/arch/ppc/platforms/ibmstb3.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibmstb3.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,55 @@ +/* + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Current maintainer + * Armin Kuster akuster@mvista.com + * + * Module name: ibmstb3.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: 01/08/2002 - armin + * initial release + * + * 05/06/02 - Armin + * converted to core_ocp[] + * + * : 07/07/02 - Armin + * added OCP_IRQ_MUL to define EMAC + * and added PM register + * + */ + +#include +#include +#include + +struct ocp_def core_ocp[] = { + {UART, UART0_IO_BASE, UART0_INT, IBM_CPM_UART0}, + {IIC, IIC0_BASE, IIC0_IRQ, IBM_CPM_IIC0}, + {IIC, IIC1_BASE, IIC1_IRQ, IBM_CPM_IIC1}, + {GPIO, GPIO0_BASE, OCP_IRQ_NA, IBM_CPM_GPIO0}, + {OPB, OPB0_BASE, OCP_IRQ_NA, OCP_CPM_NA}, + {OCP_NULL_TYPE, 0x0, OCP_IRQ_NA, OCP_CPM_NA}, + +}; diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibmstb3.h linuxppc-2.4/arch/ppc/platforms/ibmstb3.h --- linux-2.4.22/arch/ppc/platforms/ibmstb3.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibmstb3.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,280 @@ +/* + * ibmstbx.h + * + * This was dirived from the ppc4xx.h and all stbx specific definitions + * are located here. + * + * Armin Kuster + * Tom Rini + * Oct, 2001 + * + * + * + * Copyright 2001 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Version 1.0 Oct 10, 2001 - A. Kuster + * Initial version - moved stbx specific out of ibm4xx.h + * + * Version 1.1 Oct 25, 2001 - T. Rini + * Lots of cleanups, and we get included by the board-specific file. + * + * Version 1.2 Jan 16, 2002 - A. Kuster + * Removed common dcr offests that are now in ibm405.h + * + * Version 1.3 Feb 22, 2002 - Armin & David Gibson + * Added early serial boot #define support + * added UIC define + * + * Version 1.3 03/29/02 - Armin + * Changed i2c support to ocp standard + * + * Version 1.4 05/06/02 - Armin + * removed IIC_IRQ now using core_ocp[]; + * + * Version 1.5 07/22/02 - Armin + * added default power manament settings + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBMSTBX_H__ +#define __ASM_IBMSTBX_H__ + +#include +#include + +/* ibm405.h at bottom of this file */ + +/* + * Memory map for the IBM "Redwood-4" STB03xxx evaluation board. + * + * The STB03xxx internal i/o addresses don't work for us 1:1, + * so we need to map them at a well know virtual address. + * + * 4000 000x uart1 -> 0xe000 000x + * 4001 00xx ppu + * 4002 00xx smart card + * 4003 000x iic + * 4004 000x uart0 + * 4005 0xxx timer + * 4006 00xx gpio + * 4007 00xx smart card + * 400b 000x iic + * 400c 000x scp + * 400d 000x modem + */ + +#define STB03xxx_IO_BASE ((uint)0xe0000000) +#define PPC4xx_ONB_IO_PADDR ((uint)0x40000000) +#define PPC4xx_ONB_IO_VADDR STB03xxx_IO_BASE +#define PPC4xx_ONB_IO_SIZE ((uint)14*64*1024) + +/* Since we're into address mapping hacks, at least try to hide + * it under a macro..... + */ +#define STB03xxx_MAP_IO_ADDR(a) (((uint)(a) & 0x000fffff) + PPC4xx_ONB_IO_VADDR) + +#define RS_TABLE_SIZE 1 +#define UART0_INT 20 +#ifdef __BOOTER__ +#define UART0_IO_BASE 0x40040000 +#else +#define UART0_IO_BASE 0xe0040000 +#endif + + /* UART 0 is duped here so when the SICC is the default console + * then ttys1 is configured properly - armin + */ + +#define UART1_INT 20 +#ifdef __BOOTER__ +#define UART1_IO_BASE 0x40040000 +#else +#define UART1_IO_BASE 0xe0040000 +#endif + +/* need to make this work in scheme - armin */ + +#define SICC0_INTRX 21 +#define SICC0_INTTX 22 +#define SICC0_IO_BASE ((uint* )0x40000000) + +#define IIC0_BASE 0x40030000 +#define IIC1_BASE 0x400b0000 +#define OPB0_BASE 0x40010000 +#define GPIO0_BASE 0x40060000 +#define IIC0_IRQ 9 +#define IIC1_IRQ 10 +#define IIC_OWN 0x55 +#define IIC_CLOCK 50 + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: (u8 *)UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_DEBUG_IO_BASE UART1_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) +#endif + +/* ------------------------------------------------------------------------- */ + +#define DCRN_DCRX_BASE 0x020 +#define DCRN_CIC_BASE 0x030 +#define DCRN_UIC0_BASE 0x040 +#define DCRN_PLB0_BASE 0x054 +#define DCRN_PLB1_BASE 0x064 +#define DCRN_EBIMC_BASE 0x070 +#define DCRN_POB0_BASE 0x0B0 + +#define DCRN_BE_BASE 0x090 +#define DCRN_DMA0_BASE 0x0C0 +#define DCRN_DMA1_BASE 0x0C8 +#define DCRN_DMA2_BASE 0x0D0 +#define DCRN_DMA3_BASE 0x0D8 +#define DCRNCAP_DMA_CC 1 /* have DMA chained count capability */ +#define DCRN_DMASR_BASE 0x0E0 + +#define DCRN_CPMFR_BASE 0x102 +#define DCRN_SCCR_BASE 0x120 +#define UIC0 DCRN_UIC0_BASE + +#define IBM_CPM_IIC0 0x80000000 /* IIC 0 interface */ +#define IBM_CPM_I1284 0x40000000 /* IEEE-1284 */ +#define IBM_CPM_IIC1 0x20000000 /* IIC 1 interface */ +#define IBM_CPM_CPU 0x10000000 /* PPC405B3 clock control */ +#define IBM_CPM_AUD 0x08000000 /* Audio Decoder */ +#define IBM_CPM_EBIU 0x04000000 /* External Bus Interface Unit */ +#define IBM_CPM_SDRAM1 0x02000000 /* SDRAM 1 memory controller */ +#define IBM_CPM_DMA 0x01000000 /* DMA controller */ +#define IBM_CPM_UART1 0x00100000 /* Serial 1 / Infrared */ +#define IBM_CPM_UART0 0x00080000 /* Serial 0 / 16550 */ +#define IBM_CPM_DCRX 0x00040000 /* DCR Extension */ +#define IBM_CPM_SC0 0x00020000 /* Smart Card 0 */ +#define IBM_CPM_SC1 0x00008000 /* Smart Card 1 */ +#define IBM_CPM_SDRAM0 0x00004000 /* SDRAM 0 memory controller */ +#define IBM_CPM_XPT54 0x00002000 /* Transport - 54 Mhz */ +#define IBM_CPM_CBS 0x00001000 /* Cross Bar Switch */ +#define IBM_CPM_GPT 0x00000800 /* GPTPWM */ +#define IBM_CPM_GPIO0 0x00000400 /* General Purpose IO 0 */ +#define IBM_CPM_DENC 0x00000200 /* Digital video Encoder */ +#define IBM_CPM_TMRCLK 0x00000100 /* CPU timers */ +#define IBM_CPM_XPT27 0x00000080 /* Transport - 27 Mhz */ +#define IBM_CPM_UIC 0x00000040 /* Universal Interrupt Controller */ +#define IBM_CPM_MSI 0x00000010 /* Modem Serial Interface (SSP) */ +#define IBM_CPM_UART2 0x00000008 /* Serial Control Port */ +#define IBM_CPM_DSCR 0x00000004 /* Descrambler */ +#define IBM_CPM_VID2 0x00000002 /* Video Decoder clock domain 2 */ + +#define DFLT_IBM4xx_PM ~(IBM_CPM_CPU | IBM_CPM_EBIU | IBM_CPM_SDRAM1 \ + | IBM_CPM_DMA | IBM_CPM_CBS | IBM_CPM_SDRAM0 \ + | IBM_CPM_XPT54 | IBM_CPM_TMRCLK | IBM_CPM_XPT27 \ + | IBM_CPM_UIC) + +#ifdef DCRN_CIC_BASE +#define DCRN_CICCR (DCRN_CIC_BASE + 0x0) /* CIC Control Register */ +#define DCRN_DMAS1 (DCRN_CIC_BASE + 0x1) /* DMA Select1 Register */ +#define DCRN_DMAS2 (DCRN_CIC_BASE + 0x2) /* DMA Select2 Register */ +#define DCRN_CICVCR (DCRN_CIC_BASE + 0x3) /* CIC Video COntro Register */ +#define DCRN_CICSEL3 (DCRN_CIC_BASE + 0x5) /* CIC Select 3 Register */ +#define DCRN_SGPO (DCRN_CIC_BASE + 0x6) /* CIC GPIO Output Register */ +#define DCRN_SGPOD (DCRN_CIC_BASE + 0x7) /* CIC GPIO OD Register */ +#define DCRN_SGPTC (DCRN_CIC_BASE + 0x8) /* CIC GPIO Tristate Ctrl Reg */ +#define DCRN_SGPI (DCRN_CIC_BASE + 0x9) /* CIC GPIO Input Reg */ +#endif + +#ifdef DCRN_DCRX_BASE +#define DCRN_DCRXICR (DCRN_DCRX_BASE + 0x0) /* Internal Control Register */ +#define DCRN_DCRXISR (DCRN_DCRX_BASE + 0x1) /* Internal Status Register */ +#define DCRN_DCRXECR (DCRN_DCRX_BASE + 0x2) /* External Control Register */ +#define DCRN_DCRXESR (DCRN_DCRX_BASE + 0x3) /* External Status Register */ +#define DCRN_DCRXTAR (DCRN_DCRX_BASE + 0x4) /* Target Address Register */ +#define DCRN_DCRXTDR (DCRN_DCRX_BASE + 0x5) /* Target Data Register */ +#define DCRN_DCRXIGR (DCRN_DCRX_BASE + 0x6) /* Interrupt Generation Register */ +#define DCRN_DCRXBCR (DCRN_DCRX_BASE + 0x7) /* Line Buffer Control Register */ +#endif + +#ifdef DCRN_EBC_BASE +#define DCRN_EBCCFGADR (DCRN_EBC_BASE + 0x0) /* Peripheral Controller Address */ +#define DCRN_EBCCFGDATA (DCRN_EBC_BASE + 0x1) /* Peripheral Controller Data */ +#endif + +#ifdef DCRN_EBIMC_BASE +#define DCRN_BRCRH0 (DCRN_EBIMC_BASE + 0x0) /* Bus Region Config High 0 */ +#define DCRN_BRCRH1 (DCRN_EBIMC_BASE + 0x1) /* Bus Region Config High 1 */ +#define DCRN_BRCRH2 (DCRN_EBIMC_BASE + 0x2) /* Bus Region Config High 2 */ +#define DCRN_BRCRH3 (DCRN_EBIMC_BASE + 0x3) /* Bus Region Config High 3 */ +#define DCRN_BRCRH4 (DCRN_EBIMC_BASE + 0x4) /* Bus Region Config High 4 */ +#define DCRN_BRCRH5 (DCRN_EBIMC_BASE + 0x5) /* Bus Region Config High 5 */ +#define DCRN_BRCRH6 (DCRN_EBIMC_BASE + 0x6) /* Bus Region Config High 6 */ +#define DCRN_BRCRH7 (DCRN_EBIMC_BASE + 0x7) /* Bus Region Config High 7 */ +#define DCRN_BRCR0 (DCRN_EBIMC_BASE + 0x10) /* BRC 0 */ +#define DCRN_BRCR1 (DCRN_EBIMC_BASE + 0x11) /* BRC 1 */ +#define DCRN_BRCR2 (DCRN_EBIMC_BASE + 0x12) /* BRC 2 */ +#define DCRN_BRCR3 (DCRN_EBIMC_BASE + 0x13) /* BRC 3 */ +#define DCRN_BRCR4 (DCRN_EBIMC_BASE + 0x14) /* BRC 4 */ +#define DCRN_BRCR5 (DCRN_EBIMC_BASE + 0x15) /* BRC 5 */ +#define DCRN_BRCR6 (DCRN_EBIMC_BASE + 0x16) /* BRC 6 */ +#define DCRN_BRCR7 (DCRN_EBIMC_BASE + 0x17) /* BRC 7 */ +#define DCRN_BEAR0 (DCRN_EBIMC_BASE + 0x20) /* Bus Error Address Register */ +#define DCRN_BESR0 (DCRN_EBIMC_BASE + 0x21) /* Bus Error Status Register */ +#define DCRN_BIUCR (DCRN_EBIMC_BASE + 0x2A) /* Bus Interfac Unit Ctrl Reg */ +#endif + +#ifdef DCRN_SCCR_BASE +#define DCRN_SCCR (DCRN_SCCR_BASE + 0x0) +#endif + +#ifdef DCRN_SDRAM0_BASE +#define DCRN_SDRAM0_CFGADDR (DCRN_SDRAM0_BASE + 0x0) /* Memory Controller Address */ +#define DCRN_SDRAM0_CFGDATA (DCRN_SDRAM0_BASE + 0x1) /* Memory Controller Data */ +#endif + +#ifdef DCRN_OCM0_BASE +#define DCRN_OCMISARC (DCRN_OCM0_BASE + 0x0) /* OCM Instr Side Addr Range Compare */ +#define DCRN_OCMISCR (DCRN_OCM0_BASE + 0x1) /* OCM Instr Side Control */ +#define DCRN_OCMDSARC (DCRN_OCM0_BASE + 0x2) /* OCM Data Side Addr Range Compare */ +#define DCRN_OCMDSCR (DCRN_OCM0_BASE + 0x3) /* OCM Data Side Control */ +#endif + +#define REDWOOD_IDE_CMD 0xf2100000 +#define REDWOOD_IDE_CTRL 0xf4100000 +#define IDE_DMA_ADDR 0xfce00000 +#define IDE0_IRQ 25 + + +#include + +#endif /* __ASM_IBMSTBX_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibmstb4.c linuxppc-2.4/arch/ppc/platforms/ibmstb4.c --- linux-2.4.22/arch/ppc/platforms/ibmstb4.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibmstb4.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,59 @@ +/* + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Current maintainer + * Armin Kuster akuster@mvista.com + * + * Module name: ibmstb4.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: 12/26/2001 - armin + * initial release + * + * : 05/05/02 - Armin + * replaced all const strcuts with new core_ocp + * + * : 07/07/02 - Armin + * added OCP_IRQ_MUL to define EMAC + * and added PM register + * + */ + +#include +#include +#include +#include + +struct ocp_def core_ocp[] = { + {UART, UART0_IO_BASE, UART0_INT,IBM_CPM_UART0}, + {UART, UART1_IO_BASE, UART1_INT, IBM_CPM_UART1}, + {UART, UART2_IO_BASE, UART2_INT, IBM_CPM_UART2}, + {IIC, IIC0_BASE, IIC0_IRQ, IBM_CPM_IIC0}, + {IIC, IIC1_BASE, IIC1_IRQ, IBM_CPM_IIC1}, + {GPIO, GPIO0_BASE, OCP_IRQ_NA, IBM_CPM_GPIO0}, + {IDE, IDE0_BASE, IDE0_IRQ, OCP_CPM_NA}, + {USB, USB0_BASE, USB0_IRQ, IBM_CPM_USB0}, + {OCP_NULL_TYPE, 0x0, OCP_IRQ_NA, OCP_CPM_NA}, + +}; diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibmstb4.h linuxppc-2.4/arch/ppc/platforms/ibmstb4.h --- linux-2.4.22/arch/ppc/platforms/ibmstb4.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibmstb4.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,285 @@ +/* + * ibmstb4.h + * + * This was dirived from the ibm405gp.h and other previus works in ppc4xx.h + * + * Current maintainer + * Armin Kuster akuster@mvista.com + * DEC, 2001 + * + * + * Copyright 2001 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Version 1.0 (01/10/10) - A. Kuster + * Initial version + * + * Version 1.1 02/01/17 - A. Kuster + * moved common offsets to ibm405.h + * + * Version 1.2 02/22/02 - A. Kuster + * Added UIC and + * Added early serial boot #define support - David G. + * fixed __BOOTER__ + * + * Version 1.3 03/29/02 - Armin + * Added i2c & usb ocp support + * + * Version 1.4 04/30/02 - Armin + * Added IIC_PORT_DFNS macros + * Version 1.5 - 05/05/02 _ armin + * removed IIC_PORT now using core_ocp[] + * + * Version 1.6 07/22/02 - Armin + * added default power managment setting + * + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBMSTB4_H__ +#define __ASM_IBMSTB4_H__ + +#include +#include + +/* serial port defines */ +#define STB04xxx_IO_BASE ((uint)0xe0000000) +#define PPC4xx_PCI_IO_ADDR STB04xxx_IO_BASE +#define PPC4xx_ONB_IO_PADDR STB04xxx_IO_BASE +#define PPC4xx_ONB_IO_VADDR ((uint)0xe0000000) +#define PPC4xx_ONB_IO_SIZE ((uint)14*64*1024) + +/* + * map STB04xxx internal i/o address (0x400x00xx) to an address + * which is below the 2GB limit... + * + * 4000 000x uart1 -> 0xe000 000x + * 4001 00xx ppu + * 4002 00xx smart card + * 4003 000x iic + * 4004 000x uart0 + * 4005 0xxx timer + * 4006 00xx gpio + * 4007 00xx smart card + * 400b 000x iic + * 400c 000x scp + * 400d 000x modem + * 400e 000x uart2 +*/ +#define STB04xxx_MAP_IO_ADDR(a) (((uint)(a)) + (STB04xxx_IO_BASE - 0x40000000)) + +#define RS_TABLE_SIZE 3 +#define UART0_INT 20 + +#ifdef __BOOTER__ +#define UART0_IO_BASE 0x40040000 +#else +#define UART0_IO_BASE 0xe0040000 +#endif + +#define UART1_INT 21 + +#ifdef __BOOTER__ +#define UART1_IO_BASE 0x40000000 +#else +#define UART1_IO_BASE 0xe0000000 +#endif + +#define UART2_INT 31 +#ifdef __BOOTER__ +#define UART2_IO_BASE 0x400e0000 +#else +#define UART2_IO_BASE 0xe00e0000 +#endif + +#define IDE0_BASE 0x400F0000 +#define IDE0_SIZE 0x200 +#define IDE0_IRQ 25 +#define IIC0_BASE 0x40030000 +#define IIC1_BASE 0x400b0000 +#define OPB0_BASE 0x40000000 +#define GPIO0_BASE 0x40060000 + +#define USB0_IRQ 18 +#define USB0_BASE STB04xxx_MAP_IO_ADDR(0x40010000) +#define USB0_EXTENT 4096 + +#define IIC_NUMS 2 +#define UART_NUMS 3 +#define IIC0_IRQ 9 +#define IIC1_IRQ 10 +#define IIC_OWN 0x55 +#define IIC_CLOCK 50 + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: (u8 *)UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) \ + STD_UART_OP(2) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_DEBUG_IO_BASE UART2_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) \ + STD_UART_OP(2) +#endif + +#if defined(CONFIG_UART0_TTYS2) +#define SERIAL_DEBUG_IO_BASE UART2_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(2) \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#define DCRN_BE_BASE 0x090 +#define DCRN_DMA0_BASE 0x0C0 +#define DCRN_DMA1_BASE 0x0C8 +#define DCRN_DMA2_BASE 0x0D0 +#define DCRN_DMA3_BASE 0x0D8 +#define DCRNCAP_DMA_CC 1 /* have DMA chained count capability */ +#define DCRN_DMASR_BASE 0x0E0 +#define DCRN_PLB0_BASE 0x054 +#define DCRN_PLB1_BASE 0x064 +#define DCRN_POB0_BASE 0x0B0 +#define DCRN_SCCR_BASE 0x120 +#define DCRN_UIC0_BASE 0x040 +#define DCRN_BE_BASE 0x090 +#define DCRN_DMA0_BASE 0x0C0 +#define DCRN_DMA1_BASE 0x0C8 +#define DCRN_DMA2_BASE 0x0D0 +#define DCRN_DMA3_BASE 0x0D8 +#define DCRN_CIC_BASE 0x030 +#define DCRN_DMASR_BASE 0x0E0 +#define DCRN_EBIMC_BASE 0x070 +#define DCRN_DCRX_BASE 0x020 +#define DCRN_CPMFR_BASE 0x102 +#define DCRN_SCCR_BASE 0x120 +#define UIC0 DCRN_UIC0_BASE + +#define IBM_CPM_IIC0 0x80000000 /* IIC 0 interface */ +#define IBM_CPM_USB0 0x40000000 /* IEEE-1284 */ +#define IBM_CPM_IIC1 0x20000000 /* IIC 1 interface */ +#define IBM_CPM_CPU 0x10000000 /* PPC405B3 clock control */ +#define IBM_CPM_AUD 0x08000000 /* Audio Decoder */ +#define IBM_CPM_EBIU 0x04000000 /* External Bus Interface Unit */ +#define IBM_CPM_SDRAM1 0x02000000 /* SDRAM 1 memory controller */ +#define IBM_CPM_DMA 0x01000000 /* DMA controller */ +#define IBM_CPM_DMA1 0x00800000 /* reserved */ +#define IBM_CPM_XPT1 0x00400000 /* reserved */ +#define IBM_CPM_XPT2 0x00200000 /* reserved */ +#define IBM_CPM_UART1 0x00100000 /* Serial 1 / Infrared */ +#define IBM_CPM_UART0 0x00080000 /* Serial 0 / 16550 */ +#define IBM_CPM_EPI 0x00040000 /* DCR Extension */ +#define IBM_CPM_SC0 0x00020000 /* Smart Card 0 */ +#define IBM_CPM_VID 0x00010000 /* reserved */ +#define IBM_CPM_SC1 0x00008000 /* Smart Card 1 */ +#define IBM_CPM_USBSDRA 0x00004000 /* SDRAM 0 memory controller */ +#define IBM_CPM_XPT0 0x00002000 /* Transport - 54 Mhz */ +#define IBM_CPM_CBS 0x00001000 /* Cross Bar Switch */ +#define IBM_CPM_GPT 0x00000800 /* GPTPWM */ +#define IBM_CPM_GPIO0 0x00000400 /* General Purpose IO 0 */ +#define IBM_CPM_DENC 0x00000200 /* Digital video Encoder */ +#define IBM_CPM_TMRCLK 0x00000100 /* CPU timers */ +#define IBM_CPM_XPT27 0x00000080 /* Transport - 27 Mhz */ +#define IBM_CPM_UIC 0x00000040 /* Universal Interrupt Controller */ +#define IBM_CPM_SSP 0x00000010 /* Modem Serial Interface (SSP) */ +#define IBM_CPM_UART2 0x00000008 /* Serial Control Port */ +#define IBM_CPM_DDIO 0x00000004 /* Descrambler */ +#define IBM_CPM_VID2 0x00000002 /* Video Decoder clock domain 2 */ + +#define DFLT_IBM4xx_PM ~(IBM_CPM_CPU | IBM_CPM_EBIU | IBM_CPM_SDRAM1 \ + | IBM_CPM_DMA | IBM_CPM_DMA1 | IBM_CPM_CBS \ + | IBM_CPM_USBSDRA | IBM_CPM_XPT0 | IBM_CPM_TMRCLK \ + | IBM_CPM_XPT27 | IBM_CPM_UIC ) + +#define DCRN_BEAR (DCRN_BE_BASE + 0x0) /* Bus Error Address Register */ +#define DCRN_BESR (DCRN_BE_BASE + 0x1) /* Bus Error Syndrome Register */ +/* DCRN_BESR */ +#define BESR_DSES 0x80000000 /* Data-Side Error Status */ +#define BESR_DMES 0x40000000 /* DMA Error Status */ +#define BESR_RWS 0x20000000 /* Read/Write Status */ +#define BESR_ETMASK 0x1C000000 /* Error Type */ +#define ET_PROT 0 +#define ET_PARITY 1 +#define ET_NCFG 2 +#define ET_BUSERR 4 +#define ET_BUSTO 6 + +#define CHR1_CETE 0x00800000 /* CPU external timer enable */ +#define CHR1_PCIPW 0x00008000 /* PCI Int enable/Peripheral Write enable */ + +#define DCRN_CICCR (DCRN_CIC_BASE + 0x0) /* CIC Control Register */ +#define DCRN_DMAS1 (DCRN_CIC_BASE + 0x1) /* DMA Select1 Register */ +#define DCRN_DMAS2 (DCRN_CIC_BASE + 0x2) /* DMA Select2 Register */ +#define DCRN_CICVCR (DCRN_CIC_BASE + 0x3) /* CIC Video COntro Register */ +#define DCRN_CICSEL3 (DCRN_CIC_BASE + 0x5) /* CIC Select 3 Register */ +#define DCRN_SGPO (DCRN_CIC_BASE + 0x6) /* CIC GPIO Output Register */ +#define DCRN_SGPOD (DCRN_CIC_BASE + 0x7) /* CIC GPIO OD Register */ +#define DCRN_SGPTC (DCRN_CIC_BASE + 0x8) /* CIC GPIO Tristate Ctrl Reg */ +#define DCRN_SGPI (DCRN_CIC_BASE + 0x9) /* CIC GPIO Input Reg */ + +#define DCRN_DCRXICR (DCRN_DCRX_BASE + 0x0) /* Internal Control Register */ +#define DCRN_DCRXISR (DCRN_DCRX_BASE + 0x1) /* Internal Status Register */ +#define DCRN_DCRXECR (DCRN_DCRX_BASE + 0x2) /* External Control Register */ +#define DCRN_DCRXESR (DCRN_DCRX_BASE + 0x3) /* External Status Register */ +#define DCRN_DCRXTAR (DCRN_DCRX_BASE + 0x4) /* Target Address Register */ +#define DCRN_DCRXTDR (DCRN_DCRX_BASE + 0x5) /* Target Data Register */ +#define DCRN_DCRXIGR (DCRN_DCRX_BASE + 0x6) /* Interrupt Generation Register */ +#define DCRN_DCRXBCR (DCRN_DCRX_BASE + 0x7) /* Line Buffer Control Register */ + +#define DCRN_BRCRH0 (DCRN_EBIMC_BASE + 0x0) /* Bus Region Config High 0 */ +#define DCRN_BRCRH1 (DCRN_EBIMC_BASE + 0x1) /* Bus Region Config High 1 */ +#define DCRN_BRCRH2 (DCRN_EBIMC_BASE + 0x2) /* Bus Region Config High 2 */ +#define DCRN_BRCRH3 (DCRN_EBIMC_BASE + 0x3) /* Bus Region Config High 3 */ +#define DCRN_BRCRH4 (DCRN_EBIMC_BASE + 0x4) /* Bus Region Config High 4 */ +#define DCRN_BRCRH5 (DCRN_EBIMC_BASE + 0x5) /* Bus Region Config High 5 */ +#define DCRN_BRCRH6 (DCRN_EBIMC_BASE + 0x6) /* Bus Region Config High 6 */ +#define DCRN_BRCRH7 (DCRN_EBIMC_BASE + 0x7) /* Bus Region Config High 7 */ +#define DCRN_BRCR0 (DCRN_EBIMC_BASE + 0x10) /* BRC 0 */ +#define DCRN_BRCR1 (DCRN_EBIMC_BASE + 0x11) /* BRC 1 */ +#define DCRN_BRCR2 (DCRN_EBIMC_BASE + 0x12) /* BRC 2 */ +#define DCRN_BRCR3 (DCRN_EBIMC_BASE + 0x13) /* BRC 3 */ +#define DCRN_BRCR4 (DCRN_EBIMC_BASE + 0x14) /* BRC 4 */ +#define DCRN_BRCR5 (DCRN_EBIMC_BASE + 0x15) /* BRC 5 */ +#define DCRN_BRCR6 (DCRN_EBIMC_BASE + 0x16) /* BRC 6 */ +#define DCRN_BRCR7 (DCRN_EBIMC_BASE + 0x17) /* BRC 7 */ +#define DCRN_BEAR0 (DCRN_EBIMC_BASE + 0x20) /* Bus Error Address Register */ +#define DCRN_BESR0 (DCRN_EBIMC_BASE + 0x21) /* Bus Error Status Register */ +#define DCRN_BIUCR (DCRN_EBIMC_BASE + 0x2A) /* Bus Interfac Unit Ctrl Reg */ + +#include + +#endif /* __ASM_IBMSTB4_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibmstbx25.c linuxppc-2.4/arch/ppc/platforms/ibmstbx25.c --- linux-2.4.22/arch/ppc/platforms/ibmstbx25.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibmstbx25.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,50 @@ +/* + * + * Copyright 2000-2002 MontaVista Software Inc. + * Completed implementation. + * Current maintainer + * Armin Kuster akuster@mvista.com + * + * Module name: ibmstbx25.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: July 10, 2002 - armin + * initial release + * + * + */ + +#include +#include +#include +#include + +struct ocp_def core_ocp[] = { + {UART, UART0_IO_BASE, UART0_INT,IBM_CPM_UART0}, + {UART, UART1_IO_BASE, UART1_INT, IBM_CPM_UART1}, + {UART, UART2_IO_BASE, UART2_INT, IBM_CPM_UART2}, + {IIC, IIC0_BASE, IIC0_IRQ, IBM_CPM_IIC0}, + {GPIO, GPIO0_BASE, OCP_IRQ_NA, IBM_CPM_GPIO0}, + {OCP_NULL_TYPE, 0x0, OCP_IRQ_NA, OCP_CPM_NA}, + +}; diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/ibmstbx25.h linuxppc-2.4/arch/ppc/platforms/ibmstbx25.h --- linux-2.4.22/arch/ppc/platforms/ibmstbx25.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/ibmstbx25.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,287 @@ +/* + * ibmstbx25.h + * + * Current maintainer + * Armin Kuster akuster@mvista.com + * July, 2002 + * + * + * Copyright 2002 MontaVista Softare Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Version 1.0 July 10, 2002 - A. Kuster + * Initial version + * + * Version 1.1 July 24, 02 - Armin + * added Power Managment defines + * + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_IBMSTBX25_H__ +#define __ASM_IBMSTBX25_H__ + +#include +#include + +/* serial port defines */ +#define STBx25xx_IO_BASE ((uint)0xe0000000) +#define PPC4xx_ONB_IO_PADDR STBx25xx_IO_BASE +#define PPC4xx_ONB_IO_VADDR ((uint)0xe0000000) +#define PPC4xx_ONB_IO_SIZE ((uint)14*64*1024) + +/* + * map STBxxxx internal i/o address (0x400x00xx) to an address + * which is below the 2GB limit... + * + * 4000 000x uart1 -> 0xe000 000x + * 4001 00xx uart2 + * 4002 00xx smart card + * 4003 000x iic + * 4004 000x uart0 + * 4005 0xxx timer + * 4006 00xx gpio + * 4007 00xx smart card + * 400b 000x iic + * 400c 000x scp + * 400d 000x modem + * 400e 000x uart2 +*/ +#define STBx25xx_MAP_IO_ADDR(a) (((uint)(a)) + (STBx25xx_IO_BASE - 0x40000000)) + +#define RS_TABLE_SIZE 3 + +#ifdef __BOOTER__ +#define UART1_IO_BASE 0x40000000 +#define UART2_IO_BASE 0x40010000 +#else +#define UART1_IO_BASE 0xe0000000 +#define UART2_IO_BASE 0xe0010000 +#endif +#define SC0_BASE 0x40020000 /* smart card #0 */ +#define IIC0_BASE 0x40030000 +#ifdef __BOOTER__ +#define UART0_IO_BASE 0x40040000 +#else +#define UART0_IO_BASE 0xe0040000 +#endif +#define SCC0_BASE 0x40040000 /* Serial 0 controller IrdA */ +#define GPT0_BASE 0x40050000 /* General purpose timers */ +#define OPB0_BASE 0x40000000 +#define GPIO0_BASE 0x40060000 +#define SC0_base 0x40070000 /* smart card #1 */ +#define SCP0_base 0x400C0000 /* Serial Controller Port */ +#define SSP0_BASE 0x400D0000 /* Sync serial port */ + +#define REDWOOD_IDE_CMD 0xf0100000 +#define REDWOOD_IDE_CTRL 0xf1100000 + +#define RTCFPC_IRQ 0 +#define XPORT_IRQ 1 +#define AUD_IRQ 2 +#define AID_IRQ 3 +#define DMA0 4 +#define DMA1_IRQ 5 +#define DMA2_IRQ 6 +#define DMA3_IRQ 7 +#define SC0_IRQ 8 +#define IIC0_IRQ 9 +#define IIR0_IRQ 10 +#define GPT0_IRQ 11 +#define GPT1_IRQ 12 +#define SCP0_IRQ 13 +#define SSP0_IRQ 14 +#define GPT2_IRQ 15 /* count down timer */ +#define SC1_IRQ 16 +/* IRQ 17 - 19 external */ +#define UART0_INT 20 +#define UART1_INT 21 +#define UART2_INT 22 +#define XPTDMA_IRQ 23 +#define DCRIDE_IRQ 24 +/* IRQ 25 - 30 external */ +#define IDE0_IRQ 26 + +#define IIC_NUMS 1 +#define UART_NUMS 3 +#define IIC_OWN 0x55 +#define IIC_CLOCK 50 + +#define BD_EMAC_ADDR(e,i) bi_enetaddr[i] + +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, UART##num##_INT, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: (u8 *)UART##num##_IO_BASE, \ + io_type: SERIAL_IO_MEM}, + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_DEBUG_IO_BASE UART0_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(0) \ + STD_UART_OP(1) \ + STD_UART_OP(2) +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_DEBUG_IO_BASE UART2_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(1) \ + STD_UART_OP(0) \ + STD_UART_OP(2) +#endif + +#if defined(CONFIG_UART0_TTYS2) +#define SERIAL_DEBUG_IO_BASE UART2_IO_BASE +#define SERIAL_PORT_DFNS \ + STD_UART_OP(2) \ + STD_UART_OP(0) \ + STD_UART_OP(1) +#endif + +#define DCRN_BE_BASE 0x090 +#define DCRN_DMA0_BASE 0x0C0 +#define DCRN_DMA1_BASE 0x0C8 +#define DCRN_DMA2_BASE 0x0D0 +#define DCRN_DMA3_BASE 0x0D8 +#define DCRNCAP_DMA_CC 1 /* have DMA chained count capability */ +#define DCRN_DMASR_BASE 0x0E0 +#define DCRN_PLB0_BASE 0x054 +#define DCRN_PLB1_BASE 0x064 +#define DCRN_POB0_BASE 0x0B0 +#define DCRN_SCCR_BASE 0x120 +#define DCRN_UIC0_BASE 0x040 +#define DCRN_BE_BASE 0x090 +#define DCRN_DMA0_BASE 0x0C0 +#define DCRN_DMA1_BASE 0x0C8 +#define DCRN_DMA2_BASE 0x0D0 +#define DCRN_DMA3_BASE 0x0D8 +#define DCRN_CIC_BASE 0x030 +#define DCRN_DMASR_BASE 0x0E0 +#define DCRN_EBIMC_BASE 0x070 +#define DCRN_DCRX_BASE 0x020 +#define DCRN_CPMFR_BASE 0x102 +#define DCRN_SCCR_BASE 0x120 +#define DCRN_RTCFP_BASE 0x310 + +#define UIC0 DCRN_UIC0_BASE + +#define IBM_CPM_IIC0 0x80000000 /* IIC 0 interface */ +#define IBM_CPM_CPU 0x10000000 /* PPC405B3 clock control */ +#define IBM_CPM_AUD 0x08000000 /* Audio Decoder */ +#define IBM_CPM_EBIU 0x04000000 /* External Bus Interface Unit */ +#define IBM_CPM_IRR 0x02000000 /* Infrared reciever */ +#define IBM_CPM_DMA 0x01000000 /* DMA controller */ +#define IBM_CPM_UART2 0x00200000 /* Serial Control Port */ +#define IBM_CPM_UART1 0x00100000 /* Serial 1 / Infrared */ +#define IBM_CPM_UART0 0x00080000 /* Serial 0 / 16550 */ +#define IBM_PM_DCRIDE 0x00040000 /* DCR timeout & IDE line Mode clock */ +#define IBM_CPM_SC0 0x00020000 /* Smart Card 0 */ +#define IBM_CPM_VID 0x00010000 /* reserved */ +#define IBM_CPM_SC1 0x00008000 /* Smart Card 0 */ +#define IBM_CPM_XPT0 0x00002000 /* Transport - 54 Mhz */ +#define IBM_CPM_CBS 0x00001000 /* Cross Bar Switch */ +#define IBM_CPM_GPT 0x00000800 /* GPTPWM */ +#define IBM_CPM_GPIO0 0x00000400 /* General Purpose IO 0 */ +#define IBM_CPM_DENC 0x00000200 /* Digital video Encoder */ +#define IBM_CPM_C405T 0x00000100 /* CPU timers */ +#define IBM_CPM_XPT27 0x00000080 /* Transport - 27 Mhz */ +#define IBM_CPM_UIC 0x00000040 /* Universal Interrupt Controller */ +#define IBM_CPM_RTCFPC 0x00000020 /* Realtime clock and front panel */ +#define IBM_CPM_SSP 0x00000010 /* Modem Serial Interface (SSP) */ +#define IBM_CPM_VID2 0x00000002 /* Video Decoder clock domain 2 */ +#define DFLT_IBM4xx_PM ~(IBM_CPM_CPU | IBM_CPM_EBIU | IBM_CPM_DMA \ + | IBM_CPM_CBS | IBM_CPM_XPT0 | IBM_CPM_C405T \ + | IBM_CPM_XPT27 | IBM_CPM_UIC) + +#define DCRN_BEAR (DCRN_BE_BASE + 0x0) /* Bus Error Address Register */ +#define DCRN_BESR (DCRN_BE_BASE + 0x1) /* Bus Error Syndrome Register */ +/* DCRN_BESR */ +#define BESR_DSES 0x80000000 /* Data-Side Error Status */ +#define BESR_DMES 0x40000000 /* DMA Error Status */ +#define BESR_RWS 0x20000000 /* Read/Write Status */ +#define BESR_ETMASK 0x1C000000 /* Error Type */ +#define ET_PROT 0 +#define ET_PARITY 1 +#define ET_NCFG 2 +#define ET_BUSERR 4 +#define ET_BUSTO 6 + +#define CHR1_CETE 0x00800000 /* CPU external timer enable */ +#define CHR1_PCIPW 0x00008000 /* PCI Int enable/Peripheral Write enable */ + +#define DCRN_CICCR (DCRN_CIC_BASE + 0x0) /* CIC Control Register */ +#define DCRN_DMAS1 (DCRN_CIC_BASE + 0x1) /* DMA Select1 Register */ +#define DCRN_DMAS2 (DCRN_CIC_BASE + 0x2) /* DMA Select2 Register */ +#define DCRN_CICVCR (DCRN_CIC_BASE + 0x3) /* CIC Video COntro Register */ +#define DCRN_CICSEL3 (DCRN_CIC_BASE + 0x5) /* CIC Select 3 Register */ +#define DCRN_SGPO (DCRN_CIC_BASE + 0x6) /* CIC GPIO Output Register */ +#define DCRN_SGPOD (DCRN_CIC_BASE + 0x7) /* CIC GPIO OD Register */ +#define DCRN_SGPTC (DCRN_CIC_BASE + 0x8) /* CIC GPIO Tristate Ctrl Reg */ +#define DCRN_SGPI (DCRN_CIC_BASE + 0x9) /* CIC GPIO Input Reg */ + +#define DCRN_DCRXICR (DCRN_DCRX_BASE + 0x0) /* Internal Control Register */ +#define DCRN_DCRXISR (DCRN_DCRX_BASE + 0x1) /* Internal Status Register */ +#define DCRN_DCRXECR (DCRN_DCRX_BASE + 0x2) /* External Control Register */ +#define DCRN_DCRXESR (DCRN_DCRX_BASE + 0x3) /* External Status Register */ +#define DCRN_DCRXTAR (DCRN_DCRX_BASE + 0x4) /* Target Address Register */ +#define DCRN_DCRXTDR (DCRN_DCRX_BASE + 0x5) /* Target Data Register */ +#define DCRN_DCRXIGR (DCRN_DCRX_BASE + 0x6) /* Interrupt Generation Register */ +#define DCRN_DCRXBCR (DCRN_DCRX_BASE + 0x7) /* Line Buffer Control Register */ + +#define DCRN_BRCRH0 (DCRN_EBIMC_BASE + 0x0) /* Bus Region Config High 0 */ +#define DCRN_BRCRH1 (DCRN_EBIMC_BASE + 0x1) /* Bus Region Config High 1 */ +#define DCRN_BRCRH2 (DCRN_EBIMC_BASE + 0x2) /* Bus Region Config High 2 */ +#define DCRN_BRCRH3 (DCRN_EBIMC_BASE + 0x3) /* Bus Region Config High 3 */ +#define DCRN_BRCRH4 (DCRN_EBIMC_BASE + 0x4) /* Bus Region Config High 4 */ +#define DCRN_BRCRH5 (DCRN_EBIMC_BASE + 0x5) /* Bus Region Config High 5 */ +#define DCRN_BRCRH6 (DCRN_EBIMC_BASE + 0x6) /* Bus Region Config High 6 */ +#define DCRN_BRCRH7 (DCRN_EBIMC_BASE + 0x7) /* Bus Region Config High 7 */ +#define DCRN_BRCR0 (DCRN_EBIMC_BASE + 0x10) /* BRC 0 */ +#define DCRN_BRCR1 (DCRN_EBIMC_BASE + 0x11) /* BRC 1 */ +#define DCRN_BRCR2 (DCRN_EBIMC_BASE + 0x12) /* BRC 2 */ +#define DCRN_BRCR3 (DCRN_EBIMC_BASE + 0x13) /* BRC 3 */ +#define DCRN_BRCR4 (DCRN_EBIMC_BASE + 0x14) /* BRC 4 */ +#define DCRN_BRCR5 (DCRN_EBIMC_BASE + 0x15) /* BRC 5 */ +#define DCRN_BRCR6 (DCRN_EBIMC_BASE + 0x16) /* BRC 6 */ +#define DCRN_BRCR7 (DCRN_EBIMC_BASE + 0x17) /* BRC 7 */ +#define DCRN_BEAR0 (DCRN_EBIMC_BASE + 0x20) /* Bus Error Address Register */ +#define DCRN_BESR0 (DCRN_EBIMC_BASE + 0x21) /* Bus Error Status Register */ +#define DCRN_BIUCR (DCRN_EBIMC_BASE + 0x2A) /* Bus Interfac Unit Ctrl Reg */ + +#define DCRN_RTC_FPC0_CNTL (DCRN_RTCFP_BASE + 0x00) /* RTC cntl */ +#define DCRN_RTC_FPC0_INT (DCRN_RTCFP_BASE + 0x01) /* RTC Interrupt */ +#define DCRN_RTC_FPC0_TIME (DCRN_RTCFP_BASE + 0x02) /* RTC time reg */ +#define DCRN_RTC_FPC0_ALRM (DCRN_RTCFP_BASE + 0x03) /* RTC Alarm reg */ +#define DCRN_RTC_FPC0_D1 (DCRN_RTCFP_BASE + 0x04) /* LED Data 1 */ +#define DCRN_RTC_FPC0_D2 (DCRN_RTCFP_BASE + 0x05) /* LED Data 2 */ +#define DCRN_RTC_FPC0_D3 (DCRN_RTCFP_BASE + 0x06) /* LED Data 3 */ +#define DCRN_RTC_FPC0_D4 (DCRN_RTCFP_BASE + 0x07) /* LED Data 4 */ +#define DCRN_RTC_FPC0_D5 (DCRN_RTCFP_BASE + 0x08) /* LED Data 5 */ +#define DCRN_RTC_FPC0_FCNTL (DCRN_RTCFP_BASE + 0x09) /* LED control */ +#define DCRN_RTC_FPC0_BRT (DCRN_RTCFP_BASE + 0x0A) /* Brightness cntl */ + +#include + +#endif /* __ASM_IBMSTBX25_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/rainier.c linuxppc-2.4/arch/ppc/platforms/rainier.c --- linux-2.4.22/arch/ppc/platforms/rainier.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/rainier.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,228 @@ +/* + * + * + * Copyright 2000-2002 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * akuster@mvista.com + * + * Module name: rainier.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * IBM PowerPC 4xx based boards. Adapted from original + * code from walnut.c + * + * Revision: + * 1.0 - armin + * + * History: + * 02/05/02 - initial work + * + * Please read the COPYING file for all license details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +void *rainier_rtc_base; +unsigned int rainier_io_page; + +void +*locate_rainier_io(void) +{ + unsigned int temp; + + temp = in_le32((void*)PPC405_PCI_CONFIG_ADDR) & PCI_CONFIG_ADDR_MASK; + out_le32((void*)PPC405_PCI_CONFIG_ADDR, + temp | PCI_CONFIG_CYCLE_ENABLE |PCI_BASE_ADDRESS_2); + temp = in_le32((void*)PPC405_PCI_CONFIG_DATA); + + if (temp == (PCI_BASE_ADDRESS_MEM_CARD2 | PCI_BASE_ADDRESS_MEM_PREFETCH)) + return PPC_405RAINIER2_IO_PAGE; + else + return PPC_405RAINIER1_IO_PAGE; +} + +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {28, 28, 28, 28}, /* IDSEL 1 - PCI slot 1 */ + {29, 29, 29, 29}, /* IDSEL 2 - PCI slot 2 */ + {30, 30, 30, 30}, /* IDSEL 3 - PCI slot 3 */ + {31, 31, 31, 31}, /* IDSEL 4 - PCI slot 4 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +void __init +board_setup_arch(void) +{ + struct serial_struct serialreq = {0}; + + serialreq.iomem_base = (void*)PPC405_UART0_IO_BASE; + serialreq.iomem_base += rainier_io_page; + serialreq.baud_base = BASE_BAUD; + serialreq.irq = PPC405_UART0_INT; + serialreq.flags = STD_COM_FLAGS; + serialreq.io_type = SERIAL_IO_MEM; + + early_serial_setup(&serialreq); + + /* RTC step for the rainier */ + rainier_rtc_base = (void *) WALNUT_RTC_VADDR; + TODC_INIT(TODC_TYPE_DS1743, rainier_rtc_base, rainier_rtc_base, + rainier_rtc_base, 8); +} + +void __init +bios_fixup(struct pci_controller *hose, struct pcil0_regs *pcip) +{ + /* + * Expected PCI mapping: + * + * PLB addr PCI memory addr + * --------------------- --------------------- + * 0000'0000 - 7fff'ffff <--- 0000'0000 - 7fff'ffff + * 8000'0000 - Bfff'ffff ---> 8000'0000 - Bfff'ffff + * + * PLB addr PCI io addr + * --------------------- --------------------- + * e800'0000 - e800'ffff ---> 0000'0000 - 0001'0000 + * + * The following code is simplified by assuming that the bootrom + * has been well behaved in following this mapping. + */ + +#ifdef DEBUG + int i; + + printk("ioremap PCLIO_BASE = 0x%x\n", pcip); + printk("PCI bridge regs before fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif + + /* Disable region first */ + out_le32((void *) &(pcip->pmm[0].ma), 0x00000000); + /* PLB starting addr, PCI: 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].la), 0x80000000); + /* PCI start addr, 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE); + /* 512MB range of PLB to PCI */ + out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000); + /* Enable no pre-fetch, enable region */ + out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff - + (PPC405_PCI_UPPER_MEM - + PPC405_PCI_MEM_BASE)) | 0x01)); + + /*region one used bu rainier*/ + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[1].la), 0x80000000); + out_le32((void *) &(pcip->pmm[1].pcila), 0x80000000); + out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[1].ma), 0xFFFF8001); + out_le32((void *) &(pcip->ptm1ms), 0x00000000); + + /* Disable region two */ + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[2].la), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + + /* end work arround */ + +#ifdef DEBUG + printk("PCI bridge regs after fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif +} + +void __init +board_io_mapping(void) +{ + io_block_mapping(RAINIER_IO_PAGE_INTERPOSER_PADDR, + RAINIER_IO_PAGE_INTERPOSER_VADDR,PAGE_SIZE , _PAGE_IO); + + io_block_mapping(RAINIER_IO_PAGE_PCI_PADDR, + RAINIER_IO_PAGE_PCI_VADDR,PAGE_SIZE , _PAGE_IO); + + io_block_mapping(RAINIER_RTC_VADDR, + RAINIER_RTC_PADDR, RAINIER_RTC_SIZE, _PAGE_IO); + + rainier_io_page = locate_rainier_io(); + + io_block_mapping(rainier_io_page , + rainier_io_page , PAGE_SIZE, _PAGE_IO); + +} + +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ + ppc_md.time_init = m48t3x_time_init; + ppc_md.set_rtc_time = m48t3x_set_rtc_time; + ppc_md.get_rtc_time = m48t3x_get_rtc_time; + ppc_md.time_init = todc_time_init; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/rainier.h linuxppc-2.4/arch/ppc/platforms/rainier.h --- linux-2.4.22/arch/ppc/platforms/rainier.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/rainier.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,59 @@ +/* + * + * + * Copyright 2002 MontaVista Software Inc. + * NP405GS modifications + * Author: MontaVista Software, Inc. + * Akuster@mvista.com or source@mvista.com + * + * Module name: rainier.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * Network processor based boards. + * + * This includes: + * + * NP405GS "Rainier" evaluation board + * + * Please read the COPYING file for all license details. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_RAINIER_H__ +#define __ASM_RAINIER_H__ + +/* We have a N405GS core */ +#include + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained + * manuals since the rainer uses vxworks + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[6]; /* Local Ethernet MAC address */ + unsigned char bi_pci_enetaddr[6]; /* PCI Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ +} bd_t; + +#define bi_tbfreq bi_intfreq + +extern void *rainer_rtc_base; +#define RAINIER_RTC_PADDR ((uint)0xf0000000) +#define RAINIER_RTC_VADDR RAINIER_RTC_PADDR +#define RAINIER_RTC_SIZE ((uint)8*1024) + +#define BASE_BAUD 115200 + +#define PPC4xx_MACHINE_NAME "IBM Rainier" + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_RAINIER_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/redwood.c linuxppc-2.4/arch/ppc/platforms/redwood.c --- linux-2.4.22/arch/ppc/platforms/redwood.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/redwood.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,67 @@ +/* + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Author: MontaVista Software, Inc. + * Frank Rowand + * + * Module name: redwood.c + * + * Description: + * + * History: 11/09/2001 - Armin + * added board_init to add in additional instuctions needed during platfrom_init + * + * Please read the COPYING file for all license details. + */ + +#include +#include +#include +#include +#include +#include + +void __init +board_setup_arch(void) +{ +} + +void __init +board_io_mapping(void) +{ + int i; + + io_block_mapping(OAKNET_IO_VADDR, + OAKNET_IO_PADDR, OAKNET_IO_SIZE, _PAGE_IO); + +} + +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ +} + +/* hack; blame me dan. -brad */ +#ifdef CONFIG_INPUT_KEYBDEV + +void +handle_scancode(unsigned char scancode, int down) +{ + printk("handle_scancode(scancode=0x%x, down=%d)\n", scancode, down); +} + +static void +kbd_bh(unsigned long dummy) +{ +} + +DECLARE_TASKLET_DISABLED(keyboard_tasklet, kbd_bh, 0); +void (*kbd_ledfunc) (unsigned int led); + +#endif diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/redwood.h linuxppc-2.4/arch/ppc/platforms/redwood.h --- linux-2.4.22/arch/ppc/platforms/redwood.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/redwood.h 2003-07-25 02:00:01.000000000 +1000 @@ -0,0 +1,60 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * PPC405 modifications + * Author: MontaVista Software, Inc. + * frank_rowand@mvista.com or source@mvista.com + * + * Module name: redwood.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * STB03xxx "Redwood" evaluation board. + * + * Please read the COPYING file for all license details. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_REDWOOD_H__ +#define __ASM_REDWOOD_H__ + +/* Redwoods have an STB03xxx or STB04xxx core */ +#include + +#ifndef __ASSEMBLY__ +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned int bi_dummy; /* field shouldn't exist */ + unsigned char bi_enetaddr[6]; /* Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* Bus speed, in Hz */ + unsigned int bi_opb_busfreq; /* OPB Bus speed, in Hz */ + int bi_iic_fast[2]; /* Use fast i2c mode */ +} bd_t; +#define bi_tbfreq bi_intfreq + +#endif /* !__ASSEMBLY__ */ + +#define bi_tbfreq bi_intfreq +#define OAKNET_IO_PADDR ((uint)0xf2000000) +#define OAKNET_IO_VADDR OAKNET_IO_PADDR +#define OAKNET_IO_BASE OAKNET_IO_VADDR + +/* ftr revisit- io size was 0xffff in old-line, is 0x40 in oak.h */ +#define OAKNET_IO_SIZE 0xffff +#define OAKNET_INT 26 /* EXTINT1 */ + +#undef MAX_HWIFS +#define MAX_HWIFS 1 + +#define _IO_BASE 0 +#define _ISA_MEM_BASE 0 +#define PCI_DRAM_OFFSET 0 + +#define BASE_BAUD (378000000 / 18 / 16) + +#define PPC4xx_MACHINE_NAME "IBM Redwood" + +#endif /* __ASM_REDWOOD_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/redwood5.c linuxppc-2.4/arch/ppc/platforms/redwood5.c --- linux-2.4.22/arch/ppc/platforms/redwood5.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/redwood5.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,82 @@ +/* + * + * Copyright 2000-2001 MontaVista Software Inc. + * Completed implementation. + * Author: Armin Kuster + * + * Module name: redwood5.c + * + * Description: + * IBM redwood5 eval board file + * + * History: 12/29/2001 - Armin + * initail release + * + * Please read the COPYING file for all license details. + */ + +#include +#include +#include +#include +#include + +void __init +board_setup_arch(void) +{ + + bd_t *bip = (bd_t *)__res; + +#ifdef CONFIG_DEBUG_BRINGUP + printk("\n"); + printk("machine\t: %s\n", PPC4xx_MACHINE_NAME); + printk("\n"); + printk("bi_s_version\t %s\n", bip->bi_s_version); + printk("bi_r_version\t %s\n", bip->bi_r_version); + printk("bi_memsize\t 0x%8.8x\t %dMBytes\n", bip->bi_memsize,bip->bi_memsize/(1024*1000)); + printk("bi_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", 0, + bip->bi_enetaddr[0], bip->bi_enetaddr[1], + bip->bi_enetaddr[2], bip->bi_enetaddr[3], + bip->bi_enetaddr[4], bip->bi_enetaddr[5]); + + printk("bi_intfreq\t 0x%8.8x\t clock:\t %dMhz\n", + bip->bi_intfreq, bip->bi_intfreq/ 1000000); + + printk("bi_busfreq\t 0x%8.8x\t plb bus clock:\t %dMHz\n", + bip->bi_busfreq, bip->bi_busfreq / 1000000 ); + printk("bi_tbfreq\t 0x%8.8x\t TB freq:\t %dMHz\n", + bip->bi_tbfreq, bip->bi_tbfreq/1000000); + + printk("\n"); +#endif + +} + +void __init +board_io_mapping(void) +{ + int i; + + for (i = 0; i < 16; i++) { + unsigned long v, p; + + /* 0x400x0000 -> 0xe00x0000 */ + p = 0x40000000 | (i << 16); + v = STB04xxx_IO_BASE | (i << 16); + + io_block_mapping(v, p, PAGE_SIZE, + _PAGE_NO_CACHE | pgprot_val(PAGE_KERNEL) | _PAGE_GUARDED); + } + + +} + +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/redwood5.h linuxppc-2.4/arch/ppc/platforms/redwood5.h --- linux-2.4.22/arch/ppc/platforms/redwood5.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/redwood5.h 2003-07-25 02:00:01.000000000 +1000 @@ -0,0 +1,56 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * PPC405 modifications + * Author: MontaVista Software, Inc. + * Armin Kuster + * + * Module name: redwood5.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * STB03xxx "Redwood" evaluation board. + * + * Please read the COPYING file for all license details. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_REDWOOD5_H__ +#define __ASM_REDWOOD5_H__ + +/* Redwood5 has an STB04xxx core */ +#include + +#ifndef __ASSEMBLY__ +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned int bi_dummy; /* field shouldn't exist */ + unsigned char bi_enetaddr[6]; /* Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* Bus speed, in Hz */ + unsigned int bi_tbfreq; /* Software timebase freq */ + unsigned int bi_opb_busfreq; /* OPB Bus speed, in Hz */ + int bi_iic_fast[2]; /* Use fast i2c mode */ +} bd_t; +#endif /* !__ASSEMBLY__ */ + + +#define SMC91111_BASE_ADDR 0xf2000300 +#define SMC91111_IRQ 28 + +#ifdef MAX_HWIFS +#undef MAX_HWIFS +#endif +#define MAX_HWIFS 1 + +#define _IO_BASE 0 +#define _ISA_MEM_BASE 0 +#define PCI_DRAM_OFFSET 0 + +#define BASE_BAUD (378000000 / 18 / 16) + +#define PPC4xx_MACHINE_NAME "IBM Redwood5" + +#endif /* __ASM_REDWOOD5_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/redwood6.c linuxppc-2.4/arch/ppc/platforms/redwood6.c --- linux-2.4.22/arch/ppc/platforms/redwood6.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/redwood6.c 2003-06-19 16:04:49.000000000 +1000 @@ -0,0 +1,174 @@ +/* + * + * Module name: redwood6.c + * + * Description: + * IBM redwood6 eval board file + * + * Author: Armin Kuster + * Copyright 2002 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: July 10, 2002 - Armin + * initial release + * + * : July 16, 2002 - Armin + * added xilinx init code for ide interface + * added IRQ table + * + */ + +#include +#include +#include +#include +#include +#include +#include + + +/* + * Define all of the IRQ senses and polarities. + */ + +static u_char redwood6_IRQ_initsenses[] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 0: RTC/FPC */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 1: Transport */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 2: Audio Dec */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 3: Video Dec */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 4: DMA Chan 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 5: DMA Chan 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 6: DMA Chan 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 7: DMA Chan 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 8: SmartCard 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 9: IIC0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 10: IRR */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 11: Cap Timers */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 12: Cmp Timers */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 13: Serial Port */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 14: Soft Modem */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 15: Down Ctrs */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 16: SmartCard 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 17: Ext Int 7 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 18: Ext Int 8 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 19: Ext Int 9 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 20: Serial 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 21: Serial 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 22: Serial 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 23: XPT_DMA */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 24: DCR timeout */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 25: Ext Int 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 26: Ext Int 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 27: Ext Int 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 28: Ext Int 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 29: Ext Int 4 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 30: Ext Int 5 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 31: Ext Int 6 */ +}; + + +void __init +board_setup_arch(void) +{ +#ifdef CONFIG_IDE + void *xilinx, *xilinx_1, *xilinx_2; + unsigned short us_reg5; + + xilinx = (unsigned long) ioremap(IDE_XLINUX_MUX_BASE, 0x10); + /* init xilinx control registers - enable ide mux, clear reset bit */ + if (!xilinx) { + printk(KERN_CRIT + "redwood6_setup_arch() xilinxi ioremap failed\n"); + return; + } + xilinx_1 = xilinx + 0xa; + xilinx_2 = xilinx + 0xe; + + us_reg5 = readb(xilinx_1); + writeb(0x01d1, xilinx_1); + writeb(0x0008, xilinx_2); + + udelay(10 * 1000); + + writeb(0x01d1, xilinx_1); + writeb(0x0008, xilinx_2); +#endif + +#ifdef DEBUG_BRINGUP + bd_t *bip = (bd_t *) __res; + printk("\n"); + printk("machine\t: %s\n", PPC4xx_MACHINE_NAME); + printk("\n"); + printk("bi_s_version\t %s\n", bip->bi_s_version); + printk("bi_r_version\t %s\n", bip->bi_r_version); + printk("bi_memsize\t 0x%8.8x\t %dMBytes\n", bip->bi_memsize, + bip->bi_memsize / (1024 * 1000)); + printk("bi_enetaddr %d\t %2.2x%2.2x%2.2x-%2.2x%2.2x%2.2x\n", 0, + bip->bi_enetaddr[0], bip->bi_enetaddr[1], bip->bi_enetaddr[2], + bip->bi_enetaddr[3], bip->bi_enetaddr[4], bip->bi_enetaddr[5]); + + printk("bi_intfreq\t 0x%8.8x\t clock:\t %dMhz\n", + bip->bi_intfreq, bip->bi_intfreq / 1000000); + + printk("bi_busfreq\t 0x%8.8x\t plb bus clock:\t %dMHz\n", + bip->bi_busfreq, bip->bi_busfreq / 1000000); + printk("bi_tbfreq\t 0x%8.8x\t TB freq:\t %dMHz\n", + bip->bi_tbfreq, bip->bi_tbfreq / 1000000); + + printk("\n"); +#endif + ibm4xxPIC_InitSenses = redwood6_IRQ_initsenses; + ibm4xxPIC_NumInitSenses = sizeof(redwood6_IRQ_initsenses); + + /* Identify the system */ + printk(KERN_INFO "IBM Redwood6 (STBx25XX) Platform\n"); + printk(KERN_INFO + "Port by MontaVista Software, Inc. (source@mvista.com)\n"); +} + +void __init +board_io_mapping(void) +{ + int i; + + for (i = 0; i < 16; i++) { + unsigned long v, p; + + /* 0x400x0000 -> 0xe00x0000 */ + p = 0x40000000 | (i << 16); + v = STBx25xx_IO_BASE | (i << 16); + + io_block_mapping(v, p, PAGE_SIZE, + _PAGE_NO_CACHE | pgprot_val(PAGE_KERNEL) | + _PAGE_GUARDED); + } +} + +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/redwood6.h linuxppc-2.4/arch/ppc/platforms/redwood6.h --- linux-2.4.22/arch/ppc/platforms/redwood6.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/redwood6.h 2003-07-25 02:00:01.000000000 +1000 @@ -0,0 +1,78 @@ +/* + * Author: MontaVista Software, Inc. + * Armin Kuster + * + * Module name: redwood6.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * STBx25xx "Redwood6" evaluation board. + * + * Copyright 2002 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * Armin Kuster + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_REDWOOD5_H__ +#define __ASM_REDWOOD5_H__ + +/* Redwood6 has an STBx25xx core */ +#include + +#ifndef __ASSEMBLY__ +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned int bi_dummy; /* field shouldn't exist */ + unsigned char bi_enetaddr[6]; /* Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* Bus speed, in Hz */ + unsigned int bi_tbfreq; /* Software timebase freq */ + unsigned int bi_opb_busfreq; /* OPB Bus speed, in Hz */ + int bi_iic_fast[1]; /* Use fast i2c mode */ +} bd_t; +#endif /* !__ASSEMBLY__ */ + +#define SMC91111_BASE_ADDR 0xf2030300 +#define SMC91111_IRQ 27 +#define IDE_XLINUX_MUX_BASE 0xf2040000 +#define IDE_DMA_ADDR 0xfce00000 + +#ifdef MAX_HWIFS +#undef MAX_HWIFS +#endif +#define MAX_HWIFS 1 + +#define _IO_BASE 0 +#define _ISA_MEM_BASE 0 +#define PCI_DRAM_OFFSET 0 + +#define BASE_BAUD (378000000 / 18 / 16) + +#define PPC4xx_MACHINE_NAME "IBM Redwood6" + +#endif /* __ASM_REDWOOD5_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/rpxclassic.h linuxppc-2.4/arch/ppc/platforms/rpxclassic.h --- linux-2.4.22/arch/ppc/platforms/rpxclassic.h 2003-02-15 08:07:09.000000000 +1100 +++ linuxppc-2.4/arch/ppc/platforms/rpxclassic.h 2003-03-21 08:08:33.000000000 +1100 @@ -9,6 +9,7 @@ #define __MACH_RPX_DEFS #include +#include #ifndef __ASSEMBLY__ /* A Board Information structure that is given to a program when @@ -70,10 +71,6 @@ #define BCSR2_QSPACESEL ((uint)0x00004000) #define BCSR2_FETHLEDMODE ((uint)0x00000800) /* CLLF */ -#if defined(CONFIG_HTDMSOUND) -#include -#endif - /* define IO_BASE for pcmcia, CLLF only */ #if !defined(CONFIG_PCI) #define _IO_BASE 0x80000000 diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/rpxlite.h linuxppc-2.4/arch/ppc/platforms/rpxlite.h --- linux-2.4.22/arch/ppc/platforms/rpxlite.h 2003-02-15 08:07:09.000000000 +1100 +++ linuxppc-2.4/arch/ppc/platforms/rpxlite.h 2003-03-21 08:08:33.000000000 +1100 @@ -9,6 +9,7 @@ #define __MACH_RPX_DEFS #include +#include #ifndef __ASSEMBLY__ /* A Board Information structure that is given to a program when @@ -57,10 +58,6 @@ #define BCSR1_PCVCTL5 ((uint)0x00040000) #define BCSR1_PCVCTL6 ((uint)0x00020000) #define BCSR1_PCVCTL7 ((uint)0x00010000) - -#if defined(CONFIG_HTDMSOUND) -#include -#endif #endif /* !__ASSEMBLY__ */ /* define IO_BASE for pcmcia */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/subzero.c linuxppc-2.4/arch/ppc/platforms/subzero.c --- linux-2.4.22/arch/ppc/platforms/subzero.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/subzero.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,96 @@ +/* + * arch/ppc/platforms/subzero.c Platform setup for the IBM Subzero CPU core card. + * + * Based on arctic1.c by Ken Inoue, which + * was based on beech.c by Bishop Brock + * + * The source code contained herein is licensed under the IBM Public License + * Version 1.0, which has been approved by the Open Source Initiative. + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * David Gibson + * IBM OzLabs, Canberra, Australia + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + Subzero core card physical memory map: + + Main Memory (Initialized by the BIOS) + ======================================================================= + + SDRAM (32 MB) 0x00000000 - 0x02000000 + + OPB Space: (Mapped virtual = physical in ppc4xx_setup.c) + ======================================================================= + + UART0 0xEF600300 + UART1 0xEF600400 + IIC 0xEF600500 + OPB Arbiter 0xEF600600 + GPIO Controller 0xEF600700 + CODEC Interface 0xEF600900 + Touch Panel Controller 0xEF600A00 + DES Controller 0xEF600B00 + + + EBC Space: (Mapped virtual = physical in board_io_mapping()) + (EBC setup for personality cards left to individual card setups) + Space EBC Bank Physical Addresses EBC Base Address + ========================================================================= + Boot/Linux Flash 0 FF000000 - FFFFFFFF FF000000 (16MB) + +*/ + + +/**************************************************************************** + * EBC Setup + ****************************************************************************/ + +/* The EBC is set up for Arctic1. This may simply replicate the setup already + done by the IBM BIOS for Arctic1 (possibly with some address map changes), or + may be the first initialization if the board is booting from another BIOS. + Virtually all that is required to boot Linux on Subzero is that the BIOS + enable the memory controller, load a Linux image from flash, and run it. + + For optimal dynamic frequency scaling the EBC settings will also vary as the + frequency varies. +*/ + +void __init +subzero_core_ebc_setup(void) +{ + ebc0_bnap_t ap; + + /* Set EBC bank 0 for the boot/data flash. + + Access parameters assume 150ns Intel flash @ 66.66 MHz maximum bus + speed = 10 cycle access with 2 turnaround cycles (30 ns). + + NB: IBM BIOS sets this bank to burst, however bursting will never + happen in Linux because this region is mapped non-cacheable and + guarded, so it is set non-burst here. */ + ap.reg = mfdcri(DCRN_EBC0, BnAP(0)) & EBC0_BnAP_MASK; + ap.fields.twt = 10; + ap.fields.th = 2; + mtdcri(DCRN_EBC0, BnAP(0), ap.reg); + +} + diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/subzero.h linuxppc-2.4/arch/ppc/platforms/subzero.h --- linux-2.4.22/arch/ppc/platforms/subzero.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/subzero.h 2003-07-25 02:00:01.000000000 +1000 @@ -0,0 +1,100 @@ +/* + * arch/ppc/platforms/subzero.h Platform definitions for the IBM + * Subzero card, based on beech.h by Bishop Brock + * + * 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 + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * David Gibson + * IBM OzLabs, Canberra, Australia + * + * + * Ken Inoue + * IBM Thomas J. Watson Research Center + * + * + */ + +#ifdef __KERNEL__ +#ifndef __ASM_SUBZERO_CORE_H__ +#define __ASM_SUBZERO_CORE_H__ + +#include + +#ifndef __ASSEMBLY__ + +#include + +/* + * Data structure defining board information maintained by the standard boot + * ROM on the IBM Subzero card. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + * + * Original Beech BIOS Definition: + * + * typedef struct board_cfg_data { + * unsigned char usr_config_ver[4]; + * unsigned long timerclk_freq; + * unsigned char rom_sw_ver[30]; + * unsigned int mem_size; + * unsigned long sysclock_period; + * unsigned long sys_speed; + * unsigned long cpu_speed; + * unsigned long vco_speed; + * unsigned long plb_speed; + * unsigned long opb_speed; + * unsigned long ebc_speed; + * } bd_t; + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned long bi_tbfreq; /* Frequency of SysTmrClk */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned long sysclock_period; /* SysClk period in ns */ + unsigned long sys_speed; /* SysCLk frequency in Hz */ + unsigned long bi_intfreq; /* Processor speed, in Hz */ + unsigned long vco_speed; /* PLL VCO speed, in Hz */ + unsigned long bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned long bi_opb_busfreq; /* OPB Bus speed, in Hz */ + unsigned long bi_ebc_busfreq; /* EBC Bus speed, in Hz */ + int bi_iic_fast[1]; /* Use fast i2c mode */ +} bd_t; + +/* EBC Bank 0 controls the boot flash + * + * FIXME? these values assume that there is 16MB of flash on the + * personality card, in addition to the 16MB on the subzero card + * itself */ +#define SUBZERO_BANK0_PADDR ((uint)0xfe000000) +#define SUBZERO_BANK0_EBC_SIZE EBC0_BnCR_BS_32MB + +#define SUBZERO_BOOTFLASH_PADDR (SUBZERO_BANK0_PADDR) +#define SUBZERO_BOOTFLASH_SIZE ((uint)(32 * 1024 * 1024)) + +#define PCI_DRAM_OFFSET 0 + +void *beech_sram_alloc(size_t size); +int beech_sram_free(void *p); + +void subzero_core_ebc_setup(void); + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_SUBZERO_CORE_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/sycamore.c linuxppc-2.4/arch/ppc/platforms/sycamore.c --- linux-2.4.22/arch/ppc/platforms/sycamore.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/sycamore.c 2003-06-19 16:04:49.000000000 +1000 @@ -0,0 +1,291 @@ +/* + * + * Copyright 2000-2002 MontaVista Software Inc. + * Author: Armin Kuster + * MontaVista Software, Inc. + * + * Module name: sycamore.c + * + * Description: + * Architecture- / platform-specific boot-time initialization code for + * IBM PowerPC 4xx based boards. + * + * History: 08/01/02 - armin + * initial release + * + * Please read the COPYING file for all license details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef DEBUG + +#ifdef DEBUG +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +void *kb_cs; +void *kb_data; +void *sycamore_rtc_base; + +/* + * Define all of the IRQ senses and polarities. + */ + +static u_char Sycamore_IRQ_initsenses[] __initdata = { + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 0: Uart 0*/ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 1: Uart 1*/ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 2: IIC */ + (IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE), /* 3: External Master */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 4: PCI ext cmd write*/ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 5: DMA Chan 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 6: DMA Chan 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 7: DMA Chan 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 8: DMA Chan 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 9: Ethernet wakeup (WOL)*/ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 10: Mal (SEER) */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 11: Mal TXEOB */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 12: Mal RXEOB */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 13: Mal TXDE*/ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 14: Mal RXDE*/ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 15: Ethernet */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 16: Ext PCI SERR */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 17: ECC */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* 18: PCI PM*/ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 19: Ext Int 7 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 20: Ext Int 8 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 21: Ext Int 9 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 22: Ext Int 10 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 23: Ext Int 11 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 24: Ext Int 12 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 25: Ext Int 0 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 26: Ext Int 1 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 27: Ext Int 2 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 28: Ext Int 3 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 29: Ext Int 4 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 30: Ext Int 5 */ + (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* 31: Ext Int 6 */ +}; + + +/* Some IRQs unique to Sycamore. + * Used by the generic 405 PCI setup functions in ppc4xx_pci.c + */ +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ + static char pci_irq_table[][4] = + /* + * PCI IDSEL/INTPIN->INTLINE + * A B C D + */ + { + {28, 28, 28, 28}, /* IDSEL 1 - PCI slot 1 */ + {29, 29, 29, 29}, /* IDSEL 2 - PCI slot 2 */ + {30, 30, 30, 30}, /* IDSEL 3 - PCI slot 3 */ + {31, 31, 31, 31}, /* IDSEL 4 - PCI slot 4 */ + }; + + const long min_idsel = 1, max_idsel = 4, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; + +void __init +board_setup_arch(void) +{ +#define SYCAMORE_PS2_BASE 0xF0100000 +#define SYCAMORE_FPGA_BASE 0xF0300000 + + void *fpga_brdc; + unsigned char fpga_brdc_data; + void *fpga_enable; + void *fpga_polarity; + void *fpga_status; + void *fpga_trigger; + + kb_data = ioremap(SYCAMORE_PS2_BASE, 8); + if (!kb_data) { + printk(KERN_CRIT + "sycamore_setup_arch() kb_data ioremap failed\n"); + return; + } + + kb_cs = kb_data + 1; + + fpga_status = ioremap(SYCAMORE_FPGA_BASE, 8); + if (!fpga_status) { + printk(KERN_CRIT + "sycamore_setup_arch() fpga_status ioremap failed\n"); + return; + } + + fpga_enable = fpga_status + 1; + fpga_polarity = fpga_status + 2; + fpga_trigger = fpga_status + 3; + fpga_brdc = fpga_status + 4; + + /* split the keyboard and mouse interrupts */ + fpga_brdc_data = readb(fpga_brdc); + fpga_brdc_data |= 0x80; + writeb(fpga_brdc_data, fpga_brdc); + + writeb(0x3, fpga_enable); + + writeb(0x3, fpga_polarity); + + writeb(0x3, fpga_trigger); + + /* RTC step for the sycamore */ + sycamore_rtc_base = (void *) SYCAMORE_RTC_VADDR; + TODC_INIT(TODC_TYPE_DS1743, sycamore_rtc_base, sycamore_rtc_base, + sycamore_rtc_base, 8); + ibm4xxPIC_InitSenses = Sycamore_IRQ_initsenses; + ibm4xxPIC_NumInitSenses = sizeof(Sycamore_IRQ_initsenses); + + /* Identify the system */ + printk(KERN_INFO "IBM Sycamore (IBM405GPr) Platform\n"); + printk(KERN_INFO + "Port by MontaVista Software, Inc. (source@mvista.com)\n"); +} + +void __init +bios_fixup(struct pci_controller *hose, struct pcil0_regs *pcip) +{ + + unsigned int bar_response, bar; + /* + * Expected PCI mapping: + * + * PLB addr PCI memory addr + * --------------------- --------------------- + * 0000'0000 - 7fff'ffff <--- 0000'0000 - 7fff'ffff + * 8000'0000 - Bfff'ffff ---> 8000'0000 - Bfff'ffff + * + * PLB addr PCI io addr + * --------------------- --------------------- + * e800'0000 - e800'ffff ---> 0000'0000 - 0001'0000 + * + * The following code is simplified by assuming that the bootrom + * has been well behaved in following this mapping. + */ + +#ifdef DEBUG + int i; + + printk("ioremap PCLIO_BASE = 0x%x\n", pcip); + printk("PCI bridge regs before fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif + + /* added for IBM boot rom version 1.15 bios bar changes -AK */ + + /* Disable region first */ + out_le32((void *) &(pcip->pmm[0].ma), 0x00000000); + /* PLB starting addr, PCI: 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].la), 0x80000000); + /* PCI start addr, 0x80000000 */ + out_le32((void *) &(pcip->pmm[0].pcila), PPC405_PCI_MEM_BASE); + /* 512MB range of PLB to PCI */ + out_le32((void *) &(pcip->pmm[0].pciha), 0x00000000); + /* Enable no pre-fetch, enable region */ + out_le32((void *) &(pcip->pmm[0].ma), ((0xffffffff - + (PPC405_PCI_UPPER_MEM - + PPC405_PCI_MEM_BASE)) | 0x01)); + + /* Disable region one */ + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[1].la), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[1].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[1].ma), 0x00000000); + out_le32((void *) &(pcip->ptm1ms), 0x00000000); + + /* Disable region two */ + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->pmm[2].la), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pcila), 0x00000000); + out_le32((void *) &(pcip->pmm[2].pciha), 0x00000000); + out_le32((void *) &(pcip->pmm[2].ma), 0x00000000); + out_le32((void *) &(pcip->ptm2ms), 0x00000000); + + /* Zero config bars */ + for (bar = PCI_BASE_ADDRESS_1; bar <= PCI_BASE_ADDRESS_2; bar += 4) { + early_write_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + 0x00000000); + early_read_config_dword(hose, hose->first_busno, + PCI_FUNC(hose->first_busno), bar, + &bar_response); + DBG("BUS %d, device %d, Function %d bar 0x%8.8x is 0x%8.8x\n", + hose->first_busno, PCI_SLOT(hose->first_busno), + PCI_FUNC(hose->first_busno), bar, bar_response); + } + /* end work arround */ + +#ifdef DEBUG + printk("PCI bridge regs after fixup \n"); + for (i = 0; i <= 3; i++) { + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].ma))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].la))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pcila))); + printk(" pmm%dma\t0x%x\n", i, in_le32(&(pcip->pmm[i].pciha))); + } + printk(" ptm1ms\t0x%x\n", in_le32(&(pcip->ptm1ms))); + printk(" ptm1la\t0x%x\n", in_le32(&(pcip->ptm1la))); + printk(" ptm2ms\t0x%x\n", in_le32(&(pcip->ptm2ms))); + printk(" ptm2la\t0x%x\n", in_le32(&(pcip->ptm2la))); + +#endif +} + +void __init +board_io_mapping(void) +{ + io_block_mapping(SYCAMORE_RTC_VADDR, + SYCAMORE_RTC_PADDR, SYCAMORE_RTC_SIZE, _PAGE_IO); +} + +void __init +board_setup_irq(void) +{ +} + +void __init +board_init(void) +{ + ppc_md.time_init = todc_time_init; + ppc_md.set_rtc_time = todc_set_rtc_time; + ppc_md.get_rtc_time = todc_get_rtc_time; + ppc_md.nvram_read_val = todc_direct_read_val; + ppc_md.nvram_write_val = todc_direct_write_val; +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/sycamore.h linuxppc-2.4/arch/ppc/platforms/sycamore.h --- linux-2.4.22/arch/ppc/platforms/sycamore.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/sycamore.h 2003-07-25 02:00:01.000000000 +1000 @@ -0,0 +1,86 @@ +/* + * + * Copyright 2000 MontaVista Software Inc. + * Author: Armin Kuster + * MontaVista Software, Inc. + * + * Module name: sycamore.h + * + * Description: + * Macros, definitions, and data structures specific to the IBM PowerPC + * based boards. + * + * This includes: + * + * 405GP "Sycamore" evaluation board + * + * Please read the COPYING file for all license details. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_SYCAMORE_H__ +#define __ASM_SYCAMORE_H__ + +#include + +#ifndef __ASSEMBLY__ +/* + * Data structure defining board information maintained by the boot + * ROM on IBM's "Sycamore" evaluation board. An effort has been made to + * keep the field names consistent with the 8xx 'bd_t' board info + * structures. + */ + +typedef struct board_info { + unsigned char bi_s_version[4]; /* Version of this structure */ + unsigned char bi_r_version[30]; /* Version of the IBM ROM */ + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[6]; /* Local Ethernet MAC address */ + unsigned char bi_pci_enetaddr[6]; /* PCI Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ + unsigned long bi_opb_busfreq; /* OPB Bus speed, in Hz */ + int bi_iic_fast[1]; /* Use fast i2c mode */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + + +/* Memory map for the IBM "Sycamore" 405GP evaluation board. + * Generic 4xx plus RTC. + */ + +extern void *walnut_rtc_base; +#define SYCAMORE_RTC_PADDR ((uint)0xf0000000) +#define SYCAMORE_RTC_VADDR SYCAMORE_RTC_PADDR +#define SYCAMORE_RTC_SIZE ((uint)8*1024) + +/* ps2 keyboard and mouse */ +#define KEYBOARD_IRQ 25 +#define AUX_IRQ 26 + +#ifdef CONFIG_PPC405GP_INTERNAL_CLOCK +#define BASE_BAUD 201600 +#else +#define BASE_BAUD 691200 +#endif + +#define SYCAMORE_PS2_BASE 0xF0100000 +#define SYCAMORE_FPGA_BASE 0xF0300000 + + +extern void *kb_cs; +extern void *kb_data; +#define kbd_read_input() readb(kb_data) +#define kbd_read_status() readb(kb_cs) +#define kbd_write_output(val) writeb(val, kb_data) +#define kbd_write_command(val) writeb(val, kb_cs) + +#define PPC4xx_MACHINE_NAME "IBM Sycamore" + +#endif /* !__ASSEMBLY__ */ +#endif /* __ASM_SYCAMORE_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/walnut.h linuxppc-2.4/arch/ppc/platforms/walnut.h --- linux-2.4.22/arch/ppc/platforms/walnut.h 2003-06-25 20:56:52.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/walnut.h 2003-07-25 02:00:01.000000000 +1000 @@ -45,7 +45,7 @@ unsigned int bi_intfreq; /* Processor speed, in Hz */ unsigned int bi_busfreq; /* PLB Bus speed, in Hz */ unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ - unsigned int bi_opbfreq; /* OPB Bus speed, in Hz */ + unsigned int bi_opb_busfreq; /* OPB Bus speed, in Hz */ int bi_iic_fast[1]; /* Use fast i2c mode */ } bd_t; diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ml300.c linuxppc-2.4/arch/ppc/platforms/xilinx_ml300.c --- linux-2.4.22/arch/ppc/platforms/xilinx_ml300.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ml300.c 2003-08-13 08:59:41.000000000 +1000 @@ -0,0 +1,206 @@ +/* + * xilinx_ml300.c + * + * Xilinx ML300 evaluation board initialization + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xilinx_ocp/xbasic_types.h" +extern u32 XWaitInAssert; +/* + * The Xilinx drivers use something called XASSERT. It should be + * disabled in running systems to save time and space. This is done by + * defining NDEBUG when compiling anything that includes + * xilinx_ocp/xbasic_types.h. During development, it is handy to leave + * NDEBUG off, but by default, the Xilinx code just tight loops. In our + * board_init function, we register a handler to be called that prints + * out that we hit an XASSERT. In addition, we tell it to not tight + * loop by setting XWaitInAssert to zero. This will probably lead to an + * Oops and thus a backtrace. + */ + +/* SAATODO: Make sure NDEBUG is on. */ +void +reportXAssert(char *File, int Line) +{ + printk(KERN_CRIT "Xilinx OS Independent Code XAssert: %s:%d\n", + File, Line); + printk(KERN_CRIT "Code may crash due to unhandled errors.\n"); +} + +/* Have OCP take care of the serial ports. */ +struct ocp_def core_ocp[] = { +#ifdef XPAR_UARTNS550_0_BASEADDR + { .vendor = OCP_VENDOR_XILINX, + .function = OCP_FUNC_16550, + .index = 0, + .paddr = XPAR_UARTNS550_0_BASEADDR, + .irq = 31 - XPAR_INTC_0_UARTNS550_0_VEC_ID, + }, +#ifdef XPAR_UARTNS550_1_BASEADDR + { .vendor = OCP_VENDOR_XILINX, + .function = OCP_FUNC_16550, + .index = 1, + .paddr = XPAR_UARTNS550_1_BASEADDR, + .irq = 31 - XPAR_INTC_1_UARTNS550_0_VEC_ID, + }, +#ifdef XPAR_UARTNS550_2_BASEADDR + { .vendor = OCP_VENDOR_XILINX, + .function = OCP_FUNC_16550, + .index = 2, + .paddr = XPAR_UARTNS550_2_BASEADDR, + .irq = 31 - XPAR_INTC_2_UARTNS550_0_VEC_ID, + }, +#ifdef XPAR_UARTNS550_3_BASEADDR + { .vendor = OCP_VENDOR_XILINX, + .function = OCP_FUNC_16550, + .index = 3, + .paddr = XPAR_UARTNS550_3_BASEADDR, + .irq = 31 - XPAR_INTC_3_UARTNS550_0_VEC_ID, + }, +#ifdef XPAR_UARTNS550_4_BASEADDR +#error Edit this file to add more devices. +#endif /* 4 */ +#endif /* 3 */ +#endif /* 2 */ +#endif /* 1 */ +#endif /* 0 */ + {OCP_NULL_TYPE, 0x0, OCP_IRQ_NA, OCP_CPM_NA} +}; +#define NR_SER_PORTS (ARRAY_SIZE(core_ocp)-1) + +#ifdef CONFIG_PCI +int __init +ppc405_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin) +{ +#define PCI_INTA (31 - XPAR_INTC_0_PCI_0_VEC_ID_A) +#define PCI_INTB (31 - XPAR_INTC_0_PCI_0_VEC_ID_B) +#define PCI_INTC (31 - XPAR_INTC_0_PCI_0_VEC_ID_C) +#define PCI_INTD (31 - XPAR_INTC_0_PCI_0_VEC_ID_D) + static signed char pci_irq_table[][4] = + { + { PCI_INTA, PCI_INTB, PCI_INTC, PCI_INTD }, /* loopback */ + { PCI_INTA, PCI_INTB, PCI_INTC, PCI_INTD }, /* PMC */ + { PCI_INTA, PCI_INTB, PCI_INTC, -1 }, /* Card Bus */ + }; + const long min_idsel = 1, max_idsel = 3, irqs_per_slot = 4; + return PCI_IRQ_TABLE_LOOKUP; +}; +#endif + +/* + * As an overview of how the following functions (board_init, + * board_io_mapping and board_setup_arch) fit into the kernel startup + * procedure, here's a call tree: + * + * start_here arch/ppc/kernel/head_4xx.S + * machine_init arch/ppc/kernel/setup.c + * platform_init arch/ppc/kernel/setup.c + * board_init this file + * MMU_init arch/ppc/mm/init.c + * *ppc_md.setup_io_mappings == m4xx_map_io arch/ppc/kernel/ppc4xx_setup.c + * io_block_mapping of ONB and PCI + * board_io_mapping this file + * start_kernel init/main.c + * setup_arch arch/ppc/kernel/setup.c + * *ppc_md.setup_arch == ppc4xx_setup_arch arch/ppc/kernel/ppc4xx_setup.c + * ppc4xx_find_bridges arch/ppc/kernel/ppc405_pci.c + * board_setup_arch this file + * + * The main take away from this is that we need to do the initialization + * of the PCI core after the io_block_mapping of the PCI areas, but + * before ppc4xx_find_bridges. So that means we put it in + * board_io_mapping, even though that does not seem like the intuitive + * place. + */ + +void __init +board_init(void) +{ +#if defined(CONFIG_MAGIC_SYSRQ) && defined(CONFIG_PC_KEYBOARD) && \ + defined(CONFIG_VT) + extern unsigned char pckbd_sysrq_xlate[128]; + ppc_md.ppc_kbd_sysrq_xlate = pckbd_sysrq_xlate; + SYSRQ_KEY = 0x54; +#endif + + XWaitInAssert = 0; + XAssertSetCallback(reportXAssert); +} + +void __init +board_io_mapping(void) +{ + extern struct serial_state rs_table[]; + int i; + + /* Remap all the serial ports */ + for (i = 0; i < NR_SER_PORTS; i++) + rs_table[i].iomem_base = + ioremap((unsigned long) rs_table[i].iomem_base, 16); +#ifdef CONFIG_PCI + /* + * Enable the PCI initiator functions in the PCI core by writing + * to the self-configuration space as described in the + * Configuration section of the OPB to PCI Bridge chapter of the + * Virtex-II Pro Platform FPGA Developer's Kit manual. + */ + out_be32((volatile unsigned *)(PPC4xx_PCI_LCFG_VADDR + PCI_COMMAND), + (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER + | PCI_COMMAND_PARITY | PCI_COMMAND_SERR | 0xffff0000)); +#endif +} + +void __init +board_setup_arch(void) +{ +#ifdef CONFIG_PCI + static const int bus = 0; + static const int devfn = PCI_DEVFN(2, 0); + static const int cr_offset = 0x80; + static const u32 cr = ((1 << 27) | /* P2CCLK */ + (1 << 22) | /* CBRSVD */ + (4 << 16) | /* CDMACHAN = not used */ + (1 << 15) | /* MRBURSTDN */ + (1 << 6) | /* PWRSAVINGS */ + (1 << 5) | /* SUBSYSRW */ + (1 << 1)); /* KEEPCLK */ + static const int mfunc_offset = 0x8C; + static const u32 mfunc = ((2 << 8) | /* MFUNC2 = INTC */ + (2 << 4) | /* MFUNC1 = INTB */ + (2 << 0)); /* MFUNC0 = INTA */ + + /* Set up the clocks on the PCI44451 CardBus/FireWire bridge. */ + early_write_config_dword(0, bus, devfn, cr_offset, cr); + + /* Set up the interrupts on the PCI44451 CardBus/FireWire bridge. */ + early_write_config_dword(0, bus, devfn, mfunc_offset, mfunc); +#endif + + /* Identify the system */ + printk + ("Xilinx Virtex-II Pro port (C) 2002 MontaVista Software, Inc. (source@mvista.com)\n"); +} + +/* Called after board_setup_irq from ppc4xx_init_IRQ(). */ +void __init +board_setup_irq(void) +{ +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ml300.h linuxppc-2.4/arch/ppc/platforms/xilinx_ml300.h --- linux-2.4.22/arch/ppc/platforms/xilinx_ml300.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ml300.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,143 @@ +/* + * xilinx_ml300.h + * + * Include file that defines the Xilinx ML300 evaluation board + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_XILINX_ML300_H__ +#define __ASM_XILINX_ML300_H__ + +/* ML300 has a Xilinx Virtex-II Pro processor */ +#include +#include + +#ifndef __ASSEMBLY__ +typedef struct board_info { + unsigned int bi_memsize; /* DRAM installed, in bytes */ + unsigned char bi_enetaddr[6]; /* Local Ethernet MAC address */ + unsigned int bi_intfreq; /* Processor speed, in Hz */ + unsigned int bi_busfreq; /* Bus speed, in Hz */ + unsigned int bi_pci_busfreq; /* PCI Bus speed, in Hz */ +} bd_t; + +/* Some 4xx parts use a different timebase frequency from the internal clock. +*/ +#define bi_tbfreq bi_intfreq + +#endif /* !__ASSEMBLY__ */ + + +#ifdef CONFIG_PCI +/* PCI memory space */ +#define PPC405_PCI_MEM_BASE XPAR_PCI_0_MEM_BASEADDR +#define PPC405_PCI_LOWER_MEM XPAR_PCI_0_MEM_BASEADDR +#define PPC405_PCI_UPPER_MEM XPAR_PCI_0_MEM_HIGHADDR + +/* PCI I/O space parameters for io_block_mapping. */ +#define PPC4xx_PCI_IO_PADDR ((uint)XPAR_PCI_0_IO_BASEADDR) +#define PPC4xx_PCI_IO_VADDR PPC4xx_PCI_IO_PADDR +#define PPC4xx_PCI_IO_SIZE 0x10000 /* Hardcoded size from ppc405_pci.c */ +/* PCI I/O space processor address */ +#define PPC405_PCI_PHY_IO_BASE XPAR_PCI_0_IO_BASEADDR +/* PCI I/O space PCI address */ +#define PPC405_PCI_LOWER_IO 0x00000000 +#define PPC405_PCI_UPPER_IO (PPC405_PCI_LOWER_IO + PPC4xx_PCI_IO_SIZE - 1) + +/* PCI Configuration space parameters for io_block_mapping. */ +#define PPC4xx_PCI_CFG_PADDR ((uint)XPAR_PCI_0_CONFIG_ADDR) +#define PPC4xx_PCI_CFG_VADDR PPC4xx_PCI_CFG_PADDR +#define PPC4xx_PCI_CFG_SIZE 8u /* size of two registers */ +/* PCI Configuration space address and data registers. */ +#define PPC405_PCI_CONFIG_ADDR XPAR_PCI_0_CONFIG_ADDR +#define PPC405_PCI_CONFIG_DATA XPAR_PCI_0_CONFIG_DATA + +/* PCI Local configuration space parameters for io_block_mapping. */ +#define PPC4xx_PCI_LCFG_PADDR ((uint)XPAR_PCI_0_LCONFIG_ADDR) +#define PPC4xx_PCI_LCFG_VADDR PPC4xx_PCI_LCFG_PADDR +#define PPC4xx_PCI_LCFG_SIZE 256u /* PCI configuration address space size */ + +#define _IO_BASE isa_io_base +#define _ISA_MEM_BASE isa_mem_base +#define PCI_DRAM_OFFSET pci_dram_offset +#endif /* CONFIG_PCI */ + +/* We don't need anything mapped. Size of zero will accomplish that. */ +#define PPC4xx_ONB_IO_PADDR 0u +#define PPC4xx_ONB_IO_VADDR 0u +#define PPC4xx_ONB_IO_SIZE 0u + +/* *** Serial Port Constants *** */ +#define BASE_BAUD (XPAR_UARTNS550_0_CLOCK_FREQ_HZ/16) + +/* The serial ports in the Virtex-II Pro have each I/O byte in the + * LSByte of a word. This means that iomem_reg_shift needs to be 2 to + * change the byte offsets into word offsets. In addition the base + * addresses need to have 3 added to them to get to the LSByte. + */ +#define STD_UART_OP(num) \ + { 0, BASE_BAUD, 0, 31-XPAR_INTC_0_UARTNS550_##num##_VEC_ID, \ + (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST), \ + iomem_base: (u8 *)XPAR_UARTNS550_##num##_BASEADDR + 3, \ + iomem_reg_shift: 2, \ + io_type: SERIAL_IO_MEM}, + +#if defined(XPAR_INTC_0_UARTNS550_0_VEC_ID) +#define ML300_UART0 STD_UART_OP(0) +#else +#define ML300_UART0 +#endif + +#if defined(XPAR_INTC_0_UARTNS550_1_VEC_ID) +#define ML300_UART1 STD_UART_OP(1) +#else +#define ML300_UART1 +#endif + +#if defined(XPAR_INTC_0_UARTNS550_2_VEC_ID) +#define ML300_UART2 STD_UART_OP(2) +#else +#define ML300_UART2 +#endif + +#if defined(XPAR_INTC_0_UARTNS550_3_VEC_ID) +#define ML300_UART3 STD_UART_OP(3) +#else +#define ML300_UART3 +#endif + +#if defined(XPAR_INTC_0_UARTNS550_4_VEC_ID) +#error Edit this file to add more devices. +#endif + +#if defined(CONFIG_UART0_TTYS0) +#define SERIAL_PORT_DFNS \ + ML300_UART0 \ + ML300_UART1 \ + ML300_UART2 \ + ML300_UART3 +#endif + +#if defined(CONFIG_UART0_TTYS1) +#define SERIAL_PORT_DFNS \ + ML300_UART1 \ + ML300_UART0 \ + ML300_UART2 \ + ML300_UART3 +#endif + +/* ps2 keyboard and mouse */ +#define KEYBOARD_IRQ (31 - XPAR_INTC_0_PS2_1_VEC_ID) +#define AUX_IRQ (31 - XPAR_INTC_0_PS2_0_VEC_ID) + +#define PPC4xx_MACHINE_NAME "Xilinx ML300" + +#endif /* __ASM_XILINX_ML300_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/Makefile linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/Makefile --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/Makefile 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,20 @@ +# +# Makefile for the Xilinx On Chip Peripheral support code +# + +list-multi := xilinx_ocp.o + +# Linux file to EXPORT_SYMBOL all of the Xilinx entries. +export-objs += xilinx_syms.o +xilinx_ocp-objs += xilinx_syms.o + +# The Xilinx OS independent code. +xilinx_ocp-objs += xbasic_types.o xdma_channel.o xdma_channel_sg.o \ + xipif_v1_23_b.o xpacket_fifo_v1_00_b.o xversion.o + +obj-$(CONFIG_XILINX_OCP) := xilinx_ocp.o + +xilinx_ocp.o: $(xilinx_ocp-objs) + $(LD) -r -o $@ $(xilinx_ocp-objs) + +include $(TOPDIR)/Rules.make diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xbasic_types.c linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xbasic_types.c --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xbasic_types.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xbasic_types.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,135 @@ +/* $Id $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xbasic_types.c +* +* This file contains basic functions for Xilinx software IP. +* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +/** + * This variable allows testing to be done easier with asserts. An assert + * sets this variable such that a driver can evaluate this variable + * to determine if an assert occurred. + */ +unsigned int XAssertStatus; + +/** + * This variable allows the assert functionality to be changed for testing + * such that it does not wait infinitely. Use the debugger to disable the + * waiting during testing of asserts. + */ +u32 XWaitInAssert = TRUE; + +/* The callback function to be invoked when an assert is taken */ +static XAssertCallback XAssertCallbackRoutine = (XAssertCallback) NULL; + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +/** +* +* Implements assert. Currently, it calls a user-defined callback function +* if one has been set. Then, it potentially enters an infinite loop depending +* on the value of the XWaitInAssert variable. +* +* @param File is the name of the filename of the source +* @param Line is the linenumber within File +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XAssert(char *File, int Line) +{ + /* if the callback has been set then invoke it */ + if (XAssertCallbackRoutine != NULL) { + (*XAssertCallbackRoutine) (File, Line); + } + + /* if specified, wait indefinitely such that the assert will show up + * in testing + */ + while (XWaitInAssert) { + } +} + +/*****************************************************************************/ +/** +* +* Sets up a callback function to be invoked when an assert occurs. If there +* was already a callback installed, then it is replaced. +* +* @param Routine is the callback to be invoked when an assert is taken +* +* @return +* +* None. +* +* @note +* +* This function has no effect if NDEBUG is set +* +******************************************************************************/ +void +XAssertSetCallback(XAssertCallback Routine) +{ + XAssertCallbackRoutine = Routine; +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xbasic_types.h linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xbasic_types.h --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xbasic_types.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xbasic_types.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,288 @@ +/* $Id: xbasic_types.h,v 1.8 2003/05/09 14:07:02 robertm Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xbasic_types.h +* +* This file contains basic types for Xilinx software IP. These types do not +* follow the standard naming convention with respect to using the component +* name in front of each name because they are considered to be primitives. +* +* @note +* +* This file contains items which are architecture dependent. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver    Who    Date    Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rmm  12/14/01 First release
+*       rmm  05/09/03 Added "xassert always" macros to rid ourselves of diab
+*                     compiler warnings
+* 
+* +******************************************************************************/ + +#ifndef XBASIC_TYPES_H /* prevent circular inclusions */ +#define XBASIC_TYPES_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +/************************** Constant Definitions *****************************/ + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef NULL +#define NULL 0 +#endif +/** Null */ + +#define XCOMPONENT_IS_READY 0x11111111 /* component has been initialized */ +#define XCOMPONENT_IS_STARTED 0x22222222 /* component has been started */ + +/* the following constants and declarations are for unit test purposes and are + * designed to be used in test applications. + */ +#define XTEST_PASSED 0 +#define XTEST_FAILED 1 + +#define XASSERT_NONE 0 +#define XASSERT_OCCURRED 1 + +extern unsigned int XAssertStatus; +extern void XAssert(char *, int); + +/**************************** Type Definitions *******************************/ + +/** @name Primitive types + * These primitive types are created for transportability. + * They are dependent upon the target architecture. + * @{ + */ +#include + +typedef struct { + u32 Upper; + u32 Lower; +} Xuint64; + +/*@}*/ + +/** + * This data type defines an interrupt handler for a device. + * The argument points to the instance of the component + */ +typedef void (*XInterruptHandler) (void *InstancePtr); + +/** + * This data type defines a callback to be invoked when an + * assert occurs. The callback is invoked only when asserts are enabled + */ +typedef void (*XAssertCallback) (char *FilenamePtr, int LineNumber); + +/***************** Macros (Inline Functions) Definitions *********************/ + +/*****************************************************************************/ +/** +* Return the most significant half of the 64 bit data type. +* +* @param x is the 64 bit word. +* +* @return +* +* The upper 32 bits of the 64 bit word. +* +* @note +* +* None. +* +******************************************************************************/ +#define XUINT64_MSW(x) ((x).Upper) + +/*****************************************************************************/ +/** +* Return the least significant half of the 64 bit data type. +* +* @param x is the 64 bit word. +* +* @return +* +* The lower 32 bits of the 64 bit word. +* +* @note +* +* None. +* +******************************************************************************/ +#define XUINT64_LSW(x) ((x).Lower) + +#ifndef NDEBUG + +/*****************************************************************************/ +/** +* This assert macro is to be used for functions that do not return anything +* (void). This in conjunction with the XWaitInAssert boolean can be used to +* accomodate tests so that asserts which fail allow execution to continue. +* +* @param expression is the expression to evaluate. If it evaluates to false, +* the assert occurs. +* +* @return +* +* Returns void unless the XWaitInAssert variable is true, in which case +* no return is made and an infinite loop is entered. +* +* @note +* +* None. +* +******************************************************************************/ +#define XASSERT_VOID(expression) \ +{ \ + if (expression) \ + { \ + XAssertStatus = XASSERT_NONE; \ + } \ + else \ + { \ + XAssert(__FILE__, __LINE__); \ + XAssertStatus = XASSERT_OCCURRED; \ + return; \ + } \ +} + +/*****************************************************************************/ +/** +* This assert macro is to be used for functions that do return a value. This in +* conjunction with the XWaitInAssert boolean can be used to accomodate tests so +* that asserts which fail allow execution to continue. +* +* @param expression is the expression to evaluate. If it evaluates to false, +* the assert occurs. +* +* @return +* +* Returns 0 unless the XWaitInAssert variable is true, in which case +* no return is made and an infinite loop is entered. +* +* @note +* +* None. +* +******************************************************************************/ +#define XASSERT_NONVOID(expression) \ +{ \ + if (expression) \ + { \ + XAssertStatus = XASSERT_NONE; \ + } \ + else \ + { \ + XAssert(__FILE__, __LINE__); \ + XAssertStatus = XASSERT_OCCURRED; \ + return 0; \ + } \ +} + +/*****************************************************************************/ +/** +* Always assert. This assert macro is to be used for functions that do not +* return anything (void). Use for instances where an assert should always +* occur. +* +* @return +* +* Returns void unless the XWaitInAssert variable is true, in which case +* no return is made and an infinite loop is entered. +* +* @note +* +* None. +* +******************************************************************************/ +#define XASSERT_VOID_ALWAYS() \ +{ \ + XAssert(__FILE__, __LINE__); \ + XAssertStatus = XASSERT_OCCURRED; \ + return; \ +} + +/*****************************************************************************/ +/** +* Always assert. This assert macro is to be used for functions that do return +* a value. Use for instances where an assert should always occur. +* +* @return +* +* Returns void unless the XWaitInAssert variable is true, in which case +* no return is made and an infinite loop is entered. +* +* @note +* +* None. +* +******************************************************************************/ +#define XASSERT_NONVOID_ALWAYS() \ +{ \ + XAssert(__FILE__, __LINE__); \ + XAssertStatus = XASSERT_OCCURRED; \ + return 0; \ +} + +#else + +#define XASSERT_VOID(expression) +#define XASSERT_VOID_ALWAYS() +#define XASSERT_NONVOID(expression) +#define XASSERT_NONVOID_ALWAYS() +#endif + +/************************** Function Prototypes ******************************/ + +void XAssertSetCallback(XAssertCallback Routine); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xbuf_descriptor.h linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xbuf_descriptor.h --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xbuf_descriptor.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xbuf_descriptor.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,253 @@ +/* $Id: xbuf_descriptor.h,v 1.1 2001/10/12 17:18:16 linnj Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +* FILENAME: +* +* xbuf_descriptor.h +* +* DESCRIPTION: +* +* This file contains the interface for the XBufDescriptor component. +* The XBufDescriptor component is a passive component that only maps over +* a buffer descriptor data structure shared by the scatter gather DMA hardware +* and software. The component's primary purpose is to provide encapsulation of +* the buffer descriptor processing. See the source file xbuf_descriptor.c for +* details. +* +* NOTES: +* +* Most of the functions of this component are implemented as macros in order +* to optimize the processing. The names are not all uppercase such that they +* can be switched between macros and functions easily. +* +******************************************************************************/ + +#ifndef XBUF_DESCRIPTOR_H /* prevent circular inclusions */ +#define XBUF_DESCRIPTOR_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xdma_channel_i.h" + +/************************** Constant Definitions *****************************/ + +/* The following constants allow access to all fields of a buffer descriptor + * and are necessary at this level of visibility to allow macros to access + * and modify the fields of a buffer descriptor. It is not expected that the + * user of a buffer descriptor would need to use these constants. + */ + +#define XBD_DEVICE_STATUS_OFFSET 0 +#define XBD_CONTROL_OFFSET 1 +#define XBD_SOURCE_OFFSET 2 +#define XBD_DESTINATION_OFFSET 3 +#define XBD_LENGTH_OFFSET 4 +#define XBD_STATUS_OFFSET 5 +#define XBD_NEXT_PTR_OFFSET 6 +#define XBD_ID_OFFSET 7 +#define XBD_FLAGS_OFFSET 8 +#define XBD_RQSTED_LENGTH_OFFSET 9 + +#define XBD_SIZE_IN_WORDS 10 + +/* + * The following constants define the bits of the flags field of a buffer + * descriptor + */ + +#define XBD_FLAGS_LOCKED_MASK 1UL + +/**************************** Type Definitions *******************************/ + +typedef u32 XBufDescriptor[XBD_SIZE_IN_WORDS]; + +/***************** Macros (Inline Functions) Definitions *********************/ + +/* each of the following macros are named the same as functions rather than all + * upper case in order to allow either the macros or the functions to be + * used, see the source file xbuf_descriptor.c for documentation + */ + +#define XBufDescriptor_Initialize(InstancePtr) \ +{ \ + (*((u32 *)InstancePtr + XBD_CONTROL_OFFSET) = 0); \ + (*((u32 *)InstancePtr + XBD_SOURCE_OFFSET) = 0); \ + (*((u32 *)InstancePtr + XBD_DESTINATION_OFFSET) = 0); \ + (*((u32 *)InstancePtr + XBD_LENGTH_OFFSET) = 0); \ + (*((u32 *)InstancePtr + XBD_STATUS_OFFSET) = 0); \ + (*((u32 *)InstancePtr + XBD_DEVICE_STATUS_OFFSET) = 0); \ + (*((u32 *)InstancePtr + XBD_NEXT_PTR_OFFSET) = 0); \ + (*((u32 *)InstancePtr + XBD_ID_OFFSET) = 0); \ + (*((u32 *)InstancePtr + XBD_FLAGS_OFFSET) = 0); \ + (*((u32 *)InstancePtr + XBD_RQSTED_LENGTH_OFFSET) = 0); \ +} + +#define XBufDescriptor_GetControl(InstancePtr) \ + (u32)(*((u32 *)InstancePtr + XBD_CONTROL_OFFSET)) + +#define XBufDescriptor_SetControl(InstancePtr, Control) \ + (*((u32 *)InstancePtr + XBD_CONTROL_OFFSET) = (u32)Control) + +#define XBufDescriptor_IsLastControl(InstancePtr) \ + (u32)(*((u32 *)InstancePtr + XBD_CONTROL_OFFSET) & \ + XDC_CONTROL_LAST_BD_MASK) + +#define XBufDescriptor_SetLast(InstancePtr) \ + (*((u32 *)InstancePtr + XBD_CONTROL_OFFSET) |= XDC_CONTROL_LAST_BD_MASK) + +#define XBufDescriptor_GetSrcAddress(InstancePtr) \ + ((u32 *)(*((u32 *)InstancePtr + XBD_SOURCE_OFFSET))) + +#define XBufDescriptor_SetSrcAddress(InstancePtr, Source) \ + (*((u32 *)InstancePtr + XBD_SOURCE_OFFSET) = (u32)Source) + +#define XBufDescriptor_GetDestAddress(InstancePtr) \ + ((u32 *)(*((u32 *)InstancePtr + XBD_DESTINATION_OFFSET))) + +#define XBufDescriptor_SetDestAddress(InstancePtr, Destination) \ + (*((u32 *)InstancePtr + XBD_DESTINATION_OFFSET) = (u32)Destination) + +#define XBufDescriptor_GetLength(InstancePtr) \ + (u32)(*((u32 *)InstancePtr + XBD_RQSTED_LENGTH_OFFSET) - \ + *((u32 *)InstancePtr + XBD_LENGTH_OFFSET)) + +#define XBufDescriptor_SetLength(InstancePtr, Length) \ +{ \ + (*((u32 *)InstancePtr + XBD_LENGTH_OFFSET) = (u32)(Length)); \ + (*((u32 *)InstancePtr + XBD_RQSTED_LENGTH_OFFSET) = (u32)(Length));\ +} + +#define XBufDescriptor_GetStatus(InstancePtr) \ + (u32)(*((u32 *)InstancePtr + XBD_STATUS_OFFSET)) + +#define XBufDescriptor_SetStatus(InstancePtr, Status) \ + (*((u32 *)InstancePtr + XBD_STATUS_OFFSET) = (u32)Status) + +#define XBufDescriptor_IsLastStatus(InstancePtr) \ + (u32)(*((u32 *)InstancePtr + XBD_STATUS_OFFSET) & \ + XDC_STATUS_LAST_BD_MASK) + +#define XBufDescriptor_GetDeviceStatus(InstancePtr) \ + ((u32)(*((u32 *)InstancePtr + XBD_DEVICE_STATUS_OFFSET))) + +#define XBufDescriptor_SetDeviceStatus(InstancePtr, Status) \ + (*((u32 *)InstancePtr + XBD_DEVICE_STATUS_OFFSET) = (u32)Status) + +#define XBufDescriptor_GetNextPtr(InstancePtr) \ + (XBufDescriptor *)(*((u32 *)InstancePtr + XBD_NEXT_PTR_OFFSET)) + +#define XBufDescriptor_SetNextPtr(InstancePtr, NextPtr) \ + (*((u32 *)InstancePtr + XBD_NEXT_PTR_OFFSET) = (u32)NextPtr) + +#define XBufDescriptor_GetId(InstancePtr) \ + (u32)(*((u32 *)InstancePtr + XBD_ID_OFFSET)) + +#define XBufDescriptor_SetId(InstancePtr, Id) \ + (*((u32 *)InstancePtr + XBD_ID_OFFSET) = (u32)Id) + +#define XBufDescriptor_GetFlags(InstancePtr) \ + (u32)(*((u32 *)InstancePtr + XBD_FLAGS_OFFSET)) + +#define XBufDescriptor_SetFlags(InstancePtr, Flags) \ + (*((u32 *)InstancePtr + XBD_FLAGS_OFFSET) = (u32)Flags) + +#define XBufDescriptor_Lock(InstancePtr) \ + (*((u32 *)InstancePtr + XBD_FLAGS_OFFSET) |= XBD_FLAGS_LOCKED_MASK) + +#define XBufDescriptor_Unlock(InstancePtr) \ + (*((u32 *)InstancePtr + XBD_FLAGS_OFFSET) &= ~XBD_FLAGS_LOCKED_MASK) + +#define XBufDescriptor_IsLocked(InstancePtr) \ + (*((u32 *)InstancePtr + XBD_FLAGS_OFFSET) & XBD_FLAGS_LOCKED_MASK) + +/************************** Function Prototypes ******************************/ + +/* The following prototypes are provided to allow each of the functions to + * be implemented as a function rather than a macro, and to provide the + * syntax to allow users to understand how to call the macros, they are + * commented out to prevent linker errors + * + +u32 XBufDescriptor_Initialize(XBufDescriptor* InstancePtr); + +u32 XBufDescriptor_GetControl(XBufDescriptor* InstancePtr); +void XBufDescriptor_SetControl(XBufDescriptor* InstancePtr, u32 Control); + +u32 XBufDescriptor_IsLastControl(XBufDescriptor* InstancePtr); +void XBufDescriptor_SetLast(XBufDescriptor* InstancePtr); + +u32 XBufDescriptor_GetLength(XBufDescriptor* InstancePtr); +void XBufDescriptor_SetLength(XBufDescriptor* InstancePtr, u32 Length); + +u32 XBufDescriptor_GetStatus(XBufDescriptor* InstancePtr); +void XBufDescriptor_SetStatus(XBufDescriptor* InstancePtr, u32 Status); +u32 XBufDescriptor_IsLastStatus(XBufDescriptor* InstancePtr); + +u32 XBufDescriptor_GetDeviceStatus(XBufDescriptor* InstancePtr); +void XBufDescriptor_SetDeviceStatus(XBufDescriptor* InstancePtr, + u32 Status); + +u32 XBufDescriptor_GetSrcAddress(XBufDescriptor* InstancePtr); +void XBufDescriptor_SetSrcAddress(XBufDescriptor* InstancePtr, + u32 SourceAddress); + +u32 XBufDescriptor_GetDestAddress(XBufDescriptor* InstancePtr); +void XBufDescriptor_SetDestAddress(XBufDescriptor* InstancePtr, + u32 DestinationAddress); + +XBufDescriptor* XBufDescriptor_GetNextPtr(XBufDescriptor* InstancePtr); +void XBufDescriptor_SetNextPtr(XBufDescriptor* InstancePtr, + XBufDescriptor* NextPtr); + +u32 XBufDescriptor_GetId(XBufDescriptor* InstancePtr); +void XBufDescriptor_SetId(XBufDescriptor* InstancePtr, u32 Id); + +u32 XBufDescriptor_GetFlags(XBufDescriptor* InstancePtr); +void XBufDescriptor_SetFlags(XBufDescriptor* InstancePtr, u32 Flags); + +void XBufDescriptor_Lock(XBufDescriptor* InstancePtr); +void XBufDescriptor_Unlock(XBufDescriptor* InstancePtr); +u32 XBufDescriptor_IsLocked(XBufDescriptor* InstancePtr); + +void XBufDescriptor_Copy(XBufDescriptor* InstancePtr, + XBufDescriptor* DestinationPtr); + +*/ + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xdma_channel.c linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xdma_channel.c --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xdma_channel.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xdma_channel.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,739 @@ +/* $Id: xdma_channel.c,v 1.2 2001/10/19 14:58:03 linnj Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +* FILENAME: +* +* xdma_channel.c +* +* DESCRIPTION: +* +* This file contains the DMA channel component. This component supports +* a distributed DMA design in which each device can have it's own dedicated +* DMA channel, as opposed to a centralized DMA design. This component +* performs processing for DMA on all devices. +* +* See xdma_channel.h for more information about this component. +* +* NOTES: +* +* None. +* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xdma_channel.h" +#include "xbasic_types.h" +#include "xio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_Initialize +* +* DESCRIPTION: +* +* This function initializes a DMA channel. This function must be called +* prior to using a DMA channel. Initialization of a channel includes setting +* up the registers base address, and resetting the channel such that it's in a +* known state. Interrupts for the channel are disabled when the channel is +* reset. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. +* +* BaseAddress contains the base address of the registers for the DMA channel. +* +* RETURN VALUE: +* +* XST_SUCCESS indicating initialization was successful. +* +* NOTES: +* +* None. +* +******************************************************************************/ +XStatus +XDmaChannel_Initialize(XDmaChannel * InstancePtr, u32 BaseAddress) +{ + /* assert to verify input arguments, don't assert base address */ + + XASSERT_NONVOID(InstancePtr != NULL); + + /* setup the base address of the registers for the DMA channel such + * that register accesses can be done + */ + InstancePtr->RegBaseAddress = BaseAddress; + + /* initialize the scatter gather list such that it indicates it has not + * been created yet and the DMA channel is ready to use (initialized) + */ + InstancePtr->GetPtr = NULL; + InstancePtr->PutPtr = NULL; + InstancePtr->CommitPtr = NULL; + InstancePtr->LastPtr = NULL; + + InstancePtr->TotalDescriptorCount = 0; + InstancePtr->ActiveDescriptorCount = 0; + InstancePtr->IsReady = XCOMPONENT_IS_READY; + + /* initialize the version of the component + */ + XVersion_FromString(&InstancePtr->Version, "1.00a"); + + /* reset the DMA channel such that it's in a known state and ready + * and indicate the initialization occured with no errors, note that + * the is ready variable must be set before this call or reset will assert + */ + XDmaChannel_Reset(InstancePtr); + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_IsReady +* +* DESCRIPTION: +* +* This function determines if a DMA channel component has been successfully +* initialized such that it's ready to use. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. +* +* RETURN VALUE: +* +* TRUE if the DMA channel component is ready, FALSE otherwise. +* +* NOTES: +* +* None. +* +******************************************************************************/ +u32 +XDmaChannel_IsReady(XDmaChannel * InstancePtr) +{ + /* assert to verify input arguments used by the base component */ + + XASSERT_NONVOID(InstancePtr != NULL); + + return InstancePtr->IsReady == XCOMPONENT_IS_READY; +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_GetVersion +* +* DESCRIPTION: +* +* This function gets the software version for the specified DMA channel +* component. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. +* +* RETURN VALUE: +* +* A pointer to the software version of the specified DMA channel. +* +* NOTES: +* +* None. +* +******************************************************************************/ +XVersion * +XDmaChannel_GetVersion(XDmaChannel * InstancePtr) +{ + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* return a pointer to the version of the DMA channel */ + + return &InstancePtr->Version; +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_SelfTest +* +* DESCRIPTION: +* +* This function performs a self test on the specified DMA channel. This self +* test is destructive as the DMA channel is reset and a register default is +* verified. +* +* ARGUMENTS: +* +* InstancePtr is a pointer to the DMA channel to be operated on. +* +* RETURN VALUE: +* +* XST_SUCCESS is returned if the self test is successful, or one of the +* following errors. +* +* XST_DMA_RESET_REGISTER_ERROR Indicates the control register value +* after a reset was not correct +* +* NOTES: +* +* This test does not performs a DMA transfer to test the channel because the +* DMA hardware will not currently allow a non-local memory transfer to non-local +* memory (memory copy), but only allows a non-local memory to or from the device +* memory (typically a FIFO). +* +******************************************************************************/ + +#define XDC_CONTROL_REG_RESET_MASK 0x98000000UL /* control reg reset value */ + +XStatus +XDmaChannel_SelfTest(XDmaChannel * InstancePtr) +{ + u32 ControlReg; + + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* reset the DMA channel such that it's in a known state before the test + * it resets to no interrupts enabled, the desired state for the test + */ + XDmaChannel_Reset(InstancePtr); + + /* this should be the first test to help prevent a lock up with the polling + * loop that occurs later in the test, check the reset value of the DMA + * control register to make sure it's correct, return with an error if not + */ + ControlReg = XDmaChannel_GetControl(InstancePtr); + if (ControlReg != XDC_CONTROL_REG_RESET_MASK) { + return XST_DMA_RESET_REGISTER_ERROR; + } + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_Reset +* +* DESCRIPTION: +* +* This function resets the DMA channel. This is a destructive operation such +* that it should not be done while a channel is being used. If the DMA channel +* is transferring data into other blocks, such as a FIFO, it may be necessary +* to reset other blocks. This function does not modify the contents of a +* scatter gather list for a DMA channel such that the user is responsible for +* getting buffer descriptors from the list if necessary. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* None. +* +******************************************************************************/ +void +XDmaChannel_Reset(XDmaChannel * InstancePtr) +{ + /* assert to verify input arguments */ + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* reset the DMA channel such that it's in a known state, the reset + * register is self clearing such that it only has to be set + */ + XIo_Out32(InstancePtr->RegBaseAddress + XDC_RST_REG_OFFSET, + XDC_RESET_MASK); +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_GetControl +* +* DESCRIPTION: +* +* This function gets the control register contents of the DMA channel. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. +* +* RETURN VALUE: +* +* The control register contents of the DMA channel. One or more of the +* following values may be contained the register. Each of the values are +* unique bit masks. +* +* XDC_DMACR_SOURCE_INCR_MASK Increment the source address +* XDC_DMACR_DEST_INCR_MASK Increment the destination address +* XDC_DMACR_SOURCE_LOCAL_MASK Local source address +* XDC_DMACR_DEST_LOCAL_MASK Local destination address +* XDC_DMACR_SG_ENABLE_MASK Scatter gather enable +* XDC_DMACR_GEN_BD_INTR_MASK Individual buffer descriptor interrupt +* XDC_DMACR_LAST_BD_MASK Last buffer descriptor in a packet +* +* NOTES: +* +* None. +* +******************************************************************************/ +u32 +XDmaChannel_GetControl(XDmaChannel * InstancePtr) +{ + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* return the contents of the DMA control register */ + + return XIo_In32(InstancePtr->RegBaseAddress + XDC_DMAC_REG_OFFSET); +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_SetControl +* +* DESCRIPTION: +* +* This function sets the control register of the specified DMA channel. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. +* +* Control contains the value to be written to the control register of the DMA +* channel. One or more of the following values may be contained the register. +* Each of the values are unique bit masks such that they may be ORed together +* to enable multiple bits or inverted and ANDed to disable multiple bits. +* +* XDC_DMACR_SOURCE_INCR_MASK Increment the source address +* XDC_DMACR_DEST_INCR_MASK Increment the destination address +* XDC_DMACR_SOURCE_LOCAL_MASK Local source address +* XDC_DMACR_DEST_LOCAL_MASK Local destination address +* XDC_DMACR_SG_ENABLE_MASK Scatter gather enable +* XDC_DMACR_GEN_BD_INTR_MASK Individual buffer descriptor interrupt +* XDC_DMACR_LAST_BD_MASK Last buffer descriptor in a packet +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* None. +* +******************************************************************************/ +void +XDmaChannel_SetControl(XDmaChannel * InstancePtr, u32 Control) +{ + /* assert to verify input arguments except the control which can't be + * asserted since all values are valid + */ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* set the DMA control register to the specified value */ + + XIo_Out32(InstancePtr->RegBaseAddress + XDC_DMAC_REG_OFFSET, Control); +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_GetStatus +* +* DESCRIPTION: +* +* This function gets the status register contents of the DMA channel. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. +* +* RETURN VALUE: +* +* The status register contents of the DMA channel. One or more of the +* following values may be contained the register. Each of the values are +* unique bit masks. +* +* XDC_DMASR_BUSY_MASK The DMA channel is busy +* XDC_DMASR_BUS_ERROR_MASK A bus error occurred +* XDC_DMASR_BUS_TIMEOUT_MASK A bus timeout occurred +* XDC_DMASR_LAST_BD_MASK The last buffer descriptor of a packet +* +* NOTES: +* +* None. +* +******************************************************************************/ +u32 +XDmaChannel_GetStatus(XDmaChannel * InstancePtr) +{ + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* return the contents of the DMA status register */ + + return XIo_In32(InstancePtr->RegBaseAddress + XDC_DMAS_REG_OFFSET); +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_SetIntrStatus +* +* DESCRIPTION: +* +* This function sets the interrupt status register of the specified DMA channel. +* Setting any bit of the interrupt status register will clear the bit to +* indicate the interrupt processing has been completed. The definitions of each +* bit in the register match the definition of the bits in the interrupt enable +* register. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. +* +* Status contains the value to be written to the status register of the DMA +* channel. One or more of the following values may be contained the register. +* Each of the values are unique bit masks such that they may be ORed together +* to enable multiple bits or inverted and ANDed to disable multiple bits. +* +* XDC_IXR_DMA_DONE_MASK The dma operation is done +* XDC_IXR_DMA_ERROR_MASK The dma operation had an error +* XDC_IXR_PKT_DONE_MASK A packet is complete +* XDC_IXR_PKT_THRESHOLD_MASK The packet count threshold reached +* XDC_IXR_PKT_WAIT_BOUND_MASK The packet wait bound reached +* XDC_IXR_SG_DISABLE_ACK_MASK The scatter gather disable completed +* XDC_IXR_BD_MASK A buffer descriptor is done +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* None. +* +******************************************************************************/ +void +XDmaChannel_SetIntrStatus(XDmaChannel * InstancePtr, u32 Status) +{ + /* assert to verify input arguments except the status which can't be + * asserted since all values are valid + */ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* set the interrupt status register with the specified value such that + * all bits which are set in the register are cleared effectively clearing + * any active interrupts + */ + XIo_Out32(InstancePtr->RegBaseAddress + XDC_IS_REG_OFFSET, Status); +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_GetIntrStatus +* +* DESCRIPTION: +* +* This function gets the interrupt status register of the specified DMA channel. +* The interrupt status register indicates which interrupts are active +* for the DMA channel. If an interrupt is active, the status register must be +* set (written) with the bit set for each interrupt which has been processed +* in order to clear the interrupts. The definitions of each bit in the register +* match the definition of the bits in the interrupt enable register. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. +* +* RETURN VALUE: +* +* The interrupt status register contents of the specified DMA channel. +* One or more of the following values may be contained the register. +* Each of the values are unique bit masks. +* +* XDC_IXR_DMA_DONE_MASK The dma operation is done +* XDC_IXR_DMA_ERROR_MASK The dma operation had an error +* XDC_IXR_PKT_DONE_MASK A packet is complete +* XDC_IXR_PKT_THRESHOLD_MASK The packet count threshold reached +* XDC_IXR_PKT_WAIT_BOUND_MASK The packet wait bound reached +* XDC_IXR_SG_DISABLE_ACK_MASK The scatter gather disable completed +* XDC_IXR_SG_END_MASK Current descriptor was the end of the list +* XDC_IXR_BD_MASK A buffer descriptor is done +* +* NOTES: +* +* None. +* +******************************************************************************/ +u32 +XDmaChannel_GetIntrStatus(XDmaChannel * InstancePtr) +{ + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* return the contents of the interrupt status register */ + + return XIo_In32(InstancePtr->RegBaseAddress + XDC_IS_REG_OFFSET); +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_SetIntrEnable +* +* DESCRIPTION: +* +* This function sets the interrupt enable register of the specified DMA +* channel. The interrupt enable register contains bits which enable +* individual interrupts for the DMA channel. The definitions of each bit +* in the register match the definition of the bits in the interrupt status +* register. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. +* +* Enable contains the interrupt enable register contents to be written +* in the DMA channel. One or more of the following values may be contained +* the register. Each of the values are unique bit masks such that they may be +* ORed together to enable multiple bits or inverted and ANDed to disable +* multiple bits. +* +* XDC_IXR_DMA_DONE_MASK The dma operation is done +* XDC_IXR_DMA_ERROR_MASK The dma operation had an error +* XDC_IXR_PKT_DONE_MASK A packet is complete +* XDC_IXR_PKT_THRESHOLD_MASK The packet count threshold reached +* XDC_IXR_PKT_WAIT_BOUND_MASK The packet wait bound reached +* XDC_IXR_SG_DISABLE_ACK_MASK The scatter gather disable completed +* XDC_IXR_SG_END_MASK Current descriptor was the end of the list +* XDC_IXR_BD_MASK A buffer descriptor is done +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* None. +* +******************************************************************************/ +void +XDmaChannel_SetIntrEnable(XDmaChannel * InstancePtr, u32 Enable) +{ + /* assert to verify input arguments except the enable which can't be + * asserted since all values are valid + */ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* set the interrupt enable register to the specified value */ + + XIo_Out32(InstancePtr->RegBaseAddress + XDC_IE_REG_OFFSET, Enable); +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_GetIntrEnable +* +* DESCRIPTION: +* +* This function gets the interrupt enable of the DMA channel. The +* interrupt enable contains flags which enable individual interrupts for the +* DMA channel. The definitions of each bit in the register match the definition +* of the bits in the interrupt status register. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. +* +* RETURN VALUE: +* +* The interrupt enable of the DMA channel. One or more of the following values +* may be contained the register. Each of the values are unique bit masks. +* +* XDC_IXR_DMA_DONE_MASK The dma operation is done +* XDC_IXR_DMA_ERROR_MASK The dma operation had an error +* XDC_IXR_PKT_DONE_MASK A packet is complete +* XDC_IXR_PKT_THRESHOLD_MASK The packet count threshold reached +* XDC_IXR_PKT_WAIT_BOUND_MASK The packet wait bound reached +* XDC_IXR_SG_DISABLE_ACK_MASK The scatter gather disable completed +* XDC_IXR_BD_MASK A buffer descriptor is done +* +* NOTES: +* +* None. +* +******************************************************************************/ +u32 +XDmaChannel_GetIntrEnable(XDmaChannel * InstancePtr) +{ + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* return the contents of the interrupt enable register */ + + return XIo_In32(InstancePtr->RegBaseAddress + XDC_IE_REG_OFFSET); +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_Transfer +* +* DESCRIPTION: +* +* This function starts the DMA channel transferring data from a memory source +* to a memory destination. This function only starts the operation and returns +* before the operation may be complete. If the interrupt is enabled, an +* interrupt will be generated when the operation is complete, otherwise it is +* necessary to poll the channel status to determine when it's complete. It is +* the responsibility of the caller to determine when the operation is complete +* by handling the generated interrupt or polling the status. It is also the +* responsibility of the caller to ensure that the DMA channel is not busy with +* another transfer before calling this function. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. +* +* SourcePtr contains a pointer to the source memory where the data is to +* be tranferred from and must be 32 bit aligned. +* +* DestinationPtr contains a pointer to the destination memory where the data +* is to be transferred and must be 32 bit aligned. +* +* ByteCount contains the number of bytes to transfer during the DMA operation. +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* The DMA h/w will not currently allow a non-local memory transfer to non-local +* memory (memory copy), but only allows a non-local memory to or from the device +* memory (typically a FIFO). +* +* It is the responsibility of the caller to ensure that the cache is +* flushed and invalidated both before and after the DMA operation completes +* if the memory pointed to is cached. The caller must also ensure that the +* pointers contain a physical address rather than a virtual address +* if address translation is being used. +* +******************************************************************************/ +void +XDmaChannel_Transfer(XDmaChannel * InstancePtr, + u32 * SourcePtr, u32 * DestinationPtr, u32 ByteCount) +{ + /* assert to verify input arguments and the alignment of any arguments + * which have expected alignments + */ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(SourcePtr != NULL); + XASSERT_VOID(((u32) SourcePtr & 3) == 0); + XASSERT_VOID(DestinationPtr != NULL); + XASSERT_VOID(((u32) DestinationPtr & 3) == 0); + XASSERT_VOID(ByteCount != 0); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* setup the source and destination address registers for the transfer */ + + XIo_Out32(InstancePtr->RegBaseAddress + XDC_SA_REG_OFFSET, + (u32) SourcePtr); + + XIo_Out32(InstancePtr->RegBaseAddress + XDC_DA_REG_OFFSET, + (u32) DestinationPtr); + + /* start the DMA transfer to copy from the source buffer to the + * destination buffer by writing the length to the length register + */ + XIo_Out32(InstancePtr->RegBaseAddress + XDC_LEN_REG_OFFSET, ByteCount); +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xdma_channel.h linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xdma_channel.h --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xdma_channel.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xdma_channel.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,292 @@ +/* $Id: xdma_channel.h,v 1.2 2001/10/19 14:58:03 linnj Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +* FILENAME: +* +* xdma_channel.h +* +* DESCRIPTION: +* +* This file contains the DMA channel component implementation. This component +* supports a distributed DMA design in which each device can have it's own +* dedicated DMA channel, as opposed to a centralized DMA design. +* A device which uses DMA typically contains two DMA channels, one for +* sending data and the other for receiving data. +* +* This component is designed to be used as a basic building block for +* designing a device driver. It provides registers accesses such that all +* DMA processing can be maintained easier, but the device driver designer +* must still understand all the details of the DMA channel. +* +* The DMA channel allows a CPU to minimize the CPU interaction required to move +* data between a memory and a device. The CPU requests the DMA channel to +* perform a DMA operation and typically continues performing other processing +* until the DMA operation completes. DMA could be considered a primitive form +* of multiprocessing such that caching and address translation can be an issue. +* +* Scatter Gather Operations +* +* The DMA channel may support scatter gather operations. A scatter gather +* operation automates the DMA channel such that multiple buffers can be +* sent or received with minimal software interaction with the hardware. Buffer +* descriptors, contained in the XBufDescriptor component, are used by the +* scatter gather operations of the DMA channel to describe the buffers to be +* processed. +* +* Scatter Gather List Operations +* +* A scatter gather list may be supported by each DMA channel. The scatter +* gather list allows buffer descriptors to be put into the list by a device +* driver which requires scatter gather. The hardware processes the buffer +* descriptors which are contained in the list and modifies the buffer +* descriptors to reflect the status of the DMA operations. The device driver +* is notified by interrupt that specific DMA events occur including scatter +* gather events. The device driver removes the completed buffer descriptors +* from the scatter gather list to evaluate the status of each DMA operation. +* +* The scatter gather list is created and buffer descriptors are inserted into +* the list. Buffer descriptors are never removed from the list after it's +* creation such that a put operation copies from a temporary buffer descriptor +* to a buffer descriptor in the list. Get operations don't copy from the list +* to a temporary, but return a pointer to the buffer descriptor in the list. +* A buffer descriptor in the list may be locked to prevent it from being +* overwritten by a put operation. This allows the device driver to get a +* descriptor from a scatter gather list and prevent it from being overwritten +* until the buffer associated with the buffer descriptor has been processed. +* +* Typical Scatter Gather Processing +* +* The following steps illustrate the typical processing to use the +* scatter gather features of a DMA channel. +* +* 1. Create a scatter gather list for the DMA channel which puts empty buffer +* descriptors into the list. +* 2. Create buffer descriptors which describe the buffers to be filled with +* receive data or the buffers which contain data to be sent. +* 3. Put buffer descriptors into the DMA channel scatter list such that scatter +* gather operations are requested. +* 4. Commit the buffer descriptors in the list such that they are ready to be +* used by the DMA channel hardware. +* 5. Start the scatter gather operations of the DMA channel. +* 6. Process any interrupts which occur as a result of the scatter gather +* operations or poll the DMA channel to determine the status. +* +* Interrupts +* +* Each DMA channel has the ability to generate an interrupt. This component +* does not perform processing for the interrupt as this processing is typically +* tightly coupled with the device which is using the DMA channel. It is the +* responsibility of the caller of DMA functions to manage the interrupt +* including connecting to the interrupt and enabling/disabling the interrupt. +* +* Critical Sections +* +* It is the responsibility of the device driver designer to use critical +* sections as necessary when calling functions of the DMA channel. This +* component does not use critical sections and it does access registers using +* read-modify-write operations. Calls to DMA functions from a main thread +* and from an interrupt context could produce unpredictable behavior such that +* the caller must provide the appropriate critical sections. +* +* Address Translation +* +* All addresses of data structures which are passed to DMA functions must +* be physical (real) addresses as opposed to logical (virtual) addresses. +* +* Caching +* +* The memory which is passed to the function which creates the scatter gather +* list must not be cached such that buffer descriptors are non-cached. This +* is necessary because the buffer descriptors are kept in a ring buffer and +* not directly accessible to the caller of DMA functions. +* +* The caller of DMA functions is responsible for ensuring that any data +* buffers which are passed to the DMA channel are cache-line aligned if +* necessary. +* +* The caller of DMA functions is responsible for ensuring that any data +* buffers which are passed to the DMA channel have been flushed from the cache. +* +* The caller of DMA functions is responsible for ensuring that the cache is +* invalidated prior to using any data buffers which are the result of a DMA +* operation. +* +* Memory Alignment +* +* The addresses of data buffers which are passed to DMA functions must be +* 32 bit word aligned since the DMA hardware performs 32 bit word transfers. +* +* Mutual Exclusion +* +* The functions of the DMA channel are not thread safe such that the caller +* of all DMA functions is responsible for ensuring mutual exclusion for a +* DMA channel. Mutual exclusion across multiple DMA channels is not +* necessary. +* +* NOTES: +* +* Many of the provided functions which are register accessors don't provide +* a lot of error detection. The caller is expected to understand the impact +* of a function call based upon the current state of the DMA channel. This +* is done to minimize the overhead in this component. +* +******************************************************************************/ + +#ifndef XDMA_CHANNEL_H /* prevent circular inclusions */ +#define XDMA_CHANNEL_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xdma_channel_i.h" /* constants shared with buffer descriptor */ +#include "xbasic_types.h" +#include "xstatus.h" +#include "xversion.h" +#include "xbuf_descriptor.h" + +/************************** Constant Definitions *****************************/ + +/* the following constants provide access to the bit fields of the DMA control + * register (DMACR) + */ +#define XDC_DMACR_SOURCE_INCR_MASK 0x80000000UL /* increment source address */ +#define XDC_DMACR_DEST_INCR_MASK 0x40000000UL /* increment dest address */ +#define XDC_DMACR_SOURCE_LOCAL_MASK 0x20000000UL /* local source address */ +#define XDC_DMACR_DEST_LOCAL_MASK 0x10000000UL /* local dest address */ +#define XDC_DMACR_SG_DISABLE_MASK 0x08000000UL /* scatter gather disable */ +#define XDC_DMACR_GEN_BD_INTR_MASK 0x04000000UL /* descriptor interrupt */ +#define XDC_DMACR_LAST_BD_MASK XDC_CONTROL_LAST_BD_MASK /* last buffer */ + /* descriptor */ + +/* the following constants provide access to the bit fields of the DMA status + * register (DMASR) + */ +#define XDC_DMASR_BUSY_MASK 0x80000000UL /* channel is busy */ +#define XDC_DMASR_BUS_ERROR_MASK 0x40000000UL /* bus error occurred */ +#define XDC_DMASR_BUS_TIMEOUT_MASK 0x20000000UL /* bus timeout occurred */ +#define XDC_DMASR_LAST_BD_MASK XDC_STATUS_LAST_BD_MASK /* last buffer */ + /* descriptor */ +#define XDC_DMASR_SG_BUSY_MASK 0x08000000UL /* scatter gather is busy */ + +/* the following constants provide access to the bit fields of the interrupt + * status register (ISR) and the interrupt enable register (IER), bit masks + * match for both registers such that they are named IXR + */ +#define XDC_IXR_DMA_DONE_MASK 0x1UL /* dma operation done */ +#define XDC_IXR_DMA_ERROR_MASK 0x2UL /* dma operation error */ +#define XDC_IXR_PKT_DONE_MASK 0x4UL /* packet done */ +#define XDC_IXR_PKT_THRESHOLD_MASK 0x8UL /* packet count threshold */ +#define XDC_IXR_PKT_WAIT_BOUND_MASK 0x10UL /* packet wait bound reached */ +#define XDC_IXR_SG_DISABLE_ACK_MASK 0x20UL /* scatter gather disable + acknowledge occurred */ +#define XDC_IXR_SG_END_MASK 0x40UL /* last buffer descriptor + disabled scatter gather */ +#define XDC_IXR_BD_MASK 0x80UL /* buffer descriptor done */ + +/**************************** Type Definitions *******************************/ + +/* + * the following structure contains data which is on a per instance basis + * for the XDmaChannel component + */ +typedef struct XDmaChannelTag { + XVersion Version; /* version of the driver */ + u32 RegBaseAddress; /* base address of registers */ + u32 IsReady; /* device is initialized and ready */ + + XBufDescriptor *PutPtr; /* keep track of where to put into list */ + XBufDescriptor *GetPtr; /* keep track of where to get from list */ + XBufDescriptor *CommitPtr; /* keep track of where to commit in list */ + XBufDescriptor *LastPtr; /* keep track of the last put in the list */ + u32 TotalDescriptorCount; /* total # of descriptors in the list */ + u32 ActiveDescriptorCount; /* # of descriptors pointing to buffers + * in the buffer descriptor list */ +} XDmaChannel; + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +XStatus XDmaChannel_Initialize(XDmaChannel * InstancePtr, u32 BaseAddress); +u32 XDmaChannel_IsReady(XDmaChannel * InstancePtr); +XVersion *XDmaChannel_GetVersion(XDmaChannel * InstancePtr); +XStatus XDmaChannel_SelfTest(XDmaChannel * InstancePtr); +void XDmaChannel_Reset(XDmaChannel * InstancePtr); + +/* Control functions */ + +u32 XDmaChannel_GetControl(XDmaChannel * InstancePtr); +void XDmaChannel_SetControl(XDmaChannel * InstancePtr, u32 Control); + +/* Status functions */ + +u32 XDmaChannel_GetStatus(XDmaChannel * InstancePtr); +void XDmaChannel_SetIntrStatus(XDmaChannel * InstancePtr, u32 Status); +u32 XDmaChannel_GetIntrStatus(XDmaChannel * InstancePtr); +void XDmaChannel_SetIntrEnable(XDmaChannel * InstancePtr, u32 Enable); +u32 XDmaChannel_GetIntrEnable(XDmaChannel * InstancePtr); + +/* DMA without scatter gather functions */ + +void XDmaChannel_Transfer(XDmaChannel * InstancePtr, + u32 * SourcePtr, u32 * DestinationPtr, u32 ByteCount); + +/* Scatter gather functions */ + +XStatus XDmaChannel_SgStart(XDmaChannel * InstancePtr); +XStatus XDmaChannel_SgStop(XDmaChannel * InstancePtr, + XBufDescriptor ** BufDescriptorPtr); +XStatus XDmaChannel_CreateSgList(XDmaChannel * InstancePtr, + u32 * MemoryPtr, u32 ByteCount); +u32 XDmaChannel_IsSgListEmpty(XDmaChannel * InstancePtr); + +XStatus XDmaChannel_PutDescriptor(XDmaChannel * InstancePtr, + XBufDescriptor * BufDescriptorPtr); +XStatus XDmaChannel_CommitPuts(XDmaChannel * InstancePtr); +XStatus XDmaChannel_GetDescriptor(XDmaChannel * InstancePtr, + XBufDescriptor ** BufDescriptorPtr); + +/* Packet functions for interrupt collescing */ + +u32 XDmaChannel_GetPktCount(XDmaChannel * InstancePtr); +void XDmaChannel_DecrementPktCount(XDmaChannel * InstancePtr); +XStatus XDmaChannel_SetPktThreshold(XDmaChannel * InstancePtr, u8 Threshold); +u8 XDmaChannel_GetPktThreshold(XDmaChannel * InstancePtr); +void XDmaChannel_SetPktWaitBound(XDmaChannel * InstancePtr, u32 WaitBound); +u32 XDmaChannel_GetPktWaitBound(XDmaChannel * InstancePtr); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xdma_channel_i.h linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xdma_channel_i.h --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xdma_channel_i.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xdma_channel_i.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,111 @@ +/* $Id: xdma_channel_i.h,v 1.1 2001/10/12 17:18:16 linnj Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +* FILENAME: +* +* xdma_channel_i.h +* +* DESCRIPTION: +* +* This file contains data which is shared internal data for the DMA channel +* component. It is also shared with the buffer descriptor component which is +* very tightly coupled with the DMA channel component. +* +* NOTES: +* +* The last buffer descriptor constants must be located here to prevent a +* circular dependency between the DMA channel component and the buffer +* descriptor component. +* +******************************************************************************/ + +#ifndef XDMA_CHANNEL_I_H /* prevent circular inclusions */ +#define XDMA_CHANNEL_I_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xstatus.h" +#include "xversion.h" + +/************************** Constant Definitions *****************************/ + +#define XDC_DMA_CHANNEL_V1_00_A "1.00a" + +/* the following constant provides access to the bit fields of the DMA control + * register (DMACR) which must be shared between the DMA channel component + * and the buffer descriptor component + */ +#define XDC_CONTROL_LAST_BD_MASK 0x02000000UL /* last buffer descriptor */ + +/* the following constant provides access to the bit fields of the DMA status + * register (DMASR) which must be shared between the DMA channel component + * and the buffer descriptor component + */ +#define XDC_STATUS_LAST_BD_MASK 0x10000000UL /* last buffer descriptor */ + +/* the following constants provide access to each of the registers of a DMA + * channel + */ +#define XDC_RST_REG_OFFSET 0 /* reset register */ +#define XDC_MI_REG_OFFSET 0 /* module information register */ +#define XDC_DMAC_REG_OFFSET 4 /* DMA control register */ +#define XDC_SA_REG_OFFSET 8 /* source address register */ +#define XDC_DA_REG_OFFSET 12 /* destination address register */ +#define XDC_LEN_REG_OFFSET 16 /* length register */ +#define XDC_DMAS_REG_OFFSET 20 /* DMA status register */ +#define XDC_BDA_REG_OFFSET 24 /* buffer descriptor address register */ +#define XDC_SWCR_REG_OFFSET 28 /* software control register */ +#define XDC_UPC_REG_OFFSET 32 /* unserviced packet count register */ +#define XDC_PCT_REG_OFFSET 36 /* packet count threshold register */ +#define XDC_PWB_REG_OFFSET 40 /* packet wait bound register */ +#define XDC_IS_REG_OFFSET 44 /* interrupt status register */ +#define XDC_IE_REG_OFFSET 48 /* interrupt enable register */ + +/* the following constant is written to the reset register to reset the + * DMA channel + */ +#define XDC_RESET_MASK 0x0000000AUL + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xdma_channel_sg.c linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xdma_channel_sg.c --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xdma_channel_sg.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xdma_channel_sg.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,1318 @@ +/* $Id: xdma_channel_sg.c,v 1.6 2003/02/03 19:50:33 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +* FILENAME: +* +* xdma_channel_sg.c +* +* DESCRIPTION: +* +* This file contains the implementation of the XDmaChannel component which is +* related to scatter gather operations. +* +* Scatter Gather Operations +* +* The DMA channel may support scatter gather operations. A scatter gather +* operation automates the DMA channel such that multiple buffers can be +* sent or received with minimal software interaction with the hardware. Buffer +* descriptors, contained in the XBufDescriptor component, are used by the +* scatter gather operations of the DMA channel to describe the buffers to be +* processed. +* +* Scatter Gather List Operations +* +* A scatter gather list may be supported by each DMA channel. The scatter +* gather list allows buffer descriptors to be put into the list by a device +* driver which requires scatter gather. The hardware processes the buffer +* descriptors which are contained in the list and modifies the buffer +* descriptors to reflect the status of the DMA operations. The device driver +* is notified by interrupt that specific DMA events occur including scatter +* gather events. The device driver removes the completed buffer descriptors +* from the scatter gather list to evaluate the status of each DMA operation. +* +* The scatter gather list is created and buffer descriptors are inserted into +* the list. Buffer descriptors are never removed from the list after it's +* creation such that a put operation copies from a temporary buffer descriptor +* to a buffer descriptor in the list. Get operations don't copy from the list +* to a temporary, but return a pointer to the buffer descriptor in the list. +* A buffer descriptor in the list may be locked to prevent it from being +* overwritten by a put operation. This allows the device driver to get a +* descriptor from a scatter gather list and prevent it from being overwritten +* until the buffer associated with the buffer descriptor has been processed. +* +* The get and put functions only operate on the list and are asynchronous from +* the hardware which may be using the list of descriptors. This is important +* because there are no checks in the get and put functions to ensure that the +* hardware has processed the descriptors. This must be handled by the driver +* using the DMA scatter gather channel through the use of the other functions. +* When a scatter gather operation is started, the start function does ensure +* that the descriptor to start has not already been processed by the hardware +* and is not the first of a series of descriptors that have not been committed +* yet. +* +* Descriptors are put into the list but not marked as ready to use by the +* hardware until a commit operation is done. This allows multiple descriptors +* which may contain a single packet of information for a protocol to be +* guaranteed not to cause any underflow conditions during transmission. The +* hardware design only allows descriptors to cause it to stop after a descriptor +* has been processed rather than before it is processed. A series of +* descriptors are put into the list followed by a commit operation, or each +* descriptor may be commited. A commit operation is performed by changing a +* single descriptor, the first of the series of puts, to indicate that the +* hardware may now use all descriptors after it. The last descriptor in the +* list is always set to cause the hardware to stop after it is processed. +* +* Typical Scatter Gather Processing +* +* The following steps illustrate the typical processing to use the +* scatter gather features of a DMA channel. +* +* 1. Create a scatter gather list for the DMA channel which puts empty buffer +* descriptors into the list. +* 2. Create buffer descriptors which describe the buffers to be filled with +* receive data or the buffers which contain data to be sent. +* 3. Put buffer descriptors into the DMA channel scatter list such that scatter +* gather operations are requested. +* 4. Commit the buffer descriptors in the list such that they are ready to be +* used by the DMA channel hardware. +* 5. Start the scatter gather operations of the DMA channel. +* 6. Process any interrupts which occur as a result of the scatter gather +* operations or poll the DMA channel to determine the status. This may +* be accomplished by getting the packet count for the channel and then +* getting the appropriate number of descriptors from the list for that +* number of packets. +* +* Minimizing Interrupts +* +* The Scatter Gather operating mode is designed to reduce the amount of CPU +* throughput necessary to manage the hardware for devices. A key to the CPU +* throughput is the number and rate of interrupts that the CPU must service. +* Devices with higher data rates can cause larger numbers of interrupts and +* higher frequency interrupts. Ideally the number of interrupts can be reduced +* by only generating an interrupt when a specific amount of data has been +* received from the interface. This design suffers from a lack of interrupts +* when the amount of data received is less than the specified amount of data +* to generate an interrupt. In order to help minimize the number of interrupts +* which the CPU must service, an algorithm referred to as "interrupt coalescing" +* is utilized. +* +* Interrupt Coalescing +* +* The principle of interrupt coalescing is to wait before generating an +* interrupt until a certain number of packets have been received or sent. An +* interrupt is also generated if a smaller number of packets have been received +* followed by a certain period of time with no packet reception. This is a +* trade-off of latency for bandwidth and is accomplished using several +* mechanisms of the hardware including a counter for packets received or +* transmitted and a packet timer. These two hardware mechanisms work in +* combination to allow a reduction in the number of interrupts processed by the +* CPU for packet reception. +* +* Unserviced Packet Count +* +* The purpose of the packet counter is to count the number of packets received +* or transmitted and provide an interrupt when a specific number of packets +* have been processed by the hardware. An interrupt is generated whenever the +* counter is greater than or equal to the Packet Count Threshold. This counter +* contains an accurate count of the number of packets that the hardware has +* processed, either received or transmitted, and the software has not serviced. +* +* The packet counter allows the number of interrupts to be reduced by waiting +* to generate an interrupt until enough packets are received. For packet +* reception, packet counts of less than the number to generate an interrupt +* would not be serviced without the addition of a packet timer. This counter is +* continuously updated by the hardware, not latched to the value at the time +* the interrupt occurred. +* +* The packet counter can be used within the interrupt service routine for the +* device to reduce the number of interrupts. The interrupt service routine +* loops while performing processing for each packet which has been received or +* transmitted and decrements the counter by a specified value. At the same time, +* the hardware is possibly continuing to receive or transmit more packets such +* that the software may choose, based upon the value in the packet counter, to +* remain in the interrupt service routine rather than exiting and immediately +* returning. This feature should be used with caution as reducing the number of +* interrupts is beneficial, but unbounded interrupt processing is not desirable. +* +* Since the hardware may be incrementing the packet counter simultaneously +* with the software decrementing the counter, there is a need for atomic +* operations. The hardware ensures that the operation is atomic such that +* simultaneous accesses are properly handled. +* +* Packet Wait Bound +* +* The purpose of the packet wait bound is to augment the unserviced packet +* count. Whenever there is no pending interrupt for the channel and the +* unserviced packet count is non-zero, a timer starts counting timeout at the +* value contained the the packet wait bound register. If the timeout is +* reached, an interrupt is generated such that the software may service the +* data which was buffered. +* +* NOTES: +* +* Special Test Conditions: +* +* The scatter gather list processing must be thoroughly tested if changes are +* made. Testing should include putting and committing single descriptors and +* putting multiple descriptors followed by a single commit. There are some +* conditions in the code which handle the exception conditions. +* +* The Put Pointer points to the next location in the descriptor list to copy +* in a new descriptor. The Get Pointer points to the next location in the +* list to get a descriptor from. The Get Pointer only allows software to +* have a traverse the list after the hardware has finished processing some +* number of descriptors. The Commit Pointer points to the descriptor in the +* list which is to be committed. It is also used to determine that no +* descriptor is waiting to be commited (NULL). The Last Pointer points to +* the last descriptor that was put into the list. It typically points +* to the previous descriptor to the one pointed to by the Put Pointer. +* Comparisons are done between these pointers to determine when the following +* special conditions exist. + +* Single Put And Commit +* +* The buffer descriptor is ready to be used by the hardware so it is important +* for the descriptor to not appear to be waiting to be committed. The commit +* pointer is reset when a commit is done indicating there are no descriptors +* waiting to be committed. In all cases but this one, the descriptor is +* changed to cause the hardware to go to the next descriptor after processing +* this one. But in this case, this is the last descriptor in the list such +* that it must not be changed. +* +* 3 Or More Puts And Commit +* +* A series of 3 or more puts followed by a single commit is different in that +* only the 1st descriptor put into the list is changed when the commit is done. +* This requires each put starting on the 3rd to change the previous descriptor +* so that it allows the hardware to continue to the next descriptor in the list. +* +* The 1st Put Following A Commit +* +* The commit caused the commit pointer to be NULL indicating that there are no +* descriptors waiting to be committed. It is necessary for the next put to set +* the commit pointer so that a commit must follow the put for the hardware to +* use the descriptor. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- ------------------------------------------------------
+* 1.00a rpm  02/03/03 Removed the XST_DMA_SG_COUNT_EXCEEDED return code
+*                     from SetPktThreshold.
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xdma_channel.h" +#include "xbasic_types.h" +#include "xio.h" +#include "xbuf_descriptor.h" +#include "xstatus.h" + +/************************** Constant Definitions *****************************/ + +#define XDC_SWCR_SG_ENABLE_MASK 0x80000000UL /* scatter gather enable */ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/* the following macro copies selected fields of a buffer descriptor to another + * buffer descriptor, this was provided by the buffer descriptor component but + * was moved here since it is only used internally to this component and since + * it does not copy all fields + */ +#define CopyBufferDescriptor(InstancePtr, DestinationPtr) \ +{ \ + *((u32 *)DestinationPtr + XBD_CONTROL_OFFSET) = \ + *((u32 *)InstancePtr + XBD_CONTROL_OFFSET); \ + *((u32 *)DestinationPtr + XBD_SOURCE_OFFSET) = \ + *((u32 *)InstancePtr + XBD_SOURCE_OFFSET); \ + *((u32 *)DestinationPtr + XBD_DESTINATION_OFFSET) = \ + *((u32 *)InstancePtr + XBD_DESTINATION_OFFSET); \ + *((u32 *)DestinationPtr + XBD_LENGTH_OFFSET) = \ + *((u32 *)InstancePtr + XBD_LENGTH_OFFSET); \ + *((u32 *)DestinationPtr + XBD_STATUS_OFFSET) = \ + *((u32 *)InstancePtr + XBD_STATUS_OFFSET); \ + *((u32 *)DestinationPtr + XBD_DEVICE_STATUS_OFFSET) = \ + *((u32 *)InstancePtr + XBD_DEVICE_STATUS_OFFSET); \ + *((u32 *)DestinationPtr + XBD_ID_OFFSET) = \ + *((u32 *)InstancePtr + XBD_ID_OFFSET); \ + *((u32 *)DestinationPtr + XBD_FLAGS_OFFSET) = \ + *((u32 *)InstancePtr + XBD_FLAGS_OFFSET); \ + *((u32 *)DestinationPtr + XBD_RQSTED_LENGTH_OFFSET) = \ + *((u32 *)InstancePtr + XBD_RQSTED_LENGTH_OFFSET); \ +} + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_SgStart +* +* DESCRIPTION: +* +* This function starts a scatter gather operation for a scatter gather +* DMA channel. The first buffer descriptor in the buffer descriptor list +* will be started with the scatter gather operation. A scatter gather list +* should have previously been created for the DMA channel and buffer +* descriptors put into the scatter gather list such that there are scatter +* operations ready to be performed. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. The DMA +* channel should be configured to use scatter gather in order for this function +* to be called. +* +* RETURN VALUE: +* +* A status containing XST_SUCCESS if scatter gather was started successfully +* for the DMA channel. +* +* A value of XST_DMA_SG_NO_LIST indicates the scatter gather list has not +* been created. +* +* A value of XST_DMA_SG_LIST_EMPTY indicates scatter gather was not started +* because the scatter gather list of the DMA channel does not contain any +* buffer descriptors that are ready to be processed by the hardware. +* +* A value of XST_DMA_SG_IS_STARTED indicates scatter gather was not started +* because the scatter gather was not stopped, but was already started. +* +* A value of XST_DMA_SG_BD_NOT_COMMITTED indicates the buffer descriptor of +* scatter gather list which was to be started is not committed to the list. +* This status is more likely if this function is being called from an ISR +* and non-ISR processing is putting descriptors into the list. +* +* A value of XST_DMA_SG_NO_DATA indicates that the buffer descriptor of the +* scatter gather list which was to be started had already been used by the +* hardware for a DMA transfer that has been completed. +* +* NOTES: +* +* It is the responsibility of the caller to get all the buffer descriptors +* after performing a stop operation and before performing a start operation. +* If buffer descriptors are not retrieved between stop and start operations, +* buffer descriptors may be processed by the hardware more than once. +* +******************************************************************************/ +XStatus +XDmaChannel_SgStart(XDmaChannel * InstancePtr) +{ + u32 Register; + XBufDescriptor *LastDescriptorPtr; + + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* if a scatter gather list has not been created yet, return a status */ + + if (InstancePtr->TotalDescriptorCount == 0) { + return XST_DMA_SG_NO_LIST; + } + + /* if the scatter gather list exists but is empty then return a status */ + + if (XDmaChannel_IsSgListEmpty(InstancePtr)) { + return XST_DMA_SG_LIST_EMPTY; + } + + /* if scatter gather is busy for the DMA channel, return a status because + * restarting it could lose data + */ + + Register = XIo_In32(InstancePtr->RegBaseAddress + XDC_DMAS_REG_OFFSET); + if (Register & XDC_DMASR_SG_BUSY_MASK) { + return XST_DMA_SG_IS_STARTED; + } + + /* get the address of the last buffer descriptor which the DMA hardware + * finished processing + */ + LastDescriptorPtr = + (XBufDescriptor *) XIo_In32(InstancePtr->RegBaseAddress + + XDC_BDA_REG_OFFSET); + + /* setup the first buffer descriptor that will be sent when the scatter + * gather channel is enabled, this is only necessary one time since + * the BDA register of the channel maintains the last buffer descriptor + * processed + */ + if (LastDescriptorPtr == NULL) { + XIo_Out32(InstancePtr->RegBaseAddress + XDC_BDA_REG_OFFSET, + (u32) InstancePtr->GetPtr); + } else { + XBufDescriptor *NextDescriptorPtr; + + /* get the next descriptor to be started, if the status indicates it + * hasn't already been used by the h/w, then it's OK to start it, + * s/w sets the status of each descriptor to busy and then h/w clears + * the busy when it is complete + */ + NextDescriptorPtr = + XBufDescriptor_GetNextPtr(LastDescriptorPtr); + + if ((XBufDescriptor_GetStatus(NextDescriptorPtr) & + XDC_DMASR_BUSY_MASK) == 0) { + return XST_DMA_SG_NO_DATA; + } + /* don't start the DMA SG channel if the descriptor to be processed + * by h/w is to be committed by the s/w, this function can be called + * such that it interrupts a thread that was putting into the list + */ + if (NextDescriptorPtr == InstancePtr->CommitPtr) { + return XST_DMA_SG_BD_NOT_COMMITTED; + } + } + + /* start the scatter gather operation by clearing the stop bit in the + * control register and setting the enable bit in the s/w control register, + * both of these are necessary to cause it to start, right now the order of + * these statements is important, the software control register should be + * set 1st. The other order can cause the CPU to have a loss of sync + * because it cannot read/write the register while the DMA operation is + * running + */ + + Register = XIo_In32(InstancePtr->RegBaseAddress + XDC_SWCR_REG_OFFSET); + + XIo_Out32(InstancePtr->RegBaseAddress + XDC_SWCR_REG_OFFSET, + Register | XDC_SWCR_SG_ENABLE_MASK); + + Register = XIo_In32(InstancePtr->RegBaseAddress + XDC_DMAC_REG_OFFSET); + + XIo_Out32(InstancePtr->RegBaseAddress + XDC_DMAC_REG_OFFSET, + Register & ~XDC_DMACR_SG_DISABLE_MASK); + + /* indicate the DMA channel scatter gather operation was started + * successfully + */ + return XST_SUCCESS; +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_SgStop +* +* DESCRIPTION: +* +* This function stops a scatter gather operation for a scatter gather +* DMA channel. This function starts the process of stopping a scatter +* gather operation that is in progress and waits for the stop to be completed. +* Since it waits for the operation to stopped before returning, this function +* could take an amount of time relative to the size of the DMA scatter gather +* operation which is in progress. The scatter gather list of the DMA channel +* is not modified by this function such that starting the scatter gather +* channel after stopping it will cause it to resume. This operation is +* considered to be a graceful stop in that the scatter gather operation +* completes the current buffer descriptor before stopping. +* +* If the interrupt is enabled, an interrupt will be generated when the +* operation is stopped and the caller is responsible for handling the +* interrupt. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. The DMA +* channel should be configured to use scatter gather in order for this function +* to be called. +* +* BufDescriptorPtr is also a return value which contains a pointer to the +* buffer descriptor which the scatter gather operation completed when it +* was stopped. +* +* RETURN VALUE: +* +* A status containing XST_SUCCESS if scatter gather was stopped successfully +* for the DMA channel. +* +* A value of XST_DMA_SG_IS_STOPPED indicates scatter gather was not stoppped +* because the scatter gather is not started, but was already stopped. +* +* BufDescriptorPtr contains a pointer to the buffer descriptor which was +* completed when the operation was stopped. +* +* NOTES: +* +* This function implements a loop which polls the hardware for an infinite +* amount of time. If the hardware is not operating correctly, this function +* may never return. +* +******************************************************************************/ +XStatus +XDmaChannel_SgStop(XDmaChannel * InstancePtr, + XBufDescriptor ** BufDescriptorPtr) +{ + u32 Register; + + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(BufDescriptorPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* get the contents of the software control register, if scatter gather is not + * enabled (started), then return a status because the disable acknowledge + * would not be generated + */ + Register = XIo_In32(InstancePtr->RegBaseAddress + XDC_SWCR_REG_OFFSET); + + if ((Register & XDC_SWCR_SG_ENABLE_MASK) == 0) { + return XST_DMA_SG_IS_STOPPED; + } + + /* Ensure the interrupt status for the scatter gather is cleared such + * that this function will wait til the disable has occurred, writing + * a 1 to only that bit in the register will clear only it + */ + XIo_Out32(InstancePtr->RegBaseAddress + XDC_IS_REG_OFFSET, + XDC_IXR_SG_DISABLE_ACK_MASK); + + /* disable scatter gather by writing to the software control register + * without modifying any other bits of the register + */ + XIo_Out32(InstancePtr->RegBaseAddress + XDC_SWCR_REG_OFFSET, + Register & ~XDC_SWCR_SG_ENABLE_MASK); + + /* scatter gather does not disable immediately, but after the current + * buffer descriptor is complete, so wait for the DMA channel to indicate + * the disable is complete + */ + do { + Register = + XIo_In32(InstancePtr->RegBaseAddress + XDC_IS_REG_OFFSET); + } + while ((Register & XDC_IXR_SG_DISABLE_ACK_MASK) == 0); + + /* Ensure the interrupt status for the scatter gather disable is cleared, + * writing a 1 to only that bit in the register will clear only it + */ + XIo_Out32(InstancePtr->RegBaseAddress + XDC_IS_REG_OFFSET, + XDC_IXR_SG_DISABLE_ACK_MASK); + + /* set the specified buffer descriptor pointer to point to the buffer + * descriptor that the scatter gather DMA channel was processing + */ + *BufDescriptorPtr = + (XBufDescriptor *) XIo_In32(InstancePtr->RegBaseAddress + + XDC_BDA_REG_OFFSET); + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_CreateSgList +* +* DESCRIPTION: +* +* This function creates a scatter gather list in the DMA channel. A scatter +* gather list consists of a list of buffer descriptors that are available to +* be used for scatter gather operations. Buffer descriptors are put into the +* list to request a scatter gather operation to be performed. +* +* A number of buffer descriptors are created from the specified memory and put +* into a buffer descriptor list as empty buffer descriptors. This function must +* be called before non-empty buffer descriptors may be put into the DMA channel +* to request scatter gather operations. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. The DMA +* channel should be configured to use scatter gather in order for this function +* to be called. +* +* MemoryPtr contains a pointer to the memory which is to be used for buffer +* descriptors and must not be cached. +* +* ByteCount contains the number of bytes for the specified memory to be used +* for buffer descriptors. +* +* RETURN VALUE: +* +* A status contains XST_SUCCESS if the scatter gather list was successfully +* created. +* +* A value of XST_DMA_SG_LIST_EXISTS indicates that the scatter gather list +* was not created because the list has already been created. +* +* NOTES: +* +* None. +* +******************************************************************************/ +XStatus +XDmaChannel_CreateSgList(XDmaChannel * InstancePtr, + u32 * MemoryPtr, u32 ByteCount) +{ + XBufDescriptor *BufferDescriptorPtr = (XBufDescriptor *) MemoryPtr; + XBufDescriptor *PreviousDescriptorPtr = NULL; + XBufDescriptor *StartOfListPtr = BufferDescriptorPtr; + u32 UsedByteCount; + + /* assert to verify valid input arguments, alignment for those + * arguments that have alignment restrictions, and at least enough + * memory for one buffer descriptor + */ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(MemoryPtr != NULL); + XASSERT_NONVOID(((u32) MemoryPtr & 3) == 0); + XASSERT_NONVOID(ByteCount != 0); + XASSERT_NONVOID(ByteCount >= sizeof (XBufDescriptor)); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* if the scatter gather list has already been created, then return + * with a status + */ + if (InstancePtr->TotalDescriptorCount != 0) { + return XST_DMA_SG_LIST_EXISTS; + } + + /* loop thru the specified memory block and create as many buffer + * descriptors as possible putting each into the list which is + * implemented as a ring buffer, make sure not to use any memory which + * is not large enough for a complete buffer descriptor + */ + UsedByteCount = 0; + while ((UsedByteCount + sizeof (XBufDescriptor)) <= ByteCount) { + /* setup a pointer to the next buffer descriptor in the memory and + * update # of used bytes to know when all of memory is used + */ + BufferDescriptorPtr = (XBufDescriptor *) ((u32) MemoryPtr + + UsedByteCount); + + /* initialize the new buffer descriptor such that it doesn't contain + * garbage which could be used by the DMA hardware + */ + XBufDescriptor_Initialize(BufferDescriptorPtr); + + /* if this is not the first buffer descriptor to be created, + * then link it to the last created buffer descriptor + */ + if (PreviousDescriptorPtr != NULL) { + XBufDescriptor_SetNextPtr(PreviousDescriptorPtr, + BufferDescriptorPtr); + } + + /* always keep a pointer to the last created buffer descriptor such + * that they can be linked together in the ring buffer + */ + PreviousDescriptorPtr = BufferDescriptorPtr; + + /* keep a count of the number of descriptors in the list to allow + * error processing to be performed + */ + InstancePtr->TotalDescriptorCount++; + + UsedByteCount += sizeof (XBufDescriptor); + } + + /* connect the last buffer descriptor created and inserted in the list + * to the first such that a ring buffer is created + */ + XBufDescriptor_SetNextPtr(BufferDescriptorPtr, StartOfListPtr); + + /* initialize the ring buffer to indicate that there are no + * buffer descriptors in the list which point to valid data buffers + */ + InstancePtr->PutPtr = BufferDescriptorPtr; + InstancePtr->GetPtr = BufferDescriptorPtr; + InstancePtr->CommitPtr = NULL; + InstancePtr->LastPtr = BufferDescriptorPtr; + InstancePtr->ActiveDescriptorCount = 0; + + /* indicate the scatter gather list was successfully created */ + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_IsSgListEmpty +* +* DESCRIPTION: +* +* This function determines if the scatter gather list of a DMA channel is +* empty with regard to buffer descriptors which are pointing to buffers to be +* used for scatter gather operations. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. The DMA +* channel should be configured to use scatter gather in order for this function +* to be called. +* +* RETURN VALUE: +* +* A value of TRUE if the scatter gather list is empty, otherwise a value of +* FALSE. +* +* NOTES: +* +* None. +* +******************************************************************************/ +u32 +XDmaChannel_IsSgListEmpty(XDmaChannel * InstancePtr) +{ + /* assert to verify valid input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* if the number of descriptors which are being used in the list is zero + * then the list is empty + */ + return (InstancePtr->ActiveDescriptorCount == 0); +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_PutDescriptor +* +* DESCRIPTION: +* +* This function puts a buffer descriptor into the DMA channel scatter +* gather list. A DMA channel maintains a list of buffer descriptors which are +* to be processed. This function puts the specified buffer descriptor +* at the next location in the list. Note that since the list is already intact, +* the information in the parameter is copied into the list (rather than modify +* list pointers on the fly). +* +* After buffer descriptors are put into the list, they must also be committed +* by calling another function. This allows multiple buffer descriptors which +* span a single packet to be put into the list while preventing the hardware +* from starting the first buffer descriptor of the packet. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. The DMA +* channel should be configured to use scatter gather in order for this function +* to be called. +* +* BufferDescriptorPtr is a pointer to the buffer descriptor to be put into +* the next available location of the scatter gather list. +* +* RETURN VALUE: +* +* A status which indicates XST_SUCCESS if the buffer descriptor was +* successfully put into the scatter gather list. +* +* A value of XST_DMA_SG_NO_LIST indicates the scatter gather list has not +* been created. +* +* A value of XST_DMA_SG_LIST_FULL indicates the buffer descriptor was not +* put into the list because the list was full. +* +* A value of XST_DMA_SG_BD_LOCKED indicates the buffer descriptor was not +* put into the list because the buffer descriptor in the list which is to +* be overwritten was locked. A locked buffer descriptor indicates the higher +* layered software is still using the buffer descriptor. +* +* NOTES: +* +* It is necessary to create a scatter gather list for a DMA channel before +* putting buffer descriptors into it. +* +******************************************************************************/ +XStatus +XDmaChannel_PutDescriptor(XDmaChannel * InstancePtr, + XBufDescriptor * BufferDescriptorPtr) +{ + u32 Control; + + /* assert to verify valid input arguments and alignment for those + * arguments that have alignment restrictions + */ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(BufferDescriptorPtr != NULL); + XASSERT_NONVOID(((u32) BufferDescriptorPtr & 3) == 0); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* if a scatter gather list has not been created yet, return a status */ + + if (InstancePtr->TotalDescriptorCount == 0) { + return XST_DMA_SG_NO_LIST; + } + + /* if the list is full because all descriptors are pointing to valid + * buffers, then indicate an error, this code assumes no list or an + * empty list is detected above + */ + if (InstancePtr->ActiveDescriptorCount == + InstancePtr->TotalDescriptorCount) { + return XST_DMA_SG_LIST_FULL; + } + + /* if the buffer descriptor in the list which is to be overwritten is + * locked, then don't overwrite it and return a status + */ + if (XBufDescriptor_IsLocked(InstancePtr->PutPtr)) { + return XST_DMA_SG_BD_LOCKED; + } + + /* set the scatter gather stop bit in the control word of the descriptor + * to cause the h/w to stop after it processes this descriptor since it + * will be the last in the list + */ + Control = XBufDescriptor_GetControl(BufferDescriptorPtr); + XBufDescriptor_SetControl(BufferDescriptorPtr, + Control | XDC_DMACR_SG_DISABLE_MASK); + + /* set both statuses in the descriptor so we tell if they are updated with + * the status of the transfer, the hardware should change the busy in the + * DMA status to be false when it completes + */ + XBufDescriptor_SetStatus(BufferDescriptorPtr, XDC_DMASR_BUSY_MASK); + XBufDescriptor_SetDeviceStatus(BufferDescriptorPtr, 0); + + /* copy the descriptor into the next position in the list so it's ready to + * be used by the h/w, this assumes the descriptor in the list prior to this + * one still has the stop bit in the control word set such that the h/w + * use this one yet + */ + CopyBufferDescriptor(BufferDescriptorPtr, InstancePtr->PutPtr); + + /* only the last in the list and the one to be committed have scatter gather + * disabled in the control word, a commit requires only one descriptor + * to be changed, when # of descriptors to commit > 2 all others except the + * 1st and last have scatter gather enabled + */ + if ((InstancePtr->CommitPtr != InstancePtr->LastPtr) && + (InstancePtr->CommitPtr != NULL)) { + Control = XBufDescriptor_GetControl(InstancePtr->LastPtr); + XBufDescriptor_SetControl(InstancePtr->LastPtr, + Control & ~XDC_DMACR_SG_DISABLE_MASK); + } + + /* update the list data based upon putting a descriptor into the list, + * these operations must be last + */ + InstancePtr->ActiveDescriptorCount++; + + /* only update the commit pointer if it is not already active, this allows + * it to be deactivated after every commit such that a single descriptor + * which is committed does not appear to be waiting to be committed + */ + if (InstancePtr->CommitPtr == NULL) { + InstancePtr->CommitPtr = InstancePtr->LastPtr; + } + + /* these updates MUST BE LAST after the commit pointer update in order for + * the commit pointer to track the correct descriptor to be committed + */ + InstancePtr->LastPtr = InstancePtr->PutPtr; + InstancePtr->PutPtr = XBufDescriptor_GetNextPtr(InstancePtr->PutPtr); + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_CommitPuts +* +* DESCRIPTION: +* +* This function commits the buffer descriptors which have been put into the +* scatter list for the DMA channel since the last commit operation was +* performed. This enables the calling functions to put several buffer +* descriptors into the list (e.g.,a packet's worth) before allowing the scatter +* gather operations to start. This prevents the DMA channel hardware from +* starting to use the buffer descriptors in the list before they are ready +* to be used (multiple buffer descriptors for a single packet). +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. The DMA +* channel should be configured to use scatter gather in order for this function +* to be called. +* +* RETURN VALUE: +* +* A status indicating XST_SUCCESS if the buffer descriptors of the list were +* successfully committed. +* +* A value of XST_DMA_SG_NOTHING_TO_COMMIT indicates that the buffer descriptors +* were not committed because there was nothing to commit in the list. All the +* buffer descriptors which are in the list are commited. +* +* NOTES: +* +* None. +* +******************************************************************************/ +XStatus +XDmaChannel_CommitPuts(XDmaChannel * InstancePtr) +{ + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* if the buffer descriptor to be committed is already committed or + * the list is empty (none have been put in), then indicate an error + */ + if ((InstancePtr->CommitPtr == NULL) || + XDmaChannel_IsSgListEmpty(InstancePtr)) { + return XST_DMA_SG_NOTHING_TO_COMMIT; + } + + /* last descriptor in the list must have scatter gather disabled so the end + * of the list is hit by h/w, if descriptor to commit is not last in list, + * commit descriptors by enabling scatter gather in the descriptor + */ + if (InstancePtr->CommitPtr != InstancePtr->LastPtr) { + u32 Control; + + Control = XBufDescriptor_GetControl(InstancePtr->CommitPtr); + XBufDescriptor_SetControl(InstancePtr->CommitPtr, Control & + ~XDC_DMACR_SG_DISABLE_MASK); + } + /* Update the commit pointer to indicate that there is nothing to be + * committed, this state is used by start processing to know that the + * buffer descriptor to start is not waiting to be committed + */ + InstancePtr->CommitPtr = NULL; + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_GetDescriptor +* +* DESCRIPTION: +* +* This function gets a buffer descriptor from the scatter gather list of the +* DMA channel. The buffer descriptor is retrieved from the scatter gather list +* and the scatter gather list is updated to not include the retrieved buffer +* descriptor. This is typically done after a scatter gather operation +* completes indicating that a data buffer has been successfully sent or data +* has been received into the data buffer. The purpose of this function is to +* allow the device using the scatter gather operation to get the results of the +* operation. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. The DMA +* channel should be configured to use scatter gather in order for this function +* to be called. +* +* BufDescriptorPtr is a pointer to a pointer to the buffer descriptor which +* was retrieved from the list. The buffer descriptor is not really removed +* from the list, but it is changed to a state such that the hardware will not +* use it again until it is put into the scatter gather list of the DMA channel. +* +* RETURN VALUE: +* +* A status indicating XST_SUCCESS if a buffer descriptor was retrieved from +* the scatter gather list of the DMA channel. +* +* A value of XST_DMA_SG_NO_LIST indicates the scatter gather list has not +* been created. +* +* A value of XST_DMA_SG_LIST_EMPTY indicates no buffer descriptor was +* retrieved from the list because there are no buffer descriptors to be +* processed in the list. +* +* BufDescriptorPtr is updated to point to the buffer descriptor which was +* retrieved from the list if the status indicates success. +* +* NOTES: +* +* None. +* +******************************************************************************/ +XStatus +XDmaChannel_GetDescriptor(XDmaChannel * InstancePtr, + XBufDescriptor ** BufDescriptorPtr) +{ + u32 Control; + + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(BufDescriptorPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* if a scatter gather list has not been created yet, return a status */ + + if (InstancePtr->TotalDescriptorCount == 0) { + return XST_DMA_SG_NO_LIST; + } + + /* if the buffer descriptor list is empty, then indicate an error */ + + if (XDmaChannel_IsSgListEmpty(InstancePtr)) { + return XST_DMA_SG_LIST_EMPTY; + } + + /* retrieve the next buffer descriptor which is ready to be processed from + * the buffer descriptor list for the DMA channel, set the control word + * such that hardware will stop after the descriptor has been processed + */ + Control = XBufDescriptor_GetControl(InstancePtr->GetPtr); + XBufDescriptor_SetControl(InstancePtr->GetPtr, + Control | XDC_DMACR_SG_DISABLE_MASK); + + /* set the input argument, which is also an output, to point to the + * buffer descriptor which is to be retrieved from the list + */ + *BufDescriptorPtr = InstancePtr->GetPtr; + + /* update the pointer of the DMA channel to reflect the buffer descriptor + * was retrieved from the list by setting it to the next buffer descriptor + * in the list and indicate one less descriptor in the list now + */ + InstancePtr->GetPtr = XBufDescriptor_GetNextPtr(InstancePtr->GetPtr); + InstancePtr->ActiveDescriptorCount--; + + return XST_SUCCESS; +} + +/*********************** Interrupt Collescing Functions **********************/ + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_GetPktCount +* +* DESCRIPTION: +* +* This function returns the value of the unserviced packet count register of +* the DMA channel. This count represents the number of packets that have been +* sent or received by the hardware, but not processed by software. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. The DMA +* channel should be configured to use scatter gather in order for this function +* to be called. +* +* RETURN VALUE: +* +* The unserviced packet counter register contents for the DMA channel. +* +* NOTES: +* +* None. +* +******************************************************************************/ +u32 +XDmaChannel_GetPktCount(XDmaChannel * InstancePtr) +{ + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* get the unserviced packet count from the register and return it */ + + return XIo_In32(InstancePtr->RegBaseAddress + XDC_UPC_REG_OFFSET); +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_DecrementPktCount +* +* DESCRIPTION: +* +* This function decrements the value of the unserviced packet count register. +* This informs the hardware that the software has processed a packet. The +* unserviced packet count register may only be decremented by one in the +* hardware. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. The DMA +* channel should be configured to use scatter gather in order for this function +* to be called. +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* None. +* +******************************************************************************/ +void +XDmaChannel_DecrementPktCount(XDmaChannel * InstancePtr) +{ + u32 Register; + + /* assert to verify input arguments */ + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* if the unserviced packet count register can be decremented (rather + * than rolling over) decrement it by writing a 1 to the register, + * this is the only valid write to the register as it serves as an + * acknowledge that a packet was handled by the software + */ + Register = XIo_In32(InstancePtr->RegBaseAddress + XDC_UPC_REG_OFFSET); + if (Register > 0) { + XIo_Out32(InstancePtr->RegBaseAddress + XDC_UPC_REG_OFFSET, + 1UL); + } +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_SetPktThreshold +* +* DESCRIPTION: +* +* This function sets the value of the packet count threshold register of the +* DMA channel. It reflects the number of packets that must be sent or +* received before generating an interrupt. This value helps implement +* a concept called "interrupt coalescing", which is used to reduce the number +* of interrupts from devices with high data rates. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. The DMA +* channel should be configured to use scatter gather in order for this function +* to be called. +* +* Threshold is the value that is written to the threshold register of the +* DMA channel. +* +* RETURN VALUE: +* +* A status containing XST_SUCCESS if the packet count threshold was +* successfully set. +* +* NOTES: +* +* The packet threshold could be set to larger than the number of descriptors +* allocated to the DMA channel. In this case, the wait bound will take over +* and always indicate data arrival. There was a check in this function that +* returned an error if the treshold was larger than the number of descriptors, +* but that was removed because users would then have to set the threshold +* only after they set descriptor space, which is an order dependency that +* caused confustion. +* +******************************************************************************/ +XStatus +XDmaChannel_SetPktThreshold(XDmaChannel * InstancePtr, u8 Threshold) +{ + /* assert to verify input arguments, don't assert the threshold since + * it's range is unknown + */ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* set the packet count threshold in the register such that an interrupt + * may be generated, if enabled, when the packet count threshold is + * reached or exceeded + */ + XIo_Out32(InstancePtr->RegBaseAddress + XDC_PCT_REG_OFFSET, + (u32) Threshold); + + /* indicate the packet count threshold was successfully set */ + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_GetPktThreshold +* +* DESCRIPTION: +* +* This function gets the value of the packet count threshold register of the +* DMA channel. This value reflects the number of packets that must be sent or +* received before generating an interrupt. This value helps implement a concept +* called "interrupt coalescing", which is used to reduce the number of +* interrupts from devices with high data rates. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. The DMA +* channel should be configured to use scatter gather in order for this function +* to be called. +* +* RETURN VALUE: +* +* The packet threshold register contents for the DMA channel and is a value in +* the range 0 - 1023. A value of 0 indicates the packet wait bound timer is +* disabled. +* +* NOTES: +* +* None. +* +******************************************************************************/ +u8 +XDmaChannel_GetPktThreshold(XDmaChannel * InstancePtr) +{ + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* get the packet count threshold from the register and return it, + * since only 8 bits are used, cast it to return only those bits */ + + return (u8) XIo_In32(InstancePtr->RegBaseAddress + XDC_PCT_REG_OFFSET); +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_SetPktWaitBound +* +* DESCRIPTION: +* +* This function sets the value of the packet wait bound register of the +* DMA channel. This value reflects the timer value used to trigger an +* interrupt when not enough packets have been received to reach the packet +* count threshold. +* +* The timer is in millisecond units with +/- 33% accuracy. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. The DMA +* channel should be configured to use scatter gather in order for this function +* to be called. +* +* WaitBound is the value, in milliseconds, to be stored in the wait bound +* register of the DMA channel and is a value in the range 0 - 1023. A value +* of 0 disables the packet wait bound timer. +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* None. +* +******************************************************************************/ +void +XDmaChannel_SetPktWaitBound(XDmaChannel * InstancePtr, u32 WaitBound) +{ + /* assert to verify input arguments */ + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(WaitBound < 1024); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* set the packet wait bound in the register such that interrupt may be + * generated, if enabled, when packets have not been handled for a specific + * amount of time + */ + XIo_Out32(InstancePtr->RegBaseAddress + XDC_PWB_REG_OFFSET, WaitBound); +} + +/****************************************************************************** +* +* FUNCTION: +* +* XDmaChannel_GetPktWaitBound +* +* DESCRIPTION: +* +* This function gets the value of the packet wait bound register of the +* DMA channel. This value contains the timer value used to trigger an +* interrupt when not enough packets have been received to reach the packet +* count threshold. +* +* The timer is in millisecond units with +/- 33% accuracy. +* +* ARGUMENTS: +* +* InstancePtr contains a pointer to the DMA channel to operate on. The DMA +* channel should be configured to use scatter gather in order for this function +* to be called. +* +* RETURN VALUE: +* +* The packet wait bound register contents for the DMA channel. +* +* NOTES: +* +* None. +* +******************************************************************************/ +u32 +XDmaChannel_GetPktWaitBound(XDmaChannel * InstancePtr) +{ + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* get the packet wait bound from the register and return it */ + + return XIo_In32(InstancePtr->RegBaseAddress + XDC_PWB_REG_OFFSET); +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xilinx_syms.c linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xilinx_syms.c --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xilinx_syms.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xilinx_syms.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,66 @@ +/* + * xilinx_syms.c + * + * This file EXPORT_SYMBOL's all of the Xilinx entry points. + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#include + +#include "xbasic_types.h" +EXPORT_SYMBOL(XAssert); +EXPORT_SYMBOL(XAssertSetCallback); +EXPORT_SYMBOL(XAssertStatus); +extern u32 XWaitInAssert; +EXPORT_SYMBOL(XWaitInAssert); + +#include "xdma_channel.h" +EXPORT_SYMBOL(XDmaChannel_CommitPuts); +EXPORT_SYMBOL(XDmaChannel_CreateSgList); +EXPORT_SYMBOL(XDmaChannel_DecrementPktCount); +EXPORT_SYMBOL(XDmaChannel_GetControl); +EXPORT_SYMBOL(XDmaChannel_GetDescriptor); +EXPORT_SYMBOL(XDmaChannel_GetIntrEnable); +EXPORT_SYMBOL(XDmaChannel_GetIntrStatus); +EXPORT_SYMBOL(XDmaChannel_GetPktCount); +EXPORT_SYMBOL(XDmaChannel_GetPktThreshold); +EXPORT_SYMBOL(XDmaChannel_GetPktWaitBound); +EXPORT_SYMBOL(XDmaChannel_GetStatus); +EXPORT_SYMBOL(XDmaChannel_GetVersion); +EXPORT_SYMBOL(XDmaChannel_Initialize); +EXPORT_SYMBOL(XDmaChannel_IsReady); +EXPORT_SYMBOL(XDmaChannel_IsSgListEmpty); +EXPORT_SYMBOL(XDmaChannel_PutDescriptor); +EXPORT_SYMBOL(XDmaChannel_Reset); +EXPORT_SYMBOL(XDmaChannel_SelfTest); +EXPORT_SYMBOL(XDmaChannel_SetControl); +EXPORT_SYMBOL(XDmaChannel_SetIntrEnable); +EXPORT_SYMBOL(XDmaChannel_SetIntrStatus); +EXPORT_SYMBOL(XDmaChannel_SetPktThreshold); +EXPORT_SYMBOL(XDmaChannel_SetPktWaitBound); +EXPORT_SYMBOL(XDmaChannel_SgStart); +EXPORT_SYMBOL(XDmaChannel_SgStop); +EXPORT_SYMBOL(XDmaChannel_Transfer); + +#include "xipif_v1_23_b.h" +EXPORT_SYMBOL(XIpIfV123b_SelfTest); + +#include "xpacket_fifo_v1_00_b.h" +EXPORT_SYMBOL(XPacketFifoV100b_Initialize); +EXPORT_SYMBOL(XPacketFifoV100b_Read); +EXPORT_SYMBOL(XPacketFifoV100b_SelfTest); +EXPORT_SYMBOL(XPacketFifoV100b_Write); + +#include "xversion.h" +EXPORT_SYMBOL(XVersion_Copy); +EXPORT_SYMBOL(XVersion_FromString); +EXPORT_SYMBOL(XVersion_IsEqual); +EXPORT_SYMBOL(XVersion_Pack); +EXPORT_SYMBOL(XVersion_ToString); +EXPORT_SYMBOL(XVersion_UnPack); diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xio.h linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xio.h --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xio.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xio.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,63 @@ +/* + * xio.h + * + * Defines XIo functions for Xilinx OCP in terms of Linux primitives + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#ifndef XIO_H +#define XIO_H + +#include "xbasic_types.h" +#include + +typedef u32 XIo_Address; + +extern inline u8 +XIo_In8(XIo_Address InAddress) +{ + return (u8) in_8((volatile unsigned char *) InAddress); +} +extern inline u16 +XIo_In16(XIo_Address InAddress) +{ + return (u16) in_be16((volatile unsigned short *) InAddress); +} +extern inline u32 +XIo_In32(XIo_Address InAddress) +{ + return (u32) in_be32((volatile unsigned *) InAddress); +} +extern inline void +XIo_Out8(XIo_Address OutAddress, u8 Value) +{ + out_8((volatile unsigned char *) OutAddress, Value); +} +extern inline void +XIo_Out16(XIo_Address OutAddress, u16 Value) +{ + out_be16((volatile unsigned short *) OutAddress, Value); +} +extern inline void +XIo_Out32(XIo_Address OutAddress, u32 Value) +{ + out_be32((volatile unsigned *) OutAddress, Value); +} + +#define XIo_ToLittleEndian16(s,d) (*(u16*)(d) = cpu_to_le16((u16)(s))) +#define XIo_ToLittleEndian32(s,d) (*(u32*)(d) = cpu_to_le32((u32)(s))) +#define XIo_ToBigEndian16(s,d) (*(u16*)(d) = cpu_to_be16((u16)(s))) +#define XIo_ToBigEndian32(s,d) (*(u32*)(d) = cpu_to_be32((u32)(s))) + +#define XIo_FromLittleEndian16(s,d) (*(u16*)(d) = le16_to_cpu((u16)(s))) +#define XIo_FromLittleEndian32(s,d) (*(u32*)(d) = le32_to_cpu((u32)(s))) +#define XIo_FromBigEndian16(s,d) (*(u16*)(d) = be16_to_cpu((u16)(s))) +#define XIo_FromBigEndian32(s,d) (*(u32*)(d) = be32_to_cpu((u32)(s))) + +#endif /* XIO_H */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xipif_v1_23_b.c linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xipif_v1_23_b.c --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xipif_v1_23_b.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xipif_v1_23_b.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,349 @@ +/* $Id: xipif_v1_23_b.c,v 1.1 2002/03/18 23:24:52 linnj Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/****************************************************************************** +* +* FILENAME: +* +* xipif.c +* +* DESCRIPTION: +* +* This file contains the implementation of the XIpIf component. The +* XIpIf component encapsulates the IPIF, which is the standard interface +* that IP must adhere to when connecting to a bus. The purpose of this +* component is to encapsulate the IPIF processing such that maintainability +* is increased. This component does not provide a lot of abstraction from +* from the details of the IPIF as it is considered a building block for +* device drivers. A device driver designer must be familiar with the +* details of the IPIF hardware to use this component. +* +* The IPIF hardware provides a building block for all hardware devices such +* that each device does not need to reimplement these building blocks. The +* IPIF contains other building blocks, such as FIFOs and DMA channels, which +* are also common to many devices. These blocks are implemented as separate +* hardware blocks and instantiated within the IPIF. The primary hardware of +* the IPIF which is implemented by this software component is the interrupt +* architecture. Since there are many blocks of a device which may generate +* interrupts, all the interrupt processing is contained in the common part +* of the device, the IPIF. This interrupt processing is for the device level +* only and does not include any processing for the interrupt controller. +* +* A device is a mechanism such as an Ethernet MAC. The device is made +* up of several parts which include an IPIF and the IP. The IPIF contains most +* of the device infrastructure which is common to all devices, such as +* interrupt processing, DMA channels, and FIFOs. The infrastructure may also +* be referred to as IPIF internal blocks since they are part of the IPIF and +* are separate blocks that can be selected based upon the needs of the device. +* The IP of the device is the logic that is unique to the device and interfaces +* to the IPIF of the device. +* +* In general, there are two levels of registers within the IPIF. The first +* level, referred to as the device level, contains registers which are for the +* entire device. The second level, referred to as the IP level, contains +* registers which are specific to the IP of the device. The two levels of +* registers are designed to be hierarchical such that the device level is +* is a more general register set above the more specific registers of the IP. +* The IP level of registers provides functionality which is typically common +* across all devices and allows IP designers to focus on the unique aspects +* of the IP. +* +* The interrupt registers of the IPIF are parameterizable such that the only +* the number of bits necessary for the device are implemented. The functions +* of this component do not attempt to validate that the passed in arguments are +* valid based upon the number of implemented bits. This is necessary to +* maintain the level of performance required for the common components. Bits +* of the registers are assigned starting at the least significant bit of the +* registers. +* +* Critical Sections +* +* It is the responsibility of the device driver designer to use critical +* sections as necessary when calling functions of the IPIF. This component +* does not use critical sections and it does access registers using +* read-modify-write operations. Calls to IPIF functions from a main thread +* and from an interrupt context could produce unpredictable behavior such that +* the caller must provide the appropriate critical sections. +* +* Mutual Exclusion +* +* The functions of the IPIF are not thread safe such that the caller of all +* functions is responsible for ensuring mutual exclusion for an IPIF. Mutual +* exclusion across multiple IPIF components is not necessary. +* +* NOTES: +* +* None. +* +* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- ---- -------- ----------------------------------------------- +* 1.23b jhl 02/27/01 Repartioned to reduce size +* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xipif_v1_23_b.h" +#include "xio.h" + +/************************** Constant Definitions *****************************/ + +/* the following constant is used to generate bit masks for register testing + * in the self test functions, it defines the starting bit mask that is to be + * shifted from the LSB to MSB in creating a register test mask + */ +#define XIIF_V123B_FIRST_BIT_MASK 1UL + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + +static XStatus IpIntrSelfTest(u32 RegBaseAddress, u32 IpRegistersWidth); + +/****************************************************************************** +* +* FUNCTION: +* +* XIpIf_SelfTest +* +* DESCRIPTION: +* +* This function performs a self test on the specified IPIF component. Many +* of the registers in the IPIF are tested to ensure proper operation. This +* function is destructive because the IPIF is reset at the start of the test +* and at the end of the test to ensure predictable results. The IPIF reset +* also resets the entire device that uses the IPIF. This function exits with +* all interrupts for the device disabled. +* +* ARGUMENTS: +* +* InstancePtr points to the XIpIf to operate on. +* +* DeviceRegistersWidth contains the number of bits in the device interrupt +* registers. The hardware is parameterizable such that only the number of bits +* necessary to support a device are implemented. This value must be between 0 +* and 32 with 0 indicating there are no device interrupt registers used. +* +* IpRegistersWidth contains the number of bits in the IP interrupt registers +* of the device. The hardware is parameterizable such that only the number of +* bits necessary to support a device are implemented. This value must be +* between 0 and 32 with 0 indicating there are no IP interrupt registers used. +* +* RETURN VALUE: +* +* A value of XST_SUCCESS indicates the test was successful with no errors. +* Any one of the following error values may also be returned. +* +* XST_IPIF_RESET_REGISTER_ERROR The value of a register at reset was +* not valid +* XST_IPIF_IP_STATUS_ERROR A write to the IP interrupt status +* register did not read back correctly +* XST_IPIF_IP_ACK_ERROR One or more bits in the IP interrupt +* status register did not reset when acked +* XST_IPIF_IP_ENABLE_ERROR The IP interrupt enable register +* did not read back correctly based upon +* what was written to it +* +* NOTES: +* +* None. +* +******************************************************************************/ + +/* the following constant defines the maximum number of bits which may be + * used in the registers at the device and IP levels, this is based upon the + * number of bits available in the registers + */ +#define XIIF_V123B_MAX_REG_BIT_COUNT 32 + +XStatus +XIpIfV123b_SelfTest(u32 RegBaseAddress, u8 IpRegistersWidth) +{ + XStatus Status; + + /* assert to verify arguments are valid */ + + XASSERT_NONVOID(IpRegistersWidth <= XIIF_V123B_MAX_REG_BIT_COUNT); + + /* reset the IPIF such that it's in a known state before the test + * and interrupts are globally disabled + */ + XIIF_V123B_RESET(RegBaseAddress); + + /* perform the self test on the IP interrupt registers, if + * it is not successful exit with the status + */ + Status = IpIntrSelfTest(RegBaseAddress, IpRegistersWidth); + if (Status != XST_SUCCESS) { + return Status; + } + + /* reset the IPIF such that it's in a known state before exiting test */ + + XIIF_V123B_RESET(RegBaseAddress); + + /* reaching this point means there were no errors, return success */ + + return XST_SUCCESS; +} + +/****************************************************************************** +* +* FUNCTION: +* +* IpIntrSelfTest +* +* DESCRIPTION: +* +* Perform a self test on the IP interrupt registers of the IPIF. This +* function modifies registers of the IPIF such that they are not guaranteed +* to be in the same state when it returns. Any bits in the IP interrupt +* status register which are set are assumed to be set by default after a reset +* and are not tested in the test. +* +* ARGUMENTS: +* +* InstancePtr points to the XIpIf to operate on. +* +* IpRegistersWidth contains the number of bits in the IP interrupt registers +* of the device. The hardware is parameterizable such that only the number of +* bits necessary to support a device are implemented. This value must be +* between 0 and 32 with 0 indicating there are no IP interrupt registers used. +* +* RETURN VALUE: +* +* A status indicating XST_SUCCESS if the test was successful. Otherwise, one +* of the following values is returned. +* +* XST_IPIF_RESET_REGISTER_ERROR The value of a register at reset was +* not valid +* XST_IPIF_IP_STATUS_ERROR A write to the IP interrupt status +* register did not read back correctly +* XST_IPIF_IP_ACK_ERROR One or more bits in the IP status +* register did not reset when acked +* XST_IPIF_IP_ENABLE_ERROR The IP interrupt enable register +* did not read back correctly based upon +* what was written to it +* NOTES: +* +* None. +* +******************************************************************************/ +static XStatus +IpIntrSelfTest(u32 RegBaseAddress, u32 IpRegistersWidth) +{ + /* ensure that the IP interrupt interrupt enable register is zero + * as it should be at reset, the interrupt status is dependent upon the + * IP such that it's reset value is not known + */ + if (XIIF_V123B_READ_IIER(RegBaseAddress) != 0) { + return XST_IPIF_RESET_REGISTER_ERROR; + } + + /* if there are any used IP interrupts, then test all of the interrupt + * bits in all testable registers + */ + if (IpRegistersWidth > 0) { + u32 BitCount; + u32 IpInterruptMask = XIIF_V123B_FIRST_BIT_MASK; + u32 Mask = XIIF_V123B_FIRST_BIT_MASK; /* bits assigned MSB to LSB */ + u32 InterruptStatus; + + /* generate the register masks to be used for IP register tests, the + * number of bits supported by the hardware is parameterizable such + * that only that number of bits are implemented in the registers, the + * bits are allocated starting at the MSB of the registers + */ + for (BitCount = 1; BitCount < IpRegistersWidth; BitCount++) { + Mask = Mask << 1; + IpInterruptMask |= Mask; + } + + /* get the current IP interrupt status register contents, any bits + * already set must default to 1 at reset in the device and these + * bits can't be tested in the following test, remove these bits from + * the mask that was generated for the test + */ + InterruptStatus = XIIF_V123B_READ_IISR(RegBaseAddress); + IpInterruptMask &= ~InterruptStatus; + + /* set the bits in the device status register and verify them by reading + * the register again, all bits of the register are latched + */ + XIIF_V123B_WRITE_IISR(RegBaseAddress, IpInterruptMask); + InterruptStatus = XIIF_V123B_READ_IISR(RegBaseAddress); + if ((InterruptStatus & IpInterruptMask) != IpInterruptMask) + { + return XST_IPIF_IP_STATUS_ERROR; + } + + /* test to ensure that the bits set in the IP interrupt status register + * can be cleared by acknowledging them in the IP interrupt status + * register then read it again and verify it was cleared + */ + XIIF_V123B_WRITE_IISR(RegBaseAddress, IpInterruptMask); + InterruptStatus = XIIF_V123B_READ_IISR(RegBaseAddress); + if ((InterruptStatus & IpInterruptMask) != 0) { + return XST_IPIF_IP_ACK_ERROR; + } + + /* set the IP interrupt enable set register and then read the IP + * interrupt enable register and verify the interrupts were enabled + */ + XIIF_V123B_WRITE_IIER(RegBaseAddress, IpInterruptMask); + if (XIIF_V123B_READ_IIER(RegBaseAddress) != IpInterruptMask) { + return XST_IPIF_IP_ENABLE_ERROR; + } + + /* clear the IP interrupt enable register and then read the + * IP interrupt enable register and verify the interrupts were disabled + */ + XIIF_V123B_WRITE_IIER(RegBaseAddress, 0); + if (XIIF_V123B_READ_IIER(RegBaseAddress) != 0) { + return XST_IPIF_IP_ENABLE_ERROR; + } + } + return XST_SUCCESS; +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xipif_v1_23_b.h linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xipif_v1_23_b.h --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xipif_v1_23_b.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xipif_v1_23_b.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,764 @@ +/* $Id: xipif_v1_23_b.h,v 1.1 2002/03/18 23:24:52 linnj Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/****************************************************************************** +* +* FILENAME: +* +* xipif.h +* +* DESCRIPTION: +* +* The XIpIf component encapsulates the IPIF, which is the standard interface +* that IP must adhere to when connecting to a bus. The purpose of this +* component is to encapsulate the IPIF processing such that maintainability +* is increased. This component does not provide a lot of abstraction from +* from the details of the IPIF as it is considered a building block for +* device drivers. A device driver designer must be familiar with the +* details of the IPIF hardware to use this component. +* +* The IPIF hardware provides a building block for all hardware devices such +* that each device does not need to reimplement these building blocks. The +* IPIF contains other building blocks, such as FIFOs and DMA channels, which +* are also common to many devices. These blocks are implemented as separate +* hardware blocks and instantiated within the IPIF. The primary hardware of +* the IPIF which is implemented by this software component is the interrupt +* architecture. Since there are many blocks of a device which may generate +* interrupts, all the interrupt processing is contained in the common part +* of the device, the IPIF. This interrupt processing is for the device level +* only and does not include any processing for the interrupt controller. +* +* A device is a mechanism such as an Ethernet MAC. The device is made +* up of several parts which include an IPIF and the IP. The IPIF contains most +* of the device infrastructure which is common to all devices, such as +* interrupt processing, DMA channels, and FIFOs. The infrastructure may also +* be referred to as IPIF internal blocks since they are part of the IPIF and +* are separate blocks that can be selected based upon the needs of the device. +* The IP of the device is the logic that is unique to the device and interfaces +* to the IPIF of the device. +* +* In general, there are two levels of registers within the IPIF. The first +* level, referred to as the device level, contains registers which are for the +* entire device. The second level, referred to as the IP level, contains +* registers which are specific to the IP of the device. The two levels of +* registers are designed to be hierarchical such that the device level is +* is a more general register set above the more specific registers of the IP. +* The IP level of registers provides functionality which is typically common +* across all devices and allows IP designers to focus on the unique aspects +* of the IP. +* +* Critical Sections +* +* It is the responsibility of the device driver designer to use critical +* sections as necessary when calling functions of the IPIF. This component +* does not use critical sections and it does access registers using +* read-modify-write operations. Calls to IPIF functions from a main thread +* and from an interrupt context could produce unpredictable behavior such that +* the caller must provide the appropriate critical sections. +* +* Mutual Exclusion +* +* The functions of the IPIF are not thread safe such that the caller of all +* functions is responsible for ensuring mutual exclusion for an IPIF. Mutual +* exclusion across multiple IPIF components is not necessary. +* +* NOTES: +* +* None. +* +* MODIFICATION HISTORY: +* +* Ver Who Date Changes +* ----- ---- -------- ----------------------------------------------- +* 1.23b jhl 02/27/01 Repartioned to minimize size +* +******************************************************************************/ + +#ifndef XIPIF_H /* prevent circular inclusions */ +#define XIPIF_H /* by using protection macros */ + +/***************************** Include Files *********************************/ +#include "xbasic_types.h" +#include "xstatus.h" +#include "xversion.h" + +/************************** Constant Definitions *****************************/ + +/* the following constants define the register offsets for the registers of the + * IPIF, there are some holes in the memory map for reserved addresses to allow + * other registers to be added and still match the memory map of the interrupt + * controller registers + */ +#define XIIF_V123B_DISR_OFFSET 0UL /* device interrupt status register */ +#define XIIF_V123B_DIPR_OFFSET 4UL /* device interrupt pending register */ +#define XIIF_V123B_DIER_OFFSET 8UL /* device interrupt enable register */ +#define XIIF_V123B_DIIR_OFFSET 24UL /* device interrupt ID register */ +#define XIIF_V123B_DGIER_OFFSET 28UL /* device global interrupt enable reg */ +#define XIIF_V123B_IISR_OFFSET 32UL /* IP interrupt status register */ +#define XIIF_V123B_IIER_OFFSET 40UL /* IP interrupt enable register */ +#define XIIF_V123B_RESETR_OFFSET 64UL /* reset register */ + +#define XIIF_V123B_RESET_MASK 0xAUL + +/* the following constant is used for the device global interrupt enable + * register, to enable all interrupts for the device, this is the only bit + * in the register + */ +#define XIIF_V123B_GINTR_ENABLE_MASK 0x80000000UL + +/* the following constants contain the masks to identify each internal IPIF + * condition in the device registers of the IPIF, interrupts are assigned + * in the register from LSB to the MSB + */ +#define XIIF_V123B_ERROR_MASK 1UL /* LSB of the register */ + +/* The following constants contain interrupt IDs which identify each internal + * IPIF condition, this value must correlate with the mask constant for the + * error + */ +#define XIIF_V123B_ERROR_INTERRUPT_ID 0 /* interrupt bit #, (LSB = 0) */ +#define XIIF_V123B_NO_INTERRUPT_ID 128 /* no interrupts are pending */ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_RESET +* +* DESCRIPTION: +* +* Reset the IPIF component and hardware. This is a destructive operation that +* could cause the loss of data since resetting the IPIF of a device also +* resets the device using the IPIF and any blocks, such as FIFOs or DMA +* channels, within the IPIF. All registers of the IPIF will contain their +* reset value when this function returns. +* +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* None. +* +******************************************************************************/ + +/* the following constant is used in the reset register to cause the IPIF to + * reset + */ +#define XIIF_V123B_RESET(RegBaseAddress) \ + XIo_Out32(RegBaseAddress + XIIF_V123B_RESETR_OFFSET, XIIF_V123B_RESET_MASK) + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_WRITE_DISR +* +* DESCRIPTION: +* +* This function sets the device interrupt status register to the value. +* This register indicates the status of interrupt sources for a device +* which contains the IPIF. The status is independent of whether interrupts +* are enabled and could be used for polling a device at a higher level rather +* than a more detailed level. +* +* Each bit of the register correlates to a specific interrupt source within the +* device which contains the IPIF. With the exception of some internal IPIF +* conditions, the contents of this register are not latched but indicate +* the live status of the interrupt sources within the device. Writing any of +* the non-latched bits of the register will have no effect on the register. +* +* For the latched bits of this register only, setting a bit which is zero +* within this register causes an interrupt to generated. The device global +* interrupt enable register and the device interrupt enable register must be set +* appropriately to allow an interrupt to be passed out of the device. The +* interrupt is cleared by writing to this register with the bits to be +* cleared set to a one and all others to zero. This register implements a +* toggle on write functionality meaning any bits which are set in the value +* written cause the bits in the register to change to the opposite state. +* +* This function writes the specified value to the register such that +* some bits may be set and others cleared. It is the caller's responsibility +* to get the value of the register prior to setting the value to prevent a +* destructive behavior. +* +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* Status contains the value to be written to the interrupt status register of +* the device. The only bits which can be written are the latched bits which +* contain the internal IPIF conditions. The following values may be used to +* set the status register or clear an interrupt condition. +* +* XIIF_V123B_ERROR_MASK Indicates a device error in the IPIF +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* None. +* +******************************************************************************/ +#define XIIF_V123B_WRITE_DISR(RegBaseAddress, Status) \ + XIo_Out32((RegBaseAddress) + XIIF_V123B_DISR_OFFSET, (Status)) + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_READ_DISR +* +* DESCRIPTION: +* +* This function gets the device interrupt status register contents. +* This register indicates the status of interrupt sources for a device +* which contains the IPIF. The status is independent of whether interrupts +* are enabled and could be used for polling a device at a higher level. +* +* Each bit of the register correlates to a specific interrupt source within the +* device which contains the IPIF. With the exception of some internal IPIF +* conditions, the contents of this register are not latched but indicate +* the live status of the interrupt sources within the device. +* +* For only the latched bits of this register, the interrupt may be cleared by +* writing to these bits in the status register. +* +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* RETURN VALUE: +* +* A status which contains the value read from the interrupt status register of +* the device. The bit definitions are specific to the device with +* the exception of the latched internal IPIF condition bits. The following +* values may be used to detect internal IPIF conditions in the status. +* +* XIIF_V123B_ERROR_MASK Indicates a device error in the IPIF +* +* NOTES: +* +* None. +* +******************************************************************************/ +#define XIIF_V123B_READ_DISR(RegBaseAddress) \ + XIo_In32((RegBaseAddress) + XIIF_V123B_DISR_OFFSET) + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_WRITE_DIER +* +* DESCRIPTION: +* +* This function sets the device interrupt enable register contents. +* This register controls which interrupt sources of the device are allowed to +* generate an interrupt. The device global interrupt enable register must also +* be set appropriately for an interrupt to be passed out of the device. +* +* Each bit of the register correlates to a specific interrupt source within the +* device which contains the IPIF. Setting a bit in this register enables that +* interrupt source to generate an interrupt. Clearing a bit in this register +* disables interrupt generation for that interrupt source. +* +* This function writes only the specified value to the register such that +* some interrupts source may be enabled and others disabled. It is the +* caller's responsibility to get the value of the interrupt enable register +* prior to setting the value to prevent an destructive behavior. +* +* An interrupt source may not be enabled to generate an interrupt, but can +* still be polled in the interrupt status register. +* +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* Enable contains the value to be written to the interrupt enable register +* of the device. The bit definitions are specific to the device with +* the exception of the internal IPIF conditions. The following +* values may be used to enable the internal IPIF conditions interrupts. +* +* XIIF_V123B_ERROR_MASK Indicates a device error in the IPIF +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* Signature: u32 XIIF_V123B_WRITE_DIER(u32 RegBaseAddress, +* u32 Enable) +* +******************************************************************************/ +#define XIIF_V123B_WRITE_DIER(RegBaseAddress, Enable) \ + XIo_Out32((RegBaseAddress) + XIIF_V123B_DIER_OFFSET, (Enable)) + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_READ_DIER +* +* DESCRIPTION: +* +* This function gets the device interrupt enable register contents. +* This register controls which interrupt sources of the device +* are allowed to generate an interrupt. The device global interrupt enable +* register and the device interrupt enable register must also be set +* appropriately for an interrupt to be passed out of the device. +* +* Each bit of the register correlates to a specific interrupt source within the +* device which contains the IPIF. Setting a bit in this register enables that +* interrupt source to generate an interrupt if the global enable is set +* appropriately. Clearing a bit in this register disables interrupt generation +* for that interrupt source regardless of the global interrupt enable. +* +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* RETURN VALUE: +* +* The value read from the interrupt enable register of the device. The bit +* definitions are specific to the device with the exception of the internal +* IPIF conditions. The following values may be used to determine from the +* value if the internal IPIF conditions interrupts are enabled. +* +* XIIF_V123B_ERROR_MASK Indicates a device error in the IPIF +* +* NOTES: +* +* None. +* +******************************************************************************/ +#define XIIF_V123B_READ_DIER(RegBaseAddress) \ + XIo_In32((RegBaseAddress) + XIIF_V123B_DIER_OFFSET) + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_READ_DIPR +* +* DESCRIPTION: +* +* This function gets the device interrupt pending register contents. +* This register indicates the pending interrupt sources, those that are waiting +* to be serviced by the software, for a device which contains the IPIF. +* An interrupt must be enabled in the interrupt enable register of the IPIF to +* be pending. +* +* Each bit of the register correlates to a specific interrupt source within the +* the device which contains the IPIF. With the exception of some internal IPIF +* conditions, the contents of this register are not latched since the condition +* is latched in the IP interrupt status register, by an internal block of the +* IPIF such as a FIFO or DMA channel, or by the IP of the device. This register +* is read only and is not latched, but it is necessary to acknowledge (clear) +* the interrupt condition by performing the appropriate processing for the IP +* or block within the IPIF. +* +* This register can be thought of as the contents of the interrupt status +* register ANDed with the contents of the interrupt enable register. +* +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* RETURN VALUE: +* +* The value read from the interrupt pending register of the device. The bit +* definitions are specific to the device with the exception of the latched +* internal IPIF condition bits. The following values may be used to detect +* internal IPIF conditions in the value. +* +* XIIF_V123B_ERROR_MASK Indicates a device error in the IPIF +* +* NOTES: +* +* None. +* +******************************************************************************/ +#define XIIF_V123B_READ_DIPR(RegBaseAddress) \ + XIo_In32((RegBaseAddress) + XIIF_V123B_DIPR_OFFSET) + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_READ_DIIR +* +* DESCRIPTION: +* +* This function gets the device interrupt ID for the highest priority interrupt +* which is pending from the interrupt ID register. This function provides +* priority resolution such that faster interrupt processing is possible. +* Without priority resolution, it is necessary for the software to read the +* interrupt pending register and then check each interrupt source to determine +* if an interrupt is pending. Priority resolution becomes more important as the +* number of interrupt sources becomes larger. +* +* Interrupt priorities are based upon the bit position of the interrupt in the +* interrupt pending register with bit 0 being the highest priority. The +* interrupt ID is the priority of the interrupt, 0 - 31, with 0 being the +* highest priority. The interrupt ID register is live rather than latched such +* that multiple calls to this function may not yield the same results. A +* special value, outside of the interrupt priority range of 0 - 31, is +* contained in the register which indicates that no interrupt is pending. This +* may be useful for allowing software to continue processing interrupts in a +* loop until there are no longer any interrupts pending. +* +* The interrupt ID is designed to allow a function pointer table to be used +* in the software such that the interrupt ID is used as an index into that +* table. The function pointer table could contain an instance pointer, such +* as to DMA channel, and a function pointer to the function which handles +* that interrupt. This design requires the interrupt processing of the device +* driver to be partitioned into smaller more granular pieces based upon +* hardware used by the device, such as DMA channels and FIFOs. +* +* It is not mandatory that this function be used by the device driver software. +* It may choose to read the pending register and resolve the pending interrupt +* priorities on it's own. +* +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* RETURN VALUE: +* +* An interrupt ID, 0 - 31, which identifies the highest priority interrupt +* which is pending. A value of XIIF_NO_INTERRUPT_ID indicates that there is +* no interrupt pending. The following values may be used to identify the +* interrupt ID for the internal IPIF interrupts. +* +* XIIF_V123B_ERROR_INTERRUPT_ID Indicates a device error in the IPIF +* +* NOTES: +* +* None. +* +******************************************************************************/ +#define XIIF_V123B_READ_DIIR(RegBaseAddress) \ + XIo_In32((RegBaseAddress) + XIIF_V123B_DIIR_OFFSET) + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_GLOBAL_INTR_DISABLE +* +* DESCRIPTION: +* +* This function disables all interrupts for the device by writing to the global +* interrupt enable register. This register provides the ability to disable +* interrupts without any modifications to the interrupt enable register such +* that it is minimal effort to restore the interrupts to the previous enabled +* state. The corresponding function, XIpIf_GlobalIntrEnable, is provided to +* restore the interrupts to the previous enabled state. This function is +* designed to be used in critical sections of device drivers such that it is +* not necessary to disable other device interrupts. +* +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* None. +* +******************************************************************************/ +#define XIIF_V123B_GINTR_DISABLE(RegBaseAddress) \ + XIo_Out32((RegBaseAddress) + XIIF_V123B_DGIER_OFFSET, 0) + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_GINTR_ENABLE +* +* DESCRIPTION: +* +* This function writes to the global interrupt enable register to enable +* interrupts from the device. This register provides the ability to enable +* interrupts without any modifications to the interrupt enable register such +* that it is minimal effort to restore the interrupts to the previous enabled +* state. This function does not enable individual interrupts as the interrupt +* enable register must be set appropriately. This function is designed to be +* used in critical sections of device drivers such that it is not necessary to +* disable other device interrupts. +* +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* None. +* +******************************************************************************/ +#define XIIF_V123B_GINTR_ENABLE(RegBaseAddress) \ + XIo_Out32((RegBaseAddress) + XIIF_V123B_DGIER_OFFSET, \ + XIIF_V123B_GINTR_ENABLE_MASK) + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_IS_GINTR_ENABLED +* +* DESCRIPTION: +* +* This function determines if interrupts are enabled at the global level by +* reading the gloabl interrupt register. This register provides the ability to +* disable interrupts without any modifications to the interrupt enable register +* such that it is minimal effort to restore the interrupts to the previous +* enabled state. +* +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* RETURN VALUE: +* +* TRUE if interrupts are enabled for the IPIF, FALSE otherwise. +* +* NOTES: +* +* None. +* +******************************************************************************/ +#define XIIF_V123B_IS_GINTR_ENABLED(RegBaseAddress) \ + (XIo_In32((RegBaseAddress) + XIIF_V123B_DGIER_OFFSET) == \ + XIIF_V123B_GINTR_ENABLE_MASK) + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_WRITE_IISR +* +* DESCRIPTION: +* +* This function sets the IP interrupt status register to the specified value. +* This register indicates the status of interrupt sources for the IP of the +* device. The IP is defined as the part of the device that connects to the +* IPIF. The status is independent of whether interrupts are enabled such that +* the status register may also be polled when interrupts are not enabled. +* +* Each bit of the register correlates to a specific interrupt source within the +* IP. All bits of this register are latched. Setting a bit which is zero +* within this register causes an interrupt to be generated. The device global +* interrupt enable register and the device interrupt enable register must be set +* appropriately to allow an interrupt to be passed out of the device. The +* interrupt is cleared by writing to this register with the bits to be +* cleared set to a one and all others to zero. This register implements a +* toggle on write functionality meaning any bits which are set in the value +* written cause the bits in the register to change to the opposite state. +* +* This function writes only the specified value to the register such that +* some status bits may be set and others cleared. It is the caller's +* responsibility to get the value of the register prior to setting the value +* to prevent an destructive behavior. +* +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* Status contains the value to be written to the IP interrupt status +* register. The bit definitions are specific to the device IP. +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* None. +* +******************************************************************************/ +#define XIIF_V123B_WRITE_IISR(RegBaseAddress, Status) \ + XIo_Out32((RegBaseAddress) + XIIF_V123B_IISR_OFFSET, (Status)) + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_READ_IISR +* +* DESCRIPTION: +* +* This function gets the contents of the IP interrupt status register. +* This register indicates the status of interrupt sources for the IP of the +* device. The IP is defined as the part of the device that connects to the +* IPIF. The status is independent of whether interrupts are enabled such +* that the status register may also be polled when interrupts are not enabled. +* +* Each bit of the register correlates to a specific interrupt source within the +* device. All bits of this register are latched. Writing a 1 to a bit within +* this register causes an interrupt to be generated if enabled in the interrupt +* enable register and the global interrupt enable is set. Since the status is +* latched, each status bit must be acknowledged in order for the bit in the +* status register to be updated. Each bit can be acknowledged by writing a +* 0 to the bit in the status register. + +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* RETURN VALUE: +* +* A status which contains the value read from the IP interrupt status register. +* The bit definitions are specific to the device IP. +* +* NOTES: +* +* None. +* +******************************************************************************/ +#define XIIF_V123B_READ_IISR(RegBaseAddress) \ + XIo_In32((RegBaseAddress) + XIIF_V123B_IISR_OFFSET) + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_WRITE_IIER +* +* DESCRIPTION: +* +* This function sets the IP interrupt enable register contents. This register +* controls which interrupt sources of the IP are allowed to generate an +* interrupt. The global interrupt enable register and the device interrupt +* enable register must also be set appropriately for an interrupt to be +* passed out of the device containing the IPIF and the IP. +* +* Each bit of the register correlates to a specific interrupt source within the +* IP. Setting a bit in this register enables the interrupt source to generate +* an interrupt. Clearing a bit in this register disables interrupt generation +* for that interrupt source. +* +* This function writes only the specified value to the register such that +* some interrupt sources may be enabled and others disabled. It is the +* caller's responsibility to get the value of the interrupt enable register +* prior to setting the value to prevent an destructive behavior. +* +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* Enable contains the value to be written to the IP interrupt enable register. +* The bit definitions are specific to the device IP. +* +* RETURN VALUE: +* +* None. +* +* NOTES: +* +* None. +* +******************************************************************************/ +#define XIIF_V123B_WRITE_IIER(RegBaseAddress, Enable) \ + XIo_Out32((RegBaseAddress) + XIIF_V123B_IIER_OFFSET, (Enable)) + +/****************************************************************************** +* +* MACRO: +* +* XIIF_V123B_READ_IIER +* +* DESCRIPTION: +* +* +* This function gets the IP interrupt enable register contents. This register +* controls which interrupt sources of the IP are allowed to generate an +* interrupt. The global interrupt enable register and the device interrupt +* enable register must also be set appropriately for an interrupt to be +* passed out of the device containing the IPIF and the IP. +* +* Each bit of the register correlates to a specific interrupt source within the +* IP. Setting a bit in this register enables the interrupt source to generate +* an interrupt. Clearing a bit in this register disables interrupt generation +* for that interrupt source. +* +* ARGUMENTS: +* +* RegBaseAddress contains the base address of the IPIF registers. +* +* RETURN VALUE: +* +* The contents read from the IP interrupt enable register. The bit definitions +* are specific to the device IP. +* +* NOTES: +* +* Signature: u32 XIIF_V123B_READ_IIER(u32 RegBaseAddress) +* +******************************************************************************/ +#define XIIF_V123B_READ_IIER(RegBaseAddress) \ + XIo_In32((RegBaseAddress) + XIIF_V123B_IIER_OFFSET) + +/************************** Function Prototypes ******************************/ + +/* + * Initialization Functions + */ +XStatus XIpIfV123b_SelfTest(u32 RegBaseAddress, u8 IpRegistersWidth); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xpacket_fifo_v1_00_b.c linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xpacket_fifo_v1_00_b.c --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xpacket_fifo_v1_00_b.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xpacket_fifo_v1_00_b.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,449 @@ +/* $Id: xpacket_fifo_v1_00_b.c,v 1.2 2002/06/12 18:00:48 linnj Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/* +* +* @file xpacket_fifo_v1_00_b.c +* +* Contains functions for the XPacketFifoV100b component. See xpacket_fifo_v1_00_b.h +* for more information about the component. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00b rpm 03/26/02  First release
+* 
+* +*****************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xio.h" +#include "xstatus.h" +#include "xpacket_fifo_v1_00_b.h" + +/************************** Constant Definitions *****************************/ + +/* width of a FIFO word */ + +#define XPF_FIFO_WIDTH_BYTE_COUNT 4UL + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************* Variable Definitions ******************************/ + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +/* +* +* This function initializes a packet FIFO. Initialization resets the +* FIFO such that it's empty and ready to use. +* +* @param InstancePtr contains a pointer to the FIFO to operate on. +* @param RegBaseAddress contains the base address of the registers for +* the packet FIFO. +* @param DataBaseAddress contains the base address of the data for +* the packet FIFO. +* +* @return +* +* Always returns XST_SUCCESS. +* +* @note +* +* None. +* +******************************************************************************/ +XStatus +XPacketFifoV100b_Initialize(XPacketFifoV100b * InstancePtr, + u32 RegBaseAddress, u32 DataBaseAddress) +{ + /* assert to verify input argument are valid */ + + XASSERT_NONVOID(InstancePtr != NULL); + + /* initialize the component variables to the specified state */ + + InstancePtr->RegBaseAddress = RegBaseAddress; + InstancePtr->DataBaseAddress = DataBaseAddress; + InstancePtr->IsReady = XCOMPONENT_IS_READY; + + /* reset the FIFO such that it's empty and ready to use and indicate the + * initialization was successful, note that the is ready variable must be + * set prior to calling the reset function to prevent an assert + */ + XPF_V100B_RESET(InstancePtr); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/* +* +* This function performs a self-test on the specified packet FIFO. The self +* test resets the FIFO and reads a register to determine if it is the correct +* reset value. This test is destructive in that any data in the FIFO will +* be lost. +* +* @param InstancePtr is a pointer to the packet FIFO to be operated on. +* +* @param FifoType specifies the type of FIFO, read or write, for the self test. +* The FIFO type is specified by the values XPF_READ_FIFO_TYPE or +* XPF_WRITE_FIFO_TYPE. +* +* @return +* +* XST_SUCCESS is returned if the selftest is successful, or +* XST_PFIFO_BAD_REG_VALUE indicating that the value readback from the +* occupancy/vacancy count register after a reset does not match the +* specified reset value. +* +* @note +* +* None. +* +******************************************************************************/ +XStatus +XPacketFifoV100b_SelfTest(XPacketFifoV100b * InstancePtr, u32 FifoType) +{ + u32 Register; + + /* assert to verify valid input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID((FifoType == XPF_READ_FIFO_TYPE) || + (FifoType == XPF_WRITE_FIFO_TYPE)); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* reset the fifo and then check to make sure the occupancy/vacancy + * register contents are correct for a reset condition + */ + XPF_V100B_RESET(InstancePtr); + + Register = XIo_In32(InstancePtr->RegBaseAddress + + XPF_COUNT_STATUS_REG_OFFSET); + + /* check the value of the register to ensure that it's correct for the + * specified FIFO type since both FIFO types reset to empty, but a bit + * in the register changes definition based upon FIFO type + */ + + if (FifoType == XPF_READ_FIFO_TYPE) { + /* check the regiser value for a read FIFO which should be empty */ + + if (Register != XPF_EMPTY_FULL_MASK) { + return XST_PFIFO_BAD_REG_VALUE; + } + } else { + /* check the register value for a write FIFO which should not be full + * on reset + */ + if ((Register & XPF_EMPTY_FULL_MASK) != 0) { + return XST_PFIFO_BAD_REG_VALUE; + } + } + + /* the test was successful */ + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/* +* +* Read data from a FIFO and puts it into a specified buffer. The packet FIFO is +* currently 32 bits wide such that an input buffer which is a series of bytes +* is filled from the FIFO a word at a time. If the requested byte count is not +* a multiple of 32 bit words, it is necessary for this function to format the +* remaining 32 bit word from the FIFO into a series of bytes in the buffer. +* There may be up to 3 extra bytes which must be extracted from the last word +* of the FIFO and put into the buffer. +* +* @param InstancePtr contains a pointer to the FIFO to operate on. +* @param BufferPtr points to the memory buffer to write the data into. This +* buffer must be 32 bit aligned or an alignment exception could be +* generated. Since this buffer is a byte buffer, the data is assumed to +* be endian independent. +* @param ByteCount contains the number of bytes to read from the FIFO. This +* number of bytes must be present in the FIFO or an error will be +* returned. +* +* @return +* +* XST_SUCCESS indicates the operation was successful. If the number of +* bytes specified by the byte count is not present in the FIFO +* XST_PFIFO_LACK_OF_DATA is returned. +* +* If the function was successful, the specified buffer is modified to contain +* the bytes which were removed from the FIFO. +* +* @note +* +* Note that the exact number of bytes which are present in the FIFO is +* not known by this function. It can only check for a number of 32 bit +* words such that if the byte count specified is incorrect, but is still +* possible based on the number of words in the FIFO, up to 3 garbage bytes +* may be present at the end of the buffer. +*

+* This function assumes that if the device consuming data from the FIFO is +* a byte device, the order of the bytes to be consumed is from the most +* significant byte to the least significant byte of a 32 bit word removed +* from the FIFO. +* +******************************************************************************/ +XStatus +XPacketFifoV100b_Read(XPacketFifoV100b * InstancePtr, + u8 * BufferPtr, u32 ByteCount) +{ + u32 FifoCount; + u32 WordCount; + u32 ExtraByteCount; + u32 *WordBuffer = (u32 *) BufferPtr; + + /* assert to verify valid input arguments including 32 bit alignment of + * the buffer pointer + */ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(BufferPtr != NULL); + XASSERT_NONVOID(((u32) BufferPtr & + (XPF_FIFO_WIDTH_BYTE_COUNT - 1)) == 0); + XASSERT_NONVOID(ByteCount != 0); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* get the count of how many 32 bit words are in the FIFO, if there aren't + * enought words to satisfy the request, return an error + */ + + FifoCount = XIo_In32(InstancePtr->RegBaseAddress + + XPF_COUNT_STATUS_REG_OFFSET) & XPF_COUNT_MASK; + + if ((FifoCount * XPF_FIFO_WIDTH_BYTE_COUNT) < ByteCount) { + return XST_PFIFO_LACK_OF_DATA; + } + + /* calculate the number of words to read from the FIFO before the word + * containing the extra bytes, and calculate the number of extra bytes + * the extra bytes are defined as those at the end of the buffer when + * the buffer does not end on a 32 bit boundary + */ + WordCount = ByteCount / XPF_FIFO_WIDTH_BYTE_COUNT; + ExtraByteCount = ByteCount % XPF_FIFO_WIDTH_BYTE_COUNT; + + /* Read the 32 bit words from the FIFO for all the buffer except the + * last word which contains the extra bytes, the following code assumes + * that the buffer is 32 bit aligned, otherwise an alignment exception could + * be generated + */ + for (FifoCount = 0; FifoCount < WordCount; FifoCount++) { + WordBuffer[FifoCount] = XIo_In32(InstancePtr->DataBaseAddress); + } + + /* if there are extra bytes to handle, read the last word from the FIFO + * and insert the extra bytes into the buffer + */ + if (ExtraByteCount > 0) { + u32 LastWord; + u8 *ExtraBytesBuffer = (u8 *) (WordBuffer + WordCount); + + /* get the last word from the FIFO for the extra bytes */ + + LastWord = XIo_In32(InstancePtr->DataBaseAddress); + + /* one extra byte in the last word, put the byte into the next location + * of the buffer, bytes in a word of the FIFO are ordered from most + * significant byte to least + */ + if (ExtraByteCount == 1) { + ExtraBytesBuffer[0] = (u8) (LastWord >> 24); + } + + /* two extra bytes in the last word, put each byte into the next two + * locations of the buffer + */ + else if (ExtraByteCount == 2) { + ExtraBytesBuffer[0] = (u8) (LastWord >> 24); + ExtraBytesBuffer[1] = (u8) (LastWord >> 16); + } + /* three extra bytes in the last word, put each byte into the next three + * locations of the buffer + */ + else if (ExtraByteCount == 3) { + ExtraBytesBuffer[0] = (u8) (LastWord >> 24); + ExtraBytesBuffer[1] = (u8) (LastWord >> 16); + ExtraBytesBuffer[2] = (u8) (LastWord >> 8); + } + } + return XST_SUCCESS; +} + +/*****************************************************************************/ +/* +* +* Write data into a packet FIFO. The packet FIFO is currently 32 bits wide +* such that an input buffer which is a series of bytes must be written into the +* FIFO a word at a time. If the buffer is not a multiple of 32 bit words, it is +* necessary for this function to format the remaining bytes into a single 32 +* bit word to be inserted into the FIFO. This is necessary to avoid any +* accesses past the end of the buffer. +* +* @param InstancePtr contains a pointer to the FIFO to operate on. +* @param BufferPtr points to the memory buffer that data is to be read from +* and written into the FIFO. Since this buffer is a byte buffer, the data +* is assumed to be endian independent. This buffer must be 32 bit aligned +* or an alignment exception could be generated. +* @param ByteCount contains the number of bytes to read from the buffer and to +* write to the FIFO. +* +* @return +* +* XST_SUCCESS is returned if the operation succeeded. If there is not enough +* room in the FIFO to hold the specified bytes, XST_PFIFO_NO_ROOM is +* returned. +* +* @note +* +* This function assumes that if the device inserting data into the FIFO is +* a byte device, the order of the bytes in each 32 bit word is from the most +* significant byte to the least significant byte. +* +******************************************************************************/ +XStatus +XPacketFifoV100b_Write(XPacketFifoV100b * InstancePtr, + u8 * BufferPtr, u32 ByteCount) +{ + u32 FifoCount; + u32 WordCount; + u32 ExtraByteCount; + u32 *WordBuffer = (u32 *) BufferPtr; + + /* assert to verify valid input arguments including 32 bit alignment of + * the buffer pointer + */ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(BufferPtr != NULL); + XASSERT_NONVOID(((u32) BufferPtr & + (XPF_FIFO_WIDTH_BYTE_COUNT - 1)) == 0); + XASSERT_NONVOID(ByteCount != 0); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* get the count of how many words may be inserted into the FIFO */ + + FifoCount = XIo_In32(InstancePtr->RegBaseAddress + + XPF_COUNT_STATUS_REG_OFFSET) & XPF_COUNT_MASK; + + /* Calculate the number of 32 bit words required to insert the specified + * number of bytes in the FIFO and determine the number of extra bytes + * if the buffer length is not a multiple of 32 bit words + */ + + WordCount = ByteCount / XPF_FIFO_WIDTH_BYTE_COUNT; + ExtraByteCount = ByteCount % XPF_FIFO_WIDTH_BYTE_COUNT; + + /* take into account the extra bytes in the total word count */ + + if (ExtraByteCount > 0) { + WordCount++; + } + + /* if there's not enough room in the FIFO to hold the specified + * number of bytes, then indicate an error, + */ + if (FifoCount < WordCount) { + return XST_PFIFO_NO_ROOM; + } + + /* readjust the word count to not take into account the extra bytes */ + + if (ExtraByteCount > 0) { + WordCount--; + } + + /* Write all the bytes of the buffer which can be written as 32 bit + * words into the FIFO, waiting to handle the extra bytes seperately + */ + for (FifoCount = 0; FifoCount < WordCount; FifoCount++) { + XIo_Out32(InstancePtr->DataBaseAddress, WordBuffer[FifoCount]); + } + + /* if there are extra bytes to handle, extract them from the buffer + * and create a 32 bit word and write it to the FIFO + */ + if (ExtraByteCount > 0) { + u32 LastWord = 0; + u8 *ExtraBytesBuffer = (u8 *) (WordBuffer + WordCount); + + /* one extra byte in the buffer, put the byte into the last word + * to be inserted into the FIFO, perform this processing inline rather + * than in a loop to help performance + */ + if (ExtraByteCount == 1) { + LastWord = ExtraBytesBuffer[0] << 24; + } + + /* two extra bytes in the buffer, put each byte into the last word + * to be inserted into the FIFO + */ + else if (ExtraByteCount == 2) { + LastWord = ExtraBytesBuffer[0] << 24 | + ExtraBytesBuffer[1] << 16; + } + + /* three extra bytes in the buffer, put each byte into the last word + * to be inserted into the FIFO + */ + else if (ExtraByteCount == 3) { + LastWord = ExtraBytesBuffer[0] << 24 | + ExtraBytesBuffer[1] << 16 | + ExtraBytesBuffer[2] << 8; + } + + /* write the last 32 bit word to the FIFO and return with no errors */ + + XIo_Out32(InstancePtr->DataBaseAddress, LastWord); + } + + return XST_SUCCESS; +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xpacket_fifo_v1_00_b.h linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xpacket_fifo_v1_00_b.h --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xpacket_fifo_v1_00_b.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xpacket_fifo_v1_00_b.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,307 @@ +/* $Id: xpacket_fifo_v1_00_b.h,v 1.2 2002/06/12 18:00:48 linnj Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/* +* +* @file xpacket_fifo_v1_00_b.h +* +* This component is a common component because it's primary purpose is to +* prevent code duplication in drivers. A driver which must handle a packet +* FIFO uses this component rather than directly manipulating a packet FIFO. +* +* A FIFO is a device which has dual port memory such that one user may be +* inserting data into the FIFO while another is consuming data from the FIFO. +* A packet FIFO is designed for use with packet protocols such as Ethernet and +* ATM. It is typically only used with devices when DMA and/or Scatter Gather +* is used. It differs from a nonpacket FIFO in that it does not provide any +* interrupts for thresholds of the FIFO such that it is less useful without +* DMA. +* +* @note +* +* This component has the capability to generate an interrupt when an error +* condition occurs. It is the user's responsibility to provide the interrupt +* processing to handle the interrupt. This component provides the ability to +* determine if that interrupt is active, a deadlock condition, and the ability +* to reset the FIFO to clear the condition. In this condition, the device which +* is using the FIFO should also be reset to prevent other problems. This error +* condition could occur as a normal part of operation if the size of the FIFO +* is not setup correctly. See the hardware IP specification for more details. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00b rpm 03/26/02  First release
+* 
+* +*****************************************************************************/ +#ifndef XPACKET_FIFO_H /* prevent circular inclusions */ +#define XPACKET_FIFO_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xstatus.h" + +/************************** Constant Definitions *****************************/ + +/* + * These constants specify the FIFO type and are mutually exclusive + */ +#define XPF_READ_FIFO_TYPE 0 /* a read FIFO */ +#define XPF_WRITE_FIFO_TYPE 1 /* a write FIFO */ + +/* + * These constants define the offsets to each of the registers from the + * register base address, each of the constants are a number of bytes + */ +#define XPF_RESET_REG_OFFSET 0UL +#define XPF_MODULE_INFO_REG_OFFSET 0UL +#define XPF_COUNT_STATUS_REG_OFFSET 4UL + +/* + * This constant is used with the Reset Register + */ +#define XPF_RESET_FIFO_MASK 0x0000000A + +/* + * These constants are used with the Occupancy/Vacancy Count Register. This + * register also contains FIFO status + */ +#define XPF_COUNT_MASK 0x0000FFFF +#define XPF_DEADLOCK_MASK 0x20000000 +#define XPF_ALMOST_EMPTY_FULL_MASK 0x40000000 +#define XPF_EMPTY_FULL_MASK 0x80000000 + +/**************************** Type Definitions *******************************/ + +/* + * The XPacketFifo driver instance data. The driver is required to allocate a + * variable of this type for every packet FIFO in the device. + */ +typedef struct { + u32 RegBaseAddress; /* Base address of registers */ + u32 IsReady; /* Device is initialized and ready */ + u32 DataBaseAddress; /* Base address of data for FIFOs */ +} XPacketFifoV100b; + +/***************** Macros (Inline Functions) Definitions *********************/ + +/*****************************************************************************/ +/* +* +* Reset the specified packet FIFO. Resetting a FIFO will cause any data +* contained in the FIFO to be lost. +* +* @param InstancePtr contains a pointer to the FIFO to operate on. +* +* @return +* +* None. +* +* @note +* +* Signature: void XPF_V100B_RESET(XPacketFifoV100b *InstancePtr) +* +******************************************************************************/ +#define XPF_V100B_RESET(InstancePtr) \ + XIo_Out32((InstancePtr)->RegBaseAddress + XPF_RESET_REG_OFFSET, XPF_RESET_FIFO_MASK); + +/*****************************************************************************/ +/* +* +* Get the occupancy count for a read packet FIFO and the vacancy count for a +* write packet FIFO. These counts indicate the number of 32-bit words +* contained (occupancy) in the FIFO or the number of 32-bit words available +* to write (vacancy) in the FIFO. +* +* @param InstancePtr contains a pointer to the FIFO to operate on. +* +* @return +* +* The occupancy or vacancy count for the specified packet FIFO. +* +* @note +* +* Signature: u32 XPF_V100B_GET_COUNT(XPacketFifoV100b *InstancePtr) +* +******************************************************************************/ +#define XPF_V100B_GET_COUNT(InstancePtr) \ + (XIo_In32((InstancePtr)->RegBaseAddress + XPF_COUNT_STATUS_REG_OFFSET) & \ + XPF_COUNT_MASK) + +/*****************************************************************************/ +/* +* +* Determine if the specified packet FIFO is almost empty. Almost empty is +* defined for a read FIFO when there is only one data word in the FIFO. +* +* @param InstancePtr contains a pointer to the FIFO to operate on. +* +* @return +* +* TRUE if the packet FIFO is almost empty, FALSE otherwise. +* +* @note +* +* Signature: u32 XPF_V100B_IS_ALMOST_EMPTY(XPacketFifoV100b *InstancePtr) +* +******************************************************************************/ +#define XPF_V100B_IS_ALMOST_EMPTY(InstancePtr) \ + (XIo_In32((InstancePtr)->RegBaseAddress + XPF_COUNT_STATUS_REG_OFFSET) & \ + XPF_ALMOST_EMPTY_FULL_MASK) + +/*****************************************************************************/ +/* +* +* Determine if the specified packet FIFO is almost full. Almost full is +* defined for a write FIFO when there is only one available data word in the +* FIFO. +* +* @param InstancePtr contains a pointer to the FIFO to operate on. +* +* @return +* +* TRUE if the packet FIFO is almost full, FALSE otherwise. +* +* @note +* +* Signature: u32 XPF_V100B_IS_ALMOST_FULL(XPacketFifoV100b *InstancePtr) +* +******************************************************************************/ +#define XPF_V100B_IS_ALMOST_FULL(InstancePtr) \ + (XIo_In32((InstancePtr)->RegBaseAddress + XPF_COUNT_STATUS_REG_OFFSET) & \ + XPF_ALMOST_EMPTY_FULL_MASK) + +/*****************************************************************************/ +/* +* +* Determine if the specified packet FIFO is empty. This applies only to a +* read FIFO. +* +* @param InstancePtr contains a pointer to the FIFO to operate on. +* +* @return +* +* TRUE if the packet FIFO is empty, FALSE otherwise. +* +* @note +* +* Signature: u32 XPF_V100B_IS_EMPTY(XPacketFifoV100b *InstancePtr) +* +******************************************************************************/ +#define XPF_V100B_IS_EMPTY(InstancePtr) \ + (XIo_In32((InstancePtr)->RegBaseAddress + XPF_COUNT_STATUS_REG_OFFSET) & \ + XPF_EMPTY_FULL_MASK) + +/*****************************************************************************/ +/* +* +* Determine if the specified packet FIFO is full. This applies only to a +* write FIFO. +* +* @param InstancePtr contains a pointer to the FIFO to operate on. +* +* @return +* +* TRUE if the packet FIFO is full, FALSE otherwise. +* +* @note +* +* Signature: u32 XPF_V100B_IS_FULL(XPacketFifoV100b *InstancePtr) +* +******************************************************************************/ +#define XPF_V100B_IS_FULL(InstancePtr) \ + (XIo_In32((InstancePtr)->RegBaseAddress + XPF_COUNT_STATUS_REG_OFFSET) & \ + XPF_EMPTY_FULL_MASK) + +/*****************************************************************************/ +/* +* +* Determine if the specified packet FIFO is deadlocked. This condition occurs +* when the FIFO is full and empty at the same time and is caused by a packet +* being written to the FIFO which exceeds the total data capacity of the FIFO. +* It occurs because of the mark/restore features of the packet FIFO which allow +* retransmission of a packet. The software should reset the FIFO and any devices +* using the FIFO when this condition occurs. +* +* @param InstancePtr contains a pointer to the FIFO to operate on. +* +* @return +* +* TRUE if the packet FIFO is deadlocked, FALSE otherwise. +* +* @note +* +* This component has the capability to generate an interrupt when an error +* condition occurs. It is the user's responsibility to provide the interrupt +* processing to handle the interrupt. This function provides the ability to +* determine if a deadlock condition, and the ability to reset the FIFO to +* clear the condition. +* +* In this condition, the device which is using the FIFO should also be reset +* to prevent other problems. This error condition could occur as a normal part +* of operation if the size of the FIFO is not setup correctly. +* +* Signature: u32 XPF_V100B_IS_DEADLOCKED(XPacketFifoV100b *InstancePtr) +* +******************************************************************************/ +#define XPF_V100B_IS_DEADLOCKED(InstancePtr) \ + (XIo_In32((InstancePtr)->RegBaseAddress + XPF_COUNT_STATUS_REG_OFFSET) & \ + XPF_DEADLOCK_MASK) + +/************************** Function Prototypes ******************************/ + +/* Standard functions */ + +XStatus XPacketFifoV100b_Initialize(XPacketFifoV100b * InstancePtr, + u32 RegBaseAddress, u32 DataBaseAddress); +XStatus XPacketFifoV100b_SelfTest(XPacketFifoV100b * InstancePtr, u32 FifoType); + +/* Data functions */ + +XStatus XPacketFifoV100b_Read(XPacketFifoV100b * InstancePtr, + u8 * ReadBufferPtr, u32 ByteCount); +XStatus XPacketFifoV100b_Write(XPacketFifoV100b * InstancePtr, + u8 * WriteBufferPtr, u32 ByteCount); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xparameters.h linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xparameters.h --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xparameters.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xparameters.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,18 @@ +/* + * xparameters.h + * + * This file includes the correct xparameters.h for the CONFIG'ed board + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#include + +#if defined(CONFIG_XILINX_ML300) +#include "xparameters_ml300.h" +#endif diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xparameters_ml300.h linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xparameters_ml300.h --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xparameters_ml300.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xparameters_ml300.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,203 @@ + +/******************************************************************* +* +* CAUTION: This file is automatically generated by libgen. +* Version: Xilinx EDK 3.2.2 EDK_Cm.22 +* DO NOT EDIT. +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +* Description: Driver parameters +* +*******************************************************************/ + +#define XPAR_XEMAC_NUM_INSTANCES 1 +#define XPAR_OPB_ETHERNET_0_BASEADDR 0x60000000 +#define XPAR_OPB_ETHERNET_0_HIGHADDR 0x60003FFF +#define XPAR_OPB_ETHERNET_0_DEVICE_ID 0 +#define XPAR_OPB_ETHERNET_0_ERR_COUNT_EXIST 1 +#define XPAR_OPB_ETHERNET_0_DMA_PRESENT 1 +#define XPAR_OPB_ETHERNET_0_MII_EXIST 1 + +/******************************************************************/ + +#define XPAR_XGPIO_NUM_INSTANCES 2 +#define XPAR_MY_OPB_GPIO_0_BASEADDR 0x90000000 +#define XPAR_MY_OPB_GPIO_0_HIGHADDR 0x90000007 +#define XPAR_MY_OPB_GPIO_0_DEVICE_ID 0 +#define XPAR_MY_OPB_GPIO_1_BASEADDR 0x90000008 +#define XPAR_MY_OPB_GPIO_1_HIGHADDR 0x9000000F +#define XPAR_MY_OPB_GPIO_1_DEVICE_ID 1 + +/******************************************************************/ + +#define XPAR_XIIC_NUM_INSTANCES 1 +#define XPAR_OPB_IIC_0_BASEADDR 0xA8000000 +#define XPAR_OPB_IIC_0_HIGHADDR 0xA80001FF +#define XPAR_OPB_IIC_0_DEVICE_ID 0 +#define XPAR_OPB_IIC_0_TEN_BIT_ADR 0 + +/******************************************************************/ + +#define XPAR_XUARTNS550_NUM_INSTANCES 1 +#define XPAR_XUARTNS550_CLOCK_HZ 100000000 +#define XPAR_OPB_UART16550_0_BASEADDR 0xA0000000 +#define XPAR_OPB_UART16550_0_HIGHADDR 0xA0001FFF +#define XPAR_OPB_UART16550_0_DEVICE_ID 0 + +/******************************************************************/ + +#define XPAR_XINTC_HAS_IPR 1 +#define XPAR_INTC_MAX_NUM_INTR_INPUTS 7 +#define XPAR_XINTC_NUM_INSTANCES 1 +#define XPAR_XINTC_USE_DCR 0 +#define XPAR_DCR_INTC_0_BASEADDR 0xD0000FC0 +#define XPAR_DCR_INTC_0_HIGHADDR 0xD0000FDF +#define XPAR_DCR_INTC_0_DEVICE_ID 0 +#define XPAR_DCR_INTC_0_KIND_OF_INTR 0x00000000 + +/******************************************************************/ + +#define XPAR_DCR_INTC_0_MISC_LOGIC_0_PHY_MII_INT_INTR 0 +#define XPAR_DCR_INTC_0_OPB_ETHERNET_0_IP2INTC_IRPT_INTR 1 +#define XPAR_DCR_INTC_0_MISC_LOGIC_0_IIC_TEMP_CRIT_INTR 2 +#define XPAR_DCR_INTC_0_MISC_LOGIC_0_IIC_IRQ_INTR 3 +#define XPAR_DCR_INTC_0_OPB_IIC_0_IP2INTC_IRPT_INTR 4 +#define XPAR_DCR_INTC_0_OPB_SYSACE_0_SYSACE_IRQ_INTR 5 +#define XPAR_DCR_INTC_0_OPB_UART16550_0_IP2INTC_IRPT_INTR 6 + +/******************************************************************/ + +#define XPAR_XPLB2OPB_NUM_INSTANCES 1 +#define XPAR_PLB2OPB_BRIDGE_0_DCR_BASEADDR 0xD0000000 +#define XPAR_PLB2OPB_BRIDGE_0_DCR_HIGHADDR 0xD000001F + +/******************************************************************/ + +#define XPAR_PLB2OPB_BRIDGE_0_BASEADDR XPAR_PLB2OPB_BRIDGE_0_DCR_BASEADDR +#define XPAR_PLB2OPB_BRIDGE_0_HIGHADDR XPAR_PLB2OPB_BRIDGE_0_DCR_HIGHADDR + +/******************************************************************/ + +#define XPAR_XPLBARB_NUM_INSTANCES 1 +#define XPAR_PLB_V34_0_BASEADDR 0xD0000040 +#define XPAR_PLB_V34_0_HIGHADDR 0xD000007F + +/******************************************************************/ + +#define XPAR_XSYSACE_NUM_INSTANCES 1 +#define XPAR_OPB_SYSACE_0_BASEADDR 0xCF000000 +#define XPAR_OPB_SYSACE_0_HIGHADDR 0xCF0001FF +#define XPAR_OPB_SYSACE_0_MEM_WIDTH 8 +#define XPAR_OPB_SYSACE_0_DEVICE_ID 0 + +/******************************************************************/ + +#define XPAR_PLB_BRAM_IF_CNTLR_0_BASEADDR 0xFFFF8000 +#define XPAR_PLB_BRAM_IF_CNTLR_0_HIGHADDR 0xFFFFFFFF +#define XPAR_PLB_DDR_0_BASEADDR 0x00000000 +#define XPAR_PLB_DDR_0_HIGHADDR 0x0FFFFFFF + +/******************************************************************/ + +#define XPAR_CPU_PPC405_CORE_CLOCK_FREQ_HZ 300000000 + +/******************************************************************/ + +/* Linux Redefines */ + +/******************************************************************/ + +#define XPAR_UARTNS550_0_BASEADDR (XPAR_OPB_UART16550_0_BASEADDR+0x1000) +#define XPAR_UARTNS550_0_HIGHADDR XPAR_OPB_UART16550_0_HIGHADDR +#define XPAR_UARTNS550_0_CLOCK_FREQ_HZ XPAR_XUARTNS550_CLOCK_HZ +#define XPAR_UARTNS550_0_DEVICE_ID XPAR_OPB_UART16550_0_DEVICE_ID + +/******************************************************************/ + +#define XPAR_GPIO_0_BASEADDR XPAR_MY_OPB_GPIO_0_BASEADDR +#define XPAR_GPIO_0_HIGHADDR XPAR_MY_OPB_GPIO_0_HIGHADDR +#define XPAR_GPIO_0_DEVICE_ID XPAR_MY_OPB_GPIO_0_DEVICE_ID +#define XPAR_GPIO_1_BASEADDR XPAR_MY_OPB_GPIO_1_BASEADDR +#define XPAR_GPIO_1_HIGHADDR XPAR_MY_OPB_GPIO_1_HIGHADDR +#define XPAR_GPIO_1_DEVICE_ID XPAR_MY_OPB_GPIO_1_DEVICE_ID + +/******************************************************************/ + +#define XPAR_IIC_0_BASEADDR XPAR_OPB_IIC_0_BASEADDR +#define XPAR_IIC_0_HIGHADDR XPAR_OPB_IIC_0_HIGHADDR +#define XPAR_IIC_0_TEN_BIT_ADR XPAR_OPB_IIC_0_TEN_BIT_ADR +#define XPAR_IIC_0_DEVICE_ID XPAR_OPB_IIC_0_DEVICE_ID + +/******************************************************************/ + +#define XPAR_PERSISTENT_0_IIC_0_BASEADDR 1024 +#define XPAR_PERSISTENT_0_IIC_0_HIGHADDR 2047 +#define XPAR_PERSISTENT_0_IIC_0_EEPROMADDR 0xA0 +#define XPAR_SYSACE_0_BASEADDR XPAR_OPB_SYSACE_0_BASEADDR +#define XPAR_SYSACE_0_HIGHADDR XPAR_OPB_SYSACE_0_HIGHADDR +#define XPAR_SYSACE_0_DEVICE_ID XPAR_OPB_SYSACE_0_DEVICE_ID + +/******************************************************************/ + +#define XPAR_INTC_0_BASEADDR XPAR_DCR_INTC_0_BASEADDR +#define XPAR_INTC_0_HIGHADDR XPAR_DCR_INTC_0_HIGHADDR +#define XPAR_INTC_0_DEVICE_ID XPAR_DCR_INTC_0_DEVICE_ID + +/******************************************************************/ + +#define XPAR_INTC_0_EMAC_0_VEC_ID XPAR_DCR_INTC_0_OPB_ETHERNET_0_IP2INTC_IRPT_INTR +#define XPAR_INTC_0_IIC_0_VEC_ID XPAR_DCR_INTC_0_OPB_IIC_0_IP2INTC_IRPT_INTR +#define XPAR_INTC_0_SYSACE_0_VEC_ID XPAR_DCR_INTC_0_OPB_SYSACE_0_SYSACE_IRQ_INTR +#define XPAR_INTC_0_UARTNS550_0_VEC_ID XPAR_DCR_INTC_0_OPB_UART16550_0_IP2INTC_IRPT_INTR + +/******************************************************************/ + +#define XPAR_EMAC_0_BASEADDR XPAR_OPB_ETHERNET_0_BASEADDR +#define XPAR_EMAC_0_HIGHADDR XPAR_OPB_ETHERNET_0_HIGHADDR +#define XPAR_EMAC_0_DMA_PRESENT XPAR_OPB_ETHERNET_0_DMA_PRESENT +#define XPAR_EMAC_0_MII_EXIST XPAR_OPB_ETHERNET_0_MII_EXIST +#define XPAR_EMAC_0_ERR_COUNT_EXIST XPAR_OPB_ETHERNET_0_ERR_COUNT_EXIST +#define XPAR_EMAC_0_DEVICE_ID XPAR_OPB_ETHERNET_0_DEVICE_ID + +/******************************************************************/ + +#define XPAR_PLB_CLOCK_FREQ_HZ 100000000 +#define XPAR_CORE_CLOCK_FREQ_HZ XPAR_CPU_PPC405_CORE_CLOCK_FREQ_HZ +#define XPAR_DDR_0_SIZE 0x08000000 +#define XPAR_PCI_0_CLOCK_FREQ_HZ 0 + +/******************************************************************/ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xstatus.h linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xstatus.h --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xstatus.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xstatus.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,342 @@ +/* $Id: xstatus.h,v 1.19 2002/07/16 14:18:42 robertm Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xstatus.h +* +* This file contains Xilinx software status codes. Status codes have their +* own data type called XStatus. These codes are used throughout the Xilinx +* device drivers. +* +******************************************************************************/ + +#ifndef XSTATUS_H /* prevent circular inclusions */ +#define XSTATUS_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" + +/************************** Constant Definitions *****************************/ + +/*********************** Common statuses 0 - 500 *****************************/ + +#define XST_SUCCESS 0L +#define XST_FAILURE 1L +#define XST_DEVICE_NOT_FOUND 2L +#define XST_DEVICE_BLOCK_NOT_FOUND 3L +#define XST_INVALID_VERSION 4L +#define XST_DEVICE_IS_STARTED 5L +#define XST_DEVICE_IS_STOPPED 6L +#define XST_FIFO_ERROR 7L /* an error occurred during an + operation with a FIFO such as + an underrun or overrun, this + error requires the device to + be reset */ +#define XST_RESET_ERROR 8L /* an error occurred which requires + the device to be reset */ +#define XST_DMA_ERROR 9L /* a DMA error occurred, this error + typically requires the device + using the DMA to be reset */ +#define XST_NOT_POLLED 10L /* the device is not configured for + polled mode operation */ +#define XST_FIFO_NO_ROOM 11L /* a FIFO did not have room to put + the specified data into */ +#define XST_BUFFER_TOO_SMALL 12L /* the buffer is not large enough + to hold the expected data */ +#define XST_NO_DATA 13L /* there was no data available */ +#define XST_REGISTER_ERROR 14L /* a register did not contain the + expected value */ +#define XST_INVALID_PARAM 15L /* an invalid parameter was passed + into the function */ +#define XST_NOT_SGDMA 16L /* the device is not configured for + scatter-gather DMA operation */ +#define XST_LOOPBACK_ERROR 17L /* a loopback test failed */ +#define XST_NO_CALLBACK 18L /* a callback has not yet been + * registered */ +#define XST_NO_FEATURE 19L /* device is not configured with + * the requested feature */ +#define XST_NOT_INTERRUPT 20L /* device is not configured for + * interrupt mode operation */ +#define XST_DEVICE_BUSY 21L /* device is busy */ +#define XST_ERROR_COUNT_MAX 22L /* the error counters of a device + * have maxed out */ + +/***************** Utility Component statuses 401 - 500 *********************/ + +#define XST_MEMTEST_FAILED 401L /* memory test failed */ + +/***************** Common Components statuses 501 - 1000 *********************/ + +/********************* Packet Fifo statuses 501 - 510 ************************/ + +#define XST_PFIFO_LACK_OF_DATA 501L /* not enough data in FIFO */ +#define XST_PFIFO_NO_ROOM 502L /* not enough room in FIFO */ +#define XST_PFIFO_BAD_REG_VALUE 503L /* self test, a register value + was invalid after reset */ + +/************************** DMA statuses 511 - 530 ***************************/ + +#define XST_DMA_TRANSFER_ERROR 511L /* self test, DMA transfer + failed */ +#define XST_DMA_RESET_REGISTER_ERROR 512L /* self test, a register value + was invalid after reset */ +#define XST_DMA_SG_LIST_EMPTY 513L /* scatter gather list contains + no buffer descriptors ready + to be processed */ +#define XST_DMA_SG_IS_STARTED 514L /* scatter gather not stopped */ +#define XST_DMA_SG_IS_STOPPED 515L /* scatter gather not running */ +#define XST_DMA_SG_LIST_FULL 517L /* all the buffer desciptors of + the scatter gather list are + being used */ +#define XST_DMA_SG_BD_LOCKED 518L /* the scatter gather buffer + descriptor which is to be + copied over in the scatter + list is locked */ +#define XST_DMA_SG_NOTHING_TO_COMMIT 519L /* no buffer descriptors have been + put into the scatter gather + list to be commited */ +#define XST_DMA_SG_COUNT_EXCEEDED 521L /* the packet count threshold + specified was larger than the + total # of buffer descriptors + in the scatter gather list */ +#define XST_DMA_SG_LIST_EXISTS 522L /* the scatter gather list has + already been created */ +#define XST_DMA_SG_NO_LIST 523L /* no scatter gather list has + been created */ +#define XST_DMA_SG_BD_NOT_COMMITTED 524L /* the buffer descriptor which was + being started was not committed + to the list */ +#define XST_DMA_SG_NO_DATA 525L /* the buffer descriptor to start + has already been used by the + hardware so it can't be reused + */ + +/************************** IPIF statuses 531 - 550 ***************************/ + +#define XST_IPIF_REG_WIDTH_ERROR 531L /* an invalid register width + was passed into the function */ +#define XST_IPIF_RESET_REGISTER_ERROR 532L /* the value of a register at + reset was not valid */ +#define XST_IPIF_DEVICE_STATUS_ERROR 533L /* a write to the device interrupt + status register did not read + back correctly */ +#define XST_IPIF_DEVICE_ACK_ERROR 534L /* the device interrupt status + register did not reset when + acked */ +#define XST_IPIF_DEVICE_ENABLE_ERROR 535L /* the device interrupt enable + register was not updated when + other registers changed */ +#define XST_IPIF_IP_STATUS_ERROR 536L /* a write to the IP interrupt + status register did not read + back correctly */ +#define XST_IPIF_IP_ACK_ERROR 537L /* the IP interrupt status register + did not reset when acked */ +#define XST_IPIF_IP_ENABLE_ERROR 538L /* IP interrupt enable register was + not updated correctly when other + registers changed */ +#define XST_IPIF_DEVICE_PENDING_ERROR 539L /* The device interrupt pending + register did not indicate the + expected value */ +#define XST_IPIF_DEVICE_ID_ERROR 540L /* The device interrupt ID register + did not indicate the expected + value */ + +/****************** Device specific statuses 1001 - 4095 *********************/ + +/********************* Ethernet statuses 1001 - 1050 *************************/ + +#define XST_EMAC_MEMORY_SIZE_ERROR 1001L /* Memory space is not big enough + * to hold the minimum number of + * buffers or descriptors */ +#define XST_EMAC_MEMORY_ALLOC_ERROR 1002L /* Memory allocation failed */ +#define XST_EMAC_MII_READ_ERROR 1003L /* MII read error */ +#define XST_EMAC_MII_BUSY 1004L /* An MII operation is in progress */ +#define XST_EMAC_OUT_OF_BUFFERS 1005L /* Adapter is out of buffers */ +#define XST_EMAC_PARSE_ERROR 1006L /* Invalid adapter init string */ +#define XST_EMAC_COLLISION_ERROR 1007L /* Excess deferral or late + * collision on polled send */ + +/*********************** UART statuses 1051 - 1075 ***************************/ +#define XST_UART + +#define XST_UART_INIT_ERROR 1051L +#define XST_UART_START_ERROR 1052L +#define XST_UART_CONFIG_ERROR 1053L +#define XST_UART_TEST_FAIL 1054L +#define XST_UART_BAUD_ERROR 1055L +#define XST_UART_BAUD_RANGE 1056L + +/************************ IIC statuses 1076 - 1100 ***************************/ + +#define XST_IIC_SELFTEST_FAILED 1076 /* self test failed */ +#define XST_IIC_BUS_BUSY 1077 /* bus found busy */ +#define XST_IIC_GENERAL_CALL_ADDRESS 1078 /* mastersend attempted with */ + /* general call address */ +#define XST_IIC_STAND_REG_RESET_ERROR 1079 /* A non parameterizable reg */ + /* value after reset not valid */ +#define XST_IIC_TX_FIFO_REG_RESET_ERROR 1080 /* Tx fifo included in design */ + /* value after reset not valid */ +#define XST_IIC_RX_FIFO_REG_RESET_ERROR 1081 /* Rx fifo included in design */ + /* value after reset not valid */ +#define XST_IIC_TBA_REG_RESET_ERROR 1082 /* 10 bit addr incl in design */ + /* value after reset not valid */ +#define XST_IIC_CR_READBACK_ERROR 1083 /* Read of the control register */ + /* didn't return value written */ +#define XST_IIC_DTR_READBACK_ERROR 1084 /* Read of the data Tx reg */ + /* didn't return value written */ +#define XST_IIC_DRR_READBACK_ERROR 1085 /* Read of the data Receive reg */ + /* didn't return value written */ +#define XST_IIC_ADR_READBACK_ERROR 1086 /* Read of the data Tx reg */ + /* didn't return value written */ +#define XST_IIC_TBA_READBACK_ERROR 1087 /* Read of the 10 bit addr reg */ + /* didn't return written value */ +#define XST_IIC_NOT_SLAVE 1088 /* The device isn't a slave */ + +/*********************** ATMC statuses 1101 - 1125 ***************************/ + +#define XST_ATMC_ERROR_COUNT_MAX 1101L /* the error counters in the ATM + controller hit the max value + which requires the statistics + to be cleared */ + +/*********************** Flash statuses 1126 - 1150 **************************/ + +#define XST_FLASH_BUSY 1126L /* Flash is erasing or programming */ +#define XST_FLASH_READY 1127L /* Flash is ready for commands */ +#define XST_FLASH_ERROR 1128L /* Flash had detected an internal + error. Use XFlash_DeviceControl + to retrieve device specific codes */ +#define XST_FLASH_ERASE_SUSPENDED 1129L /* Flash is in suspended erase state */ +#define XST_FLASH_WRITE_SUSPENDED 1130L /* Flash is in suspended write state */ +#define XST_FLASH_PART_NOT_SUPPORTED 1131L /* Flash type not supported by + driver */ +#define XST_FLASH_NOT_SUPPORTED 1132L /* Operation not supported */ +#define XST_FLASH_TOO_MANY_REGIONS 1133L /* Too many erase regions */ +#define XST_FLASH_TIMEOUT_ERROR 1134L /* Programming or erase operation + aborted due to a timeout */ +#define XST_FLASH_ADDRESS_ERROR 1135L /* Accessed flash outside its + addressible range */ +#define XST_FLASH_ALIGNMENT_ERROR 1136L /* Write alignment error */ +#define XST_FLASH_BLOCKING_CALL_ERROR 1137L /* Couldn't return immediately from + write/erase function with + XFL_NON_BLOCKING_WRITE/ERASE + option cleared */ +#define XST_FLASH_CFI_QUERY_ERROR 1138L /* Failed to query the device */ + +/*********************** SPI statuses 1151 - 1175 ****************************/ + +#define XST_SPI_MODE_FAULT 1151 /* master was selected as slave */ +#define XST_SPI_TRANSFER_DONE 1152 /* data transfer is complete */ +#define XST_SPI_TRANSMIT_UNDERRUN 1153 /* slave underruns transmit register */ +#define XST_SPI_RECEIVE_OVERRUN 1154 /* device overruns receive register */ +#define XST_SPI_NO_SLAVE 1155 /* no slave has been selected yet */ +#define XST_SPI_TOO_MANY_SLAVES 1156 /* more than one slave is being + * selected */ +#define XST_SPI_NOT_MASTER 1157 /* operation is valid only as master */ +#define XST_SPI_SLAVE_ONLY 1158 /* device is configured as slave-only */ +#define XST_SPI_SLAVE_MODE_FAULT 1159 /* slave was selected while disabled */ + +/********************** OPB Arbiter statuses 1176 - 1200 *********************/ + +#define XST_OPBARB_INVALID_PRIORITY 1176 /* the priority registers have either + * one master assigned to two or more + * priorities, or one master not + * assigned to any priority + */ +#define XST_OPBARB_NOT_SUSPENDED 1177 /* an attempt was made to modify the + * priority levels without first + * suspending the use of priority + * levels + */ +#define XST_OPBARB_PARK_NOT_ENABLED 1178 /* bus parking by id was enabled but + * bus parking was not enabled + */ +#define XST_OPBARB_NOT_FIXED_PRIORITY 1179 /* the arbiter must be in fixed + * priority mode to allow the + * priorities to be changed + */ + +/************************ Intc statuses 1201 - 1225 **************************/ + +#define XST_INTC_FAIL_SELFTEST 1201 /* self test failed */ +#define XST_INTC_CONNECT_ERROR 1202 /* interrupt already in use */ + +/********************** TmrCtr statuses 1226 - 1250 **************************/ + +#define XST_TMRCTR_TIMER_FAILED 1226 /* self test failed */ + +/********************** WdtTb statuses 1251 - 1275 ***************************/ + +#define XST_WDTTB_TIMER_FAILED 1251L + +/********************** PlbArb statuses 1276 - 1300 **************************/ + +#define XST_PLBARB_FAIL_SELFTEST 1276L + +/********************** Plb2Opb statuses 1301 - 1325 *************************/ + +#define XST_PLB2OPB_FAIL_SELFTEST 1301L + +/********************** Opb2Plb statuses 1326 - 1350 *************************/ + +#define XST_OPB2PLB_FAIL_SELFTEST 1326L + +/********************** SysAce statuses 1351 - 1360 **************************/ + +#define XST_SYSACE_NO_LOCK 1351L /* No MPU lock has been granted */ + +/********************** PCI Bridge statuses 1361 - 1375 **********************/ + +#define XST_PCI_INVALID_ADDRESS 1361L + +/**************************** Type Definitions *******************************/ + +/** + * The status typedef. + */ +typedef u32 XStatus; + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xversion.c linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xversion.c --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xversion.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xversion.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,351 @@ +/* $Id: xversion.c,v 1.5 2002/03/15 22:48:59 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/***************************************************************************** +* +* This file contains the implementation of the XVersion component. This +* component represents a version ID. It is encapsulated within a component +* so that it's type and implementation can change without affecting users of +* it. +* +* The version is formatted as X.YYZ where X = 0 - 9, Y = 00 - 99, Z = a - z +* X is the major revision, YY is the minor revision, and Z is the +* compatability revision. +* +* Packed versions are also utilized for the configuration ROM such that +* memory is minimized. A packed version consumes only 16 bits and is +* formatted as follows. +* +*
+* Revision                  Range       Bit Positions
+*
+* Major Revision            0 - 9       Bits 15 - 12
+* Minor Revision            0 - 99      Bits 11 - 5
+* Compatability Revision    a - z       Bits 4 - 0
+
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xversion.h" + +/************************** Constant Definitions *****************************/ + +/* the following constants define the masks and shift values to allow the + * revisions to be packed and unpacked, a packed version is packed into a 16 + * bit value in the following format, XXXXYYYYYYYZZZZZ, where XXXX is the + * major revision, YYYYYYY is the minor revision, and ZZZZZ is the compatability + * revision + */ +#define XVE_MAJOR_SHIFT_VALUE 12 +#define XVE_MINOR_ONLY_MASK 0x0FE0 +#define XVE_MINOR_SHIFT_VALUE 5 +#define XVE_COMP_ONLY_MASK 0x001F + +/* the following constants define the specific characters of a version string + * for each character of the revision, a version string is in the following + * format, "X.YYZ" where X is the major revision (0 - 9), YY is the minor + * revision (00 - 99), and Z is the compatability revision (a - z) + */ +#define XVE_MAJOR_CHAR 0 /* major revision 0 - 9 */ +#define XVE_MINOR_TENS_CHAR 2 /* minor revision tens 0 - 9 */ +#define XVE_MINOR_ONES_CHAR 3 /* minor revision ones 0 - 9 */ +#define XVE_COMP_CHAR 4 /* compatability revision a - z */ +#define XVE_END_STRING_CHAR 5 + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +static u32 IsVersionStringValid(s8 * StringPtr); + +/***************************************************************************** +* +* Unpacks a packed version into the specified version. Versions are packed +* into the configuration ROM to reduce the amount storage. A packed version +* is a binary format as oppossed to a non-packed version which is implemented +* as a string. +* +* @param InstancePtr points to the version to unpack the packed version into. +* @param PackedVersion contains the packed version to unpack. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XVersion_UnPack(XVersion * InstancePtr, u16 PackedVersion) +{ + /* not implemented yet since CROM related */ +} + +/***************************************************************************** +* +* Packs a version into the specified packed version. Versions are packed into +* the configuration ROM to reduce the amount storage. +* +* @param InstancePtr points to the version to pack. +* @param PackedVersionPtr points to the packed version which will receive +* the new packed version. +* +* @return +* +* A status, XST_SUCCESS, indicating the packing was accomplished +* successfully, or an error, XST_INVALID_VERSION, indicating the specified +* input version was not valid such that the pack did not occur +*

+* The packed version pointed to by PackedVersionPtr is modified with the new +* packed version if the status indicates success. +* +* @note +* +* None. +* +******************************************************************************/ +XStatus +XVersion_Pack(XVersion * InstancePtr, u16 * PackedVersionPtr) +{ + /* not implemented yet since CROM related */ + + return XST_SUCCESS; +} + +/***************************************************************************** +* +* Determines if two versions are equal. +* +* @param InstancePtr points to the first version to be compared. +* @param VersionPtr points to a second version to be compared. +* +* @return +* +* TRUE if the versions are equal, FALSE otherwise. +* +* @note +* +* None. +* +******************************************************************************/ +u32 +XVersion_IsEqual(XVersion * InstancePtr, XVersion * VersionPtr) +{ + u8 *Version1 = (u8 *) InstancePtr; + u8 *Version2 = (u8 *) VersionPtr; + int Index; + + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(VersionPtr != NULL); + + /* check each byte of the versions to see if they are the same, + * return at any point a byte differs between them + */ + for (Index = 0; Index < sizeof (XVersion); Index++) { + if (Version1[Index] != Version2[Index]) { + return FALSE; + } + } + + /* No byte was found to be different between the versions, so indicate + * the versions are equal + */ + return TRUE; +} + +/***************************************************************************** +* +* Converts a version to a null terminated string. +* +* @param InstancePtr points to the version to convert. +* @param StringPtr points to the string which will be the result of the +* conversion. This does not need to point to a null terminated +* string as an input, but must point to storage which is an adequate +* amount to hold the result string. +* +* @return +* +* The null terminated string is inserted at the location pointed to by +* StringPtr if the status indicates success. +* +* @note +* +* It is necessary for the caller to have already allocated the storage to +* contain the string. The amount of memory necessary for the string is +* specified in the version header file. +* +******************************************************************************/ +void +XVersion_ToString(XVersion * InstancePtr, s8 * StringPtr) +{ + /* assert to verify input arguments */ + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(StringPtr != NULL); + + /* since version is implemented as a string, just copy the specified + * input into the specified output + */ + XVersion_Copy(InstancePtr, (XVersion *) StringPtr); +} + +/***************************************************************************** +* +* Initializes a version from a null terminated string. Since the string may not +* be a format which is compatible with the version, an error could occur. +* +* @param InstancePtr points to the version which is to be initialized. +* @param StringPtr points to a null terminated string which will be +* converted to a version. The format of the string must match the +* version string format which is X.YYX where X = 0 - 9, YY = 00 - 99, +* Z = a - z. +* +* @return +* +* A status, XST_SUCCESS, indicating the conversion was accomplished +* successfully, or XST_INVALID_VERSION indicating the version string format +* was not valid. +* +* @note +* +* None. +* +******************************************************************************/ +XStatus +XVersion_FromString(XVersion * InstancePtr, s8 * StringPtr) +{ + /* assert to verify input arguments */ + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(StringPtr != NULL); + + /* if the version string specified is not valid, return an error */ + + if (!IsVersionStringValid(StringPtr)) { + return XST_INVALID_VERSION; + } + + /* copy the specified string into the specified version and indicate the + * conversion was successful + */ + XVersion_Copy((XVersion *) StringPtr, InstancePtr); + + return XST_SUCCESS; +} + +/***************************************************************************** +* +* Copies the contents of a version to another version. +* +* @param InstancePtr points to the version which is the source of data for +* the copy operation. +* @param VersionPtr points to another version which is the destination of +* the copy operation. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XVersion_Copy(XVersion * InstancePtr, XVersion * VersionPtr) +{ + u8 *Source = (u8 *) InstancePtr; + u8 *Destination = (u8 *) VersionPtr; + int Index; + + /* assert to verify input arguments */ + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(VersionPtr != NULL); + + /* copy each byte of the source version to the destination version */ + + for (Index = 0; Index < sizeof (XVersion); Index++) { + Destination[Index] = Source[Index]; + } +} + +/***************************************************************************** +* +* Determines if the specified version is valid. +* +* @param StringPtr points to the string to be validated. +* +* @return +* +* TRUE if the version string is a valid format, FALSE otherwise. +* +* @note +* +* None. +* +******************************************************************************/ +static u32 +IsVersionStringValid(s8 * StringPtr) +{ + /* if the input string is not a valid format, "X.YYZ" where X = 0 - 9, + * YY = 00 - 99, and Z = a - z, then indicate it's not valid + */ + if ((StringPtr[XVE_MAJOR_CHAR] < '0') || + (StringPtr[XVE_MAJOR_CHAR] > '9') || + (StringPtr[XVE_MINOR_TENS_CHAR] < '0') || + (StringPtr[XVE_MINOR_TENS_CHAR] > '9') || + (StringPtr[XVE_MINOR_ONES_CHAR] < '0') || + (StringPtr[XVE_MINOR_ONES_CHAR] > '9') || + (StringPtr[XVE_COMP_CHAR] < 'a') || + (StringPtr[XVE_COMP_CHAR] > 'z')) { + return FALSE; + } + + return TRUE; +} diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xversion.h linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xversion.h --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp/xversion.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp/xversion.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,98 @@ +/* $Id: xversion.h,v 1.4 2002/03/15 22:48:59 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/***************************************************************************** +* +* This file contains the interface for the XVersion component. This +* component represents a version ID. It is encapsulated within a component +* so that it's type and implementation can change without affecting users of +* it. +* +* The version is formatted as X.YYZ where X = 0 - 9, Y = 00 - 99, Z = a - z +* X is the major revision, YY is the minor revision, and Z is the +* compatability revision. +* +* Packed versions are also utilized for the configuration ROM such that +* memory is minimized. A packed version consumes only 16 bits and is +* formatted as follows. +* +*
+* Revision                  Range       Bit Positions
+*
+* Major Revision            0 - 9       Bits 15 - 12
+* Minor Revision            0 - 99      Bits 11 - 5
+* Compatability Revision    a - z       Bits 4 - 0
+* 
+* +******************************************************************************/ + +#ifndef XVERSION_H /* prevent circular inclusions */ +#define XVERSION_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xstatus.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/* the following data type is used to hold a null terminated version string + * consisting of the following format, "X.YYX" + */ +typedef s8 XVersion[6]; + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +void XVersion_UnPack(XVersion * InstancePtr, u16 PackedVersion); + +XStatus XVersion_Pack(XVersion * InstancePtr, u16 * PackedVersion); + +u32 XVersion_IsEqual(XVersion * InstancePtr, XVersion * VersionPtr); + +void XVersion_ToString(XVersion * InstancePtr, s8 * StringPtr); + +XStatus XVersion_FromString(XVersion * InstancePtr, s8 * StringPtr); + +void XVersion_Copy(XVersion * InstancePtr, XVersion * VersionPtr); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/arch/ppc/platforms/xilinx_ocp.h linuxppc-2.4/arch/ppc/platforms/xilinx_ocp.h --- linux-2.4.22/arch/ppc/platforms/xilinx_ocp.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/arch/ppc/platforms/xilinx_ocp.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,23 @@ +/* + * xilinx_ocp.h + * + * Include file that defines the Xilinx On Chip Peripherals + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +#ifdef __KERNEL__ +#ifndef __ASM_XILINX_OCP_H__ +#define __ASM_XILINX_OCP_H__ + +#ifndef __ASSEMBLY__ +#include +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_XILINX_OCP_H__ */ +#endif /* __KERNEL__ */ diff -urN -X bkexcl linux-2.4.22/drivers/block/Config.in linuxppc-2.4/drivers/block/Config.in --- linux-2.4.22/drivers/block/Config.in 2002-08-10 16:13:57.000000000 +1000 +++ linuxppc-2.4/drivers/block/Config.in 2003-03-14 20:46:44.000000000 +1100 @@ -39,6 +39,10 @@ dep_tristate 'Mylex DAC960/DAC1100 PCI RAID Controller support' CONFIG_BLK_DEV_DAC960 $CONFIG_PCI dep_tristate 'Micro Memory MM5415 Battery Backed RAM support (EXPERIMENTAL)' CONFIG_BLK_DEV_UMEM $CONFIG_PCI $CONFIG_EXPERIMENTAL +if [ "$CONFIG_XILINX_OCP" = "y" ]; then + tristate 'Xilinx on-chip System ACE' CONFIG_XILINX_SYSACE +fi + tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET diff -urN -X bkexcl linux-2.4.22/drivers/block/Makefile linuxppc-2.4/drivers/block/Makefile --- linux-2.4.22/drivers/block/Makefile 2003-01-25 09:04:04.000000000 +1100 +++ linuxppc-2.4/drivers/block/Makefile 2003-03-14 20:46:44.000000000 +1100 @@ -33,5 +33,8 @@ obj-$(CONFIG_BLK_DEV_NBD) += nbd.o subdir-$(CONFIG_PARIDE) += paride +mod-subdirs += xilinx_sysace +subdir-$(CONFIG_XILINX_SYSACE) += xilinx_sysace +obj-$(CONFIG_XILINX_SYSACE) += xilinx_sysace/xilinx_sysace.o include $(TOPDIR)/Rules.make diff -urN -X bkexcl linux-2.4.22/drivers/block/xilinx_sysace/Makefile linuxppc-2.4/drivers/block/xilinx_sysace/Makefile --- linux-2.4.22/drivers/block/xilinx_sysace/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/block/xilinx_sysace/Makefile 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,21 @@ +# +# Makefile for the Xilinx System ACE driver +# + +EXTRA_CFLAGS += -I$(TOPDIR)/arch/ppc/platforms/xilinx_ocp + +list-multi := xilinx_sysace.o + +# The Linux adapter for the Xilinx driver code. +xilinx_sysace-objs += adapter.o + +# The Xilinx OS independent code. +xilinx_sysace-objs += xsysace.o xsysace_g.o xsysace_intr.o xsysace_l.o +xilinx_sysace-objs += xsysace_compactflash.o xsysace_jtagcfg.o + +obj-$(CONFIG_XILINX_SYSACE) := xilinx_sysace.o + +xilinx_sysace.o: $(xilinx_sysace-objs) + $(LD) -r -o $@ $(xilinx_sysace-objs) + +include $(TOPDIR)/Rules.make diff -urN -X bkexcl linux-2.4.22/drivers/block/xilinx_sysace/adapter.c linuxppc-2.4/drivers/block/xilinx_sysace/adapter.c --- linux-2.4.22/drivers/block/xilinx_sysace/adapter.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/block/xilinx_sysace/adapter.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,860 @@ +/* + * adapter.c + * + * Xilinx System ACE Adapter component to interface System ACE to Linux + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +/* + * Through System ACE, the processor can access the CompactFlash and the + * JTAG chain. In addition, the System ACE controls system reset and + * which configuration will be loaded into the JTAG chain at that time. + * This driver provides two different interfaces. The first is handling + * reset by tying into the system's reset code as well as providing a + * /proc interface to read and write which configuration should be used + * when the system is reset. The second is to expose a block interface + * to the CompactFlash. + * + * This driver is a bit unusual in that it is composed of two logical + * parts where one part is the OS independent code and the other part is + * the OS dependent code. Xilinx provides their drivers split in this + * fashion. This file represents the Linux OS dependent part known as + * the Linux adapter. The other files in this directory are the OS + * independent files as provided by Xilinx with no changes made to them. + * The names exported by those files begin with XSysAce_. All functions + * in this file that are called by Linux have names that begin with + * xsysace_. Any other functions are static helper functions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NR_HD 1 /* System ACE only handles one CompactFlash */ +#define PARTN_BITS 4 /* Only allow 15 partitions. */ + +/* Dynamically allocate a major number. */ +static int xsa_major = 0; +#define MAJOR_NR (xsa_major) +#define MAJOR_NAME "xsysace" + +#define DEVICE_NAME "System ACE" +#define DEVICE_REQUEST xsysace_do_request +#define DEVICE_NR(device) (MINOR(device) >> PARTN_BITS) +#include +#include + +#include +#include "xsysace.h" + +MODULE_AUTHOR("MontaVista Software, Inc. "); +MODULE_DESCRIPTION("Xilinx System ACE block driver"); +MODULE_LICENSE("GPL"); + +/* + * We have to wait for a lock and for the CompactFlash to not be busy + * via polling. If dont_spin is non-zero, we will use schedule_timeout + * in a loop to check for these conditions. If dont_spin is zero, we + * will tight loop without a schedule_timeout as long as need_resched is + * false. If need_resched is true, we'll fall back to using + * schedule_timeout. This could obviously be made run-time settable. + */ +static const int dont_spin = 0; + +static u32 save_BaseAddress; /* Saved physical base address */ +static void (*old_restart) (char *cmd) = NULL; /* old ppc_md.restart */ + +static unsigned char heads; +static unsigned char sectors; +static unsigned short cylinders; + +static int access_count = 0; +static char revalidating = 0; +static DECLARE_WAIT_QUEUE_HEAD(revalidate_wait); + +/* + * The following variables are used to keep track of what all has been + * done to make error handling easier. + */ +static char reqirq = 0; /* Has request_irq() been called? */ +static char registered = 0; /* Has devfs_register_blkdev() been called? */ + +/* + * The underlying OS independent code needs space as well. A pointer to + * the following XSysAce structure will be passed to any XSysAce_ + * function that requires it. However, we treat the data as an opaque + * object in this file (meaning that we never reference any of the + * fields inside of the structure). + */ +static XSysAce SysAce; + +/* These tables are indexed by major and minor numbers. */ +static int xsa_sizes[NR_HD << PARTN_BITS]; /* Size of the device, kb */ +static int xsa_blocksizes[NR_HD << PARTN_BITS]; /* Block size, bytes */ +static int xsa_hardsectsizes[NR_HD << PARTN_BITS]; /* Sector size, bytes */ +static int xsa_maxsect[NR_HD << PARTN_BITS]; /* Max request size, sectors */ +static struct hd_struct xsa_hd[NR_HD << PARTN_BITS]; /* Partition Table */ + +/* + * In general, requests enter this driver through xsysace_do_request() + * which then schedules the xsa_thread. The xsa_thread sends the + * request down to the Xilinx OS independent layer. When the request + * completes, EventHandler() will get called as a result of an + * interrupt. The following variables support this flow. + */ +static struct task_struct *xsa_task = NULL; /* xsa_thread pointer */ +static struct completion task_sync; /* xsa_thread start/stop syncing */ +static char task_shutdown = 0; /* Set to non-zero when task should quit. */ +/* + * req_fnc will be either NULL or a pointer to XSysAce_SectorRead or + * XSysAce_SectorWrite. It will be set to point to one of the XSysAce* + * functions by xsysace_do_request and then xsa_thread will be scheduled. + * The xsa_thread will then NULL req_fnc after it has copied the pointer. + */ +static XStatus(*req_fnc) (XSysAce * InstancePtr, u32 StartSector, + int NumSectors, u8 * BufferPtr); +/* xsa_thread waits on req_wait until xsysace_do_request does wake_up on it */ +static DECLARE_WAIT_QUEUE_HEAD(req_wait); +/* req_active is a simple flag that says a request is being serviced. */ +static char req_active = 0; +/* req_str will be used for errors and will be either "reading" or "writing" */ +static char *req_str; + + +/* SAATODO: Xilinx is going to add this function. Nuke when they do. */ +unsigned int +XSysAce_GetCfgAddr(XSysAce * InstancePtr) +{ + u32 Status; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + Status = XSysAce_mGetControlReg(InstancePtr->BaseAddress); + if (!(Status & XSA_CR_FORCECFGADDR_MASK)) + Status = XSysAce_mGetStatusReg(InstancePtr->BaseAddress); + + return (unsigned int) ((Status & XSA_SR_CFGADDR_MASK) >> + XSA_CR_CFGADDR_SHIFT); +} + +/* SAATODO: Nuke the following line when Config stuff moved out. */ +extern XSysAce_Config XSysAce_ConfigTable[]; +/* SAATODO: This function will be moved into the Xilinx code. */ +/*****************************************************************************/ +/** +* +* Lookup the device configuration based on the sysace instance. The table +* XSysAce_ConfigTable contains the configuration info for each device in the system. +* +* @param Instance is the index of the interface being looked up. +* +* @return +* +* A pointer to the configuration table entry corresponding to the given +* device ID, or NULL if no match is found. +* +* @note +* +* None. +* +******************************************************************************/ +XSysAce_Config * +XSysAce_GetConfig(int Instance) +{ + if (Instance < 0 || Instance >= XPAR_XSYSACE_NUM_INSTANCES) { + return NULL; + } + + return &XSysAce_ConfigTable[Instance]; +} + +/* + * The following block of code implements the reset handling. The first + * part implements /proc/xsysace/cfgaddr. When read, it will yield a + * number from 0 to 7 that represents which configuration will be used + * next (the configuration address). Writing a number to it will change + * the configuration address. After that is the function that is hooked + * into the system's reset handler. + */ +#ifndef CONFIG_PROC_FS +#define proc_init() 0 +#define proc_cleanup() +#else +#define CFGADDR_NAME "cfgaddr" +static struct proc_dir_entry *xsysace_dir = NULL; +static struct proc_dir_entry *cfgaddr_file = NULL; + +static int +cfgaddr_read(char *page, char **start, + off_t off, int count, int *eof, void *data) +{ + unsigned int cfgaddr; + + /* Make sure we have room for a digit (0-7), a newline and a NULL */ + if (count < 3) + return -EINVAL; + + MOD_INC_USE_COUNT; + + cfgaddr = XSysAce_GetCfgAddr(&SysAce); + + count = sprintf(page + off, "%d\n", cfgaddr); + *eof = 1; + + MOD_DEC_USE_COUNT; + + return count; +} + +static int +cfgaddr_write(struct file *file, + const char *buffer, unsigned long count, void *data) +{ + char val[2]; + + if (count < 1 || count > 2) + return -EINVAL; + + MOD_INC_USE_COUNT; + + if (copy_from_user(val, buffer, count)) { + MOD_DEC_USE_COUNT; + return -EFAULT; + } + + if (val[0] < '0' || val[0] > '7' || (count == 2 && !(val[1] == '\n' || + val[1] == '\0'))) { + MOD_DEC_USE_COUNT; + return -EINVAL; + } + + XSysAce_SetCfgAddr(&SysAce, val[0] - '0'); + + MOD_DEC_USE_COUNT; + + return count; +} + +static int +proc_init(void) +{ + xsysace_dir = proc_mkdir(MAJOR_NAME, NULL); + if (!xsysace_dir) + return -ENOMEM; + xsysace_dir->owner = THIS_MODULE; + + cfgaddr_file = create_proc_entry(CFGADDR_NAME, 0644, xsysace_dir); + if (!cfgaddr_file) { + remove_proc_entry(MAJOR_NAME, NULL); + return -ENOMEM; + } + cfgaddr_file->read_proc = cfgaddr_read; + cfgaddr_file->write_proc = cfgaddr_write; + cfgaddr_file->owner = THIS_MODULE; + return 0; +} + +static void +proc_cleanup(void) +{ + if (cfgaddr_file) + remove_proc_entry(CFGADDR_NAME, xsysace_dir); + if (xsysace_dir) + remove_proc_entry(MAJOR_NAME, NULL); +} +#endif + +static void +xsysace_restart(char *cmd) +{ + XSysAce_ResetCfg(&SysAce); + + /* Wait for reset. */ + for (;;) ; +} + +/* + * The code to handle the block device starts here. + */ + +/* + * This is just a small helper function to clean up a request that has + * been given to the xsa_thread. + */ +static void +xsa_complete_request(int uptodate) +{ + unsigned long flags; + + XSysAce_Unlock(&SysAce); + spin_lock_irqsave(&io_request_lock, flags); + end_request(uptodate); + req_active = 0; + /* If there's something in the queue, */ + if (!QUEUE_EMPTY) + xsysace_do_request(NULL); /* handle it. */ + spin_unlock_irqrestore(&io_request_lock, flags); +} + +/* Small helper function used inside polling loops. */ +static inline void +xsa_short_delay(void) +{ + /* If someone else needs the CPU, go to sleep. */ + if (dont_spin || current->need_resched) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 100); + } +} + +/* Simple function that hands an interrupt to the Xilinx code. */ +static void +xsysace_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + XSysAce_InterruptHandler(&SysAce); +} + +/* Called by the Xilinx interrupt handler to give us an event. */ +static void +EventHandler(void *CallbackRef, int Event) +{ + u32 ErrorMask; + + switch (Event) { + case XSA_EVENT_DATA_DONE: + xsa_complete_request(1); /* The request succeeded. */ + break; + + case XSA_EVENT_ERROR: + ErrorMask = XSysAce_GetErrors(&SysAce); + + /* Print out what went wrong. */ + if (ErrorMask & XSA_ER_CARD_RESET) + printk(KERN_ERR "CompactFlash failed to reset\n"); + if (ErrorMask & XSA_ER_CARD_READY) + printk(KERN_ERR "CompactFlash failed to ready\n"); + if (ErrorMask & XSA_ER_CARD_READ) + printk(KERN_ERR "CompactFlash read command failed\n"); + if (ErrorMask & XSA_ER_CARD_WRITE) + printk(KERN_ERR "CompactFlash write command failed\n"); + if (ErrorMask & XSA_ER_SECTOR_READY) + printk(KERN_ERR + "CompactFlash sector failed to ready\n"); + if (ErrorMask & XSA_ER_BAD_BLOCK) + printk(KERN_ERR "CompactFlash bad block detected\n"); + if (ErrorMask & XSA_ER_UNCORRECTABLE) + printk(KERN_ERR "CompactFlash uncorrectable error\n"); + if (ErrorMask & XSA_ER_SECTOR_ID) + printk(KERN_ERR "CompactFlash sector ID not found\n"); + if (ErrorMask & XSA_ER_ABORT) + printk(KERN_ERR "CompactFlash command aborted\n"); + if (ErrorMask & XSA_ER_GENERAL) + printk(KERN_ERR "CompactFlash general error\n"); + + if (ErrorMask & XSA_ER_CFG_READ) + printk(KERN_ERR + "JTAG controller couldn't read configuration from the CompactFlash\n"); + if (ErrorMask & XSA_ER_CFG_ADDR) + printk(KERN_ERR + "Invalid address given to JTAG controller\n"); + if (ErrorMask & XSA_ER_CFG_FAIL) + printk(KERN_ERR + "JTAG controller failed to configure a device\n"); + if (ErrorMask & XSA_ER_CFG_INSTR) + printk(KERN_ERR + "Invalid instruction during JTAG configuration\n"); + if (ErrorMask & XSA_ER_CFG_INIT) + printk(KERN_ERR "JTAG CFGINIT pin error\n"); + + /* Check for errors that should reset the CompactFlash */ + if (ErrorMask & (XSA_ER_CARD_RESET | + XSA_ER_CARD_READY | + XSA_ER_CARD_READ | + XSA_ER_CARD_WRITE | + XSA_ER_SECTOR_READY | + XSA_ER_BAD_BLOCK | + XSA_ER_UNCORRECTABLE | + XSA_ER_SECTOR_ID | XSA_ER_ABORT | + XSA_ER_GENERAL)) { + if (XSysAce_ResetCF(&SysAce) != XST_SUCCESS) + printk(KERN_ERR + "Could not reset CompactFlash\n"); + xsa_complete_request(0); /* The request failed. */ + } + break; + case XSA_EVENT_CFG_DONE: + printk(KERN_WARNING "XSA_EVENT_CFG_DONE not handled yet.\n"); + break; + default: + printk(KERN_ERR "%s: unrecognized event %d\n", + DEVICE_NAME, Event); + break; + } +} + +/* + * This task takes care of sending requests down to the Xilinx OS independent + * code. A task is necessary because there isn't an interrupt telling us + * when the hardware is ready to accept another request, so we have to poll. + */ +static int +xsa_thread(void *arg) +{ + XStatus stat; + unsigned long sector; + + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + xsa_task = tsk; + + daemonize(); + reparent_to_init(); + strcpy(xsa_task->comm, MAJOR_NAME); + xsa_task->tty = NULL; + + /* only want to receive SIGKILL */ + spin_lock_irq(&xsa_task->sigmask_lock); + siginitsetinv(&xsa_task->blocked, sigmask(SIGKILL)); + recalc_sigpending(xsa_task); + spin_unlock_irq(&xsa_task->sigmask_lock); + + complete(&task_sync); + + add_wait_queue(&req_wait, &wait); + for (;;) { + XStatus(*cur_req) (XSysAce * InstancePtr, u32 StartSector, + int NumSectors, u8 * BufferPtr); + + /* Block waiting for request */ + for (;;) { + set_task_state(tsk, TASK_INTERRUPTIBLE); + cur_req = req_fnc; + if (cur_req != NULL) { + req_fnc = NULL; + break; /* We've got a request. */ + } + while (signal_pending(tsk)) { + siginfo_t info; + + /* Only honor the signal if we're cleaning up */ + if (task_shutdown) + goto exit; + /* + * Someone else sent us a kill (probably + * the shutdown scripts "Sending all + * processes the KILL signal"). Just + * dequeue it and ignore it. + */ + spin_lock_irq(¤t->sigmask_lock); + (void)dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + } + schedule(); + } + set_task_state(tsk, TASK_RUNNING); + + /* We have a request. */ + sector = (CURRENT->sector + + xsa_hd[MINOR(CURRENT->rq_dev)].start_sect); + + while ((stat = XSysAce_Lock(&SysAce, 0)) == XST_DEVICE_BUSY) + xsa_short_delay(); + if (stat != XST_SUCCESS) { + printk(KERN_ERR "%s: Error %d when locking.\n", + DEVICE_NAME, stat); + xsa_complete_request(0); /* Request failed. */ + } + + while ((stat = cur_req(&SysAce, sector, + CURRENT->current_nr_sectors, + CURRENT->buffer)) == XST_DEVICE_BUSY) + xsa_short_delay(); + /* + * If the stat is XST_SUCCESS, we have successfully + * gotten the request started on the hardware. The + * completion (or error) interrupt will unlock the + * CompactFlash and complete the request, so we don't + * need to do anything except just loop around and wait + * for the next request. If the status is not + * XST_SUCCESS, we need to finish the request with an + * error before waiting for the next request. + */ + if (stat != XST_SUCCESS) { + printk(KERN_ERR "%s: Error %d when %s sector %lu.\n", + DEVICE_NAME, stat, req_str, sector); + xsa_complete_request(0); /* Request failed. */ + } + } + + exit: + remove_wait_queue(&req_wait, &wait); + + xsa_task = NULL; + complete_and_exit(&task_sync, 0); +} + +static void +xsysace_do_request(request_queue_t * q) +{ + /* We're already handling a request. Don't accept another. */ + if (req_active) + return; + + for (;;) { + INIT_REQUEST; + + switch (CURRENT->cmd) { + case READ: + req_str = "reading"; + req_fnc = XSysAce_SectorRead; + break; + case WRITE: + req_str = "writing"; + req_fnc = XSysAce_SectorWrite; + break; + default: + printk(KERN_CRIT "%s: unknown request %d.\n", + DEVICE_NAME, CURRENT->cmd); + end_request(0); /* Bad request. End it and onwards. */ + continue; /* Go on to the next request. */ + } + + req_active = 1; + wake_up(&req_wait); /* Schedule task to take care of it */ + /* + * It is up to the task or interrupt handler to complete + * the request. We return now so the io_request_lock + * will get cleared and requests can be queued. + */ + return; + } +} + +extern struct gendisk xsa_gendisk; +static int +xsa_revalidate(kdev_t kdev, int max_access) +{ + int target, max_p, start, i; + long flags; + + target = DEVICE_NR(kdev); + + save_flags(flags); + cli(); + if (revalidating || access_count > max_access) { + restore_flags(flags); + return -EBUSY; + } + revalidating = 1; + restore_flags(flags); + + max_p = xsa_gendisk.max_p; + start = target << PARTN_BITS; + for (i = max_p - 1; i >= 0; i--) { + int minor = start + i; + invalidate_device(MKDEV(MAJOR_NR, minor), 1); + xsa_gendisk.part[minor].start_sect = 0; + xsa_gendisk.part[minor].nr_sects = 0; + } + + grok_partitions(&xsa_gendisk, target, NR_HD << PARTN_BITS, + (long) cylinders * (long) heads * (long) sectors); + revalidating = 0; + wake_up(&revalidate_wait); + return 0; +} + +/* + * For right now, there isn't a way to tell the hardware to spin down + * the drive. For IBM Microdrives, which is what the ML300 ships with, + * removing them without a spindown is probably not very good. Thus, we + * don't even want to have the System ACE driver handle removable media. + * For now, placeholder code has been surrounded by ifdef XSA_REMOVABLE + * (which is not defined) in a few places in this file. Something that + * is not handled in the placeholder code is identifying the new drive. + */ +#ifdef XSA_REMOVABLE +static int +xsysace_revalidate(kdev_t kdev) +{ + return xsa_revalidate(kdev, 0); +} + +static int +xsysace_check_change(kdev_t kdev) +{ + /* + * If and when we do support removable media, we'll need to + * figure out how to tell if the media was changed. For now, + * just always say that the media was changed, which is safe, + * but not optimal. + */ + return 1; +} +#endif + +static int +xsysace_open(struct inode *inode, struct file *file) +{ + wait_event(revalidate_wait, !revalidating); + access_count++; + + MOD_INC_USE_COUNT; + return 0; +} +static int +xsysace_release(struct inode *inode, struct file *file) +{ + access_count--; + + MOD_DEC_USE_COUNT; + return 0; +} +static int +xsysace_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + if (!inode || !inode->i_rdev) + return -EINVAL; + + switch (cmd) { + case BLKGETSIZE: + return put_user(xsa_hd[MINOR(inode->i_rdev)].nr_sects, + (unsigned long *) arg); + case BLKGETSIZE64: + return put_user((u64) xsa_hd[MINOR(inode->i_rdev)]. + nr_sects, (u64 *) arg); + case BLKRRPART: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + return xsa_revalidate(inode->i_rdev, 1); + + case HDIO_GETGEO: + { + struct hd_geometry *loc, g; + loc = (struct hd_geometry *) arg; + if (!loc) + return -EINVAL; + g.heads = heads; + g.sectors = sectors; + g.cylinders = cylinders; + g.start = xsa_hd[MINOR(inode->i_rdev)].start_sect; + return copy_to_user(loc, &g, sizeof(g)) ? -EFAULT : 0; + } + default: + /* Let the block layer handle all the others. */ + return blk_ioctl(inode->i_rdev, cmd, arg); + } +} + +static struct block_device_operations xsa_fops = { + open:xsysace_open, + release:xsysace_release, + ioctl:xsysace_ioctl, +#ifdef XSA_REMOVABLE + check_media_change:xsysace_check_change, + revalidate:xsysace_revalidate +#endif +}; +static struct gendisk xsa_gendisk = { /* Generic Hard Disk */ + major_name:MAJOR_NAME, + minor_shift:PARTN_BITS, + max_p:NR_HD << PARTN_BITS, + part:xsa_hd, + sizes:xsa_sizes, + fops:&xsa_fops +}; + +#define XSA_IRQ (31 - XPAR_INTC_0_SYSACE_0_VEC_ID) + +static void +cleanup(void) +{ + XSysAce_Config *cfg; + int i; + + /* Make sure everything is flushed. */ + for (i = 0; i < (NR_HD << PARTN_BITS); i++) + fsync_dev(MKDEV(xsa_major, i)); + + proc_cleanup(); + + if (registered) { + blk_cleanup_queue(BLK_DEFAULT_QUEUE(xsa_major)); + del_gendisk(&xsa_gendisk); + if (devfs_unregister_blkdev(xsa_major, DEVICE_NAME) != 0) { + printk(KERN_ERR "%s: unable to release major %d\n", + DEVICE_NAME, xsa_major); + } + } + + /* Tidy up a little. */ + read_ahead[xsa_major] = 0; + blk_size[xsa_major] = NULL; + blksize_size[xsa_major] = NULL; + hardsect_size[xsa_major] = NULL; + max_sectors[xsa_major] = NULL; + + if (xsa_task) { + task_shutdown = 1; + send_sig(SIGKILL, xsa_task, 1); + wait_for_completion(&task_sync); + } + + if (reqirq) { + XSysAce_DisableInterrupt(&SysAce); + disable_irq(XSA_IRQ); + free_irq(XSA_IRQ, NULL); + } + + cfg = XSysAce_GetConfig(0); + iounmap((void *) cfg->BaseAddress); + cfg->BaseAddress = save_BaseAddress; + + if (old_restart) + ppc_md.restart = old_restart; +} + +static int __init +xsysace_init(void) +{ + static const unsigned long remap_size + = XPAR_SYSACE_0_HIGHADDR - XPAR_SYSACE_0_BASEADDR + 1; + XSysAce_Config *cfg; + XSysAce_CFParameters ident; + XStatus stat; + long size; + int i; + + /* Find the config for our device. */ + cfg = XSysAce_GetConfig(0); + if (!cfg) + return -ENODEV; + + /* Change the addresses to be virtual; save the old ones to restore. */ + save_BaseAddress = cfg->BaseAddress; + cfg->BaseAddress = (u32) ioremap(save_BaseAddress, remap_size); + + /* Tell the Xilinx code to bring this interface up. */ + if (XSysAce_Initialize(&SysAce, cfg->DeviceId) != XST_SUCCESS) { + printk(KERN_ERR "%s: Could not initialize device.\n", + DEVICE_NAME); + cleanup(); + return -ENODEV; + } + + i = request_irq(XSA_IRQ, xsysace_interrupt, 0, DEVICE_NAME, NULL); + if (i) { + printk(KERN_ERR "%s: Could not allocate interrupt %d.\n", + DEVICE_NAME, XSA_IRQ); + cleanup(); + return i; + } + reqirq = 1; + XSysAce_SetEventHandler(&SysAce, EventHandler, (void *) NULL); + XSysAce_EnableInterrupt(&SysAce); + + /* Time to identify the drive. */ + while (XSysAce_Lock(&SysAce, 0) == XST_DEVICE_BUSY) + xsa_short_delay(); + while ((stat = XSysAce_IdentifyCF(&SysAce, &ident)) == XST_DEVICE_BUSY) + xsa_short_delay(); + XSysAce_Unlock(&SysAce); + if (stat != XST_SUCCESS) { + printk(KERN_ERR "%s: Could not send identify command.\n", + DEVICE_NAME); + cleanup(); + return -ENODEV; + } + + /* Fill in what we learned. */ + heads = ident.NumHeads; + sectors = ident.NumSectorsPerTrack; + cylinders = ident.NumCylinders; + size = (long) cylinders * (long) heads * (long) sectors; + + /* Start up our task. */ + init_completion(&task_sync); + if ((i = kernel_thread(xsa_thread, 0, 0)) < 0) { + cleanup(); + return i; + } + wait_for_completion(&task_sync); + + i = devfs_register_blkdev(xsa_major, DEVICE_NAME, &xsa_fops); + if (i < 0) { + printk(KERN_ERR "%s: unable to register device\n", DEVICE_NAME); + cleanup(); + return i; + } + if (xsa_major == 0) + xsa_major = i; + registered = 1; + xsa_gendisk.major = xsa_major; + + blk_init_queue(BLK_DEFAULT_QUEUE(xsa_major), xsysace_do_request); + + read_ahead[xsa_major] = 8; /* 8 sector (4kB) read-ahead */ + add_gendisk(&xsa_gendisk); + + /* Start with zero-sized partitions, and correctly sized unit */ + memset(xsa_sizes, 0, sizeof(xsa_sizes)); + xsa_sizes[0] = size / 2; /* convert sectors to kilobytes */ + blk_size[xsa_major] = xsa_sizes; + memset(xsa_hd, 0, sizeof(xsa_hd)); + xsa_hd[0].nr_sects = size; + for (i = 0; i < (NR_HD << PARTN_BITS); i++) { + xsa_blocksizes[i] = 1024; + xsa_hardsectsizes[i] = 512; + /* XSysAce_Sector{Read,Write} will allow up to 256 sectors. */ + xsa_maxsect[i] = 256; + } + blksize_size[xsa_major] = xsa_blocksizes; + hardsect_size[xsa_major] = xsa_hardsectsizes; + max_sectors[xsa_major] = xsa_maxsect; + + xsa_gendisk.nr_real = NR_HD; + + register_disk(&xsa_gendisk, MKDEV(xsa_major, 0), + NR_HD << PARTN_BITS, &xsa_fops, size); + + printk(KERN_INFO + "%s at 0x%08X mapped to 0x%08X, irq=%d, %ldKB\n", + DEVICE_NAME, save_BaseAddress, cfg->BaseAddress, XSA_IRQ, + size / 2); + + /* Hook our reset function into system's restart code. */ + old_restart = ppc_md.restart; + ppc_md.restart = xsysace_restart; + + if (proc_init()) + printk(KERN_WARNING "%s: could not register /proc interface.\n", + DEVICE_NAME); + + return 0; +} + +static void __exit +xsysace_exit(void) +{ + cleanup(); +} + +EXPORT_NO_SYMBOLS; + +module_init(xsysace_init); +module_exit(xsysace_exit); diff -urN -X bkexcl linux-2.4.22/drivers/block/xilinx_sysace/xsysace.c linuxppc-2.4/drivers/block/xilinx_sysace/xsysace.c --- linux-2.4.22/drivers/block/xilinx_sysace/xsysace.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/block/xilinx_sysace/xsysace.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,390 @@ +/* $Id: xsysace.c,v 1.6 2003/05/14 13:53:30 robertm Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xsysace.c +* +* The Xilinx System ACE driver component. This driver supports the Xilinx +* System Advanced Configuration Environment (ACE) controller. It currently +* supports only the CompactFlash solution. See xsysace.h for a detailed +* description of the driver. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rpm  06/17/02 work in progress
+* 1.00a rmm  05/14/03 Fixed diab compiler warnings relating to asserts
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xparameters.h" +#include "xsysace.h" +#include "xsysace_l.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +static void StubEventHandler(void *CallBackRef, int Event); + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** +* +* Initialize a specific XSysAce instance. The configuration information for +* the given device ID is found and the driver instance data is initialized +* appropriately. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* @param DeviceId is the unique id of the device controlled by this XSysAce +* instance. +* +* @return +* +* XST_SUCCESS if successful, or XST_DEVICE_NOT_FOUND if the device was not +* found in the configuration table in xsysace_g.c. +* +* @note +* +* We do not want to reset the configuration controller here since this could +* cause a reconfiguration of the JTAG target chain, depending on how the +* CFGMODEPIN of the device is wired. +* +******************************************************************************/ +XStatus +XSysAce_Initialize(XSysAce * InstancePtr, u16 DeviceId) +{ + XSysAce_Config *ConfigPtr; + + XASSERT_NONVOID(InstancePtr != NULL); + + InstancePtr->IsReady = 0; + + /* + * Lookup configuration data in the device configuration table. + * Use this configuration info down below when initializing this component. + */ + ConfigPtr = XSysAce_LookupConfig(DeviceId); + + if (ConfigPtr == (XSysAce_Config *) NULL) { + return XST_DEVICE_NOT_FOUND; + } + + /* + * Set some default values for the instance data + */ + InstancePtr->BaseAddress = ConfigPtr->BaseAddress; + InstancePtr->EventHandler = StubEventHandler; + InstancePtr->NumRequested = 0; + InstancePtr->NumRemaining = 0; + InstancePtr->BufferPtr = NULL; + + /* + * Put the device into 16-bit mode or 8-bit mode depending on compile-time + * parameter + */ +#if (XPAR_XSYSACE_MEM_WIDTH == 16) + XSysAce_RegWrite16(InstancePtr->BaseAddress + XSA_BMR_OFFSET, + XSA_BMR_16BIT_MASK); +#else + XSysAce_RegWrite16(InstancePtr->BaseAddress + XSA_BMR_OFFSET, 0); +#endif + + /* + * Disable interrupts. Interrupts must be enabled by the user using + * XSysAce_EnableInterrupt(). Put the interrupt request line in reset and + * clear the interrupt enable bits. + */ + XSysAce_mOrControlReg(InstancePtr->BaseAddress, XSA_CR_RESETIRQ_MASK); + XSysAce_mAndControlReg(InstancePtr->BaseAddress, + ~(XSA_CR_DATARDYIRQ_MASK | XSA_CR_ERRORIRQ_MASK | + XSA_CR_CFGDONEIRQ_MASK)); + + /* + * Indicate the instance is now ready to use, initialized without error + */ + InstancePtr->IsReady = XCOMPONENT_IS_READY; + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Attempt to lock access to the CompactFlash. The CompactFlash may be accessed +* by the MPU port as well as the JTAG configuration port within the System ACE +* device. This function requests exclusive access to the CompactFlash for the +* MPU port. This is a non-blocking request. If access cannot be locked +* (because the configuration controller has the lock), an appropriate status is +* returned. In this case, the user should call this function again until +* successful. +* +* If the user requests a forced lock, the JTAG configuration controller will +* be put into a reset state in case it currently has a lock on the CompactFlash. +* This effectively aborts any operation the configuration controller had in +* progress and makes the configuration controller restart its process the +* next time it is able to get a lock. +* +* A lock must be granted to the user before attempting to read or write the +* CompactFlash device. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* @param Force is a boolean value that, when set to TRUE, will force the MPU +* lock to occur in the System ACE. When set to FALSE, the lock is +* requested and the device arbitrates between the MPU request and +* JTAG requests. Forcing the MPU lock resets the configuration +* controller, thus aborting any configuration operations in progress. +* +* @return +* +* XST_SUCCESS if the lock was granted, or XST_DEVICE_BUSY if the lock was +* not granted because the configuration controller currently has access to +* the CompactFlash. +* +* @note +* +* If the lock is not granted to the MPU immediately, this function removes its +* request for a lock so that a lock is not later granted at a time when the +* application is (a) not ready for the lock, or (b) cannot be informed +* asynchronously about the granted lock since there is no such interrupt event. +* +******************************************************************************/ +XStatus +XSysAce_Lock(XSysAce * InstancePtr, u32 Force) +{ + u32 IsLocked; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Check to see if the configuration controller currently has the lock + */ + IsLocked = (XSysAce_mGetStatusReg(InstancePtr->BaseAddress) & + XSA_SR_CFGLOCK_MASK); + + if (Force) { + /* + * Reset the configuration controller if it has the lock. Per ASIC + * designer, this eliminates a potential deadlock if the FORCELOCK and + * LOCKREQ bits are both set and the RDYFORCFCMD is not set. + */ + if (IsLocked) { + /* Reset the configuration controller */ + XSysAce_mOrControlReg(InstancePtr->BaseAddress, + XSA_CR_CFGRESET_MASK); + } + + /* Force the MPU lock. The lock will occur immediately. */ + XSysAce_mOrControlReg(InstancePtr->BaseAddress, + XSA_CR_LOCKREQ_MASK | + XSA_CR_FORCELOCK_MASK); + } else { + /* + * Check to see if the configuration controller has the lock. If so, + * return a busy status. + */ + if (IsLocked) { + return XST_DEVICE_BUSY; + } + + /* Request the lock, but do not force it */ + XSysAce_mOrControlReg(InstancePtr->BaseAddress, + XSA_CR_LOCKREQ_MASK); + } + + /* + * See if the lock was granted. Note that it is guaranteed to occur if + * the user forced it. + */ + if (!XSysAce_mIsMpuLocked(InstancePtr->BaseAddress)) { + /* Lock was not granted, so remove request and return a busy */ + XSysAce_mAndControlReg(InstancePtr->BaseAddress, + ~(XSA_CR_LOCKREQ_MASK | + XSA_CR_FORCELOCK_MASK)); + + return XST_DEVICE_BUSY; + } + + /* + * Lock has been granted. + * + * If the configuration controller had the lock and has been reset, + * go ahead and release it from reset as it will not be able to get + * the lock again until the MPU lock is released. + */ + if (IsLocked && Force) { + /* Release the reset of the configuration controller */ + XSysAce_mAndControlReg(InstancePtr->BaseAddress, + ~XSA_CR_CFGRESET_MASK); + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Release the MPU lock to the CompactFlash. If a lock is not currently granted +* to the MPU port, this function has no effect. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XSysAce_Unlock(XSysAce * InstancePtr) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Blindly clear the lock and force-lock request bits of the control + * register + */ + XSysAce_mAndControlReg(InstancePtr->BaseAddress, + ~(XSA_CR_LOCKREQ_MASK | XSA_CR_FORCELOCK_MASK)); +} + +/*****************************************************************************/ +/** +* +* Get all outstanding errors. Errors include the inability to read or write +* CompactFlash and the inability to successfully configure FPGA devices along +* the target FPGA chain. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* +* @return +* +* A 32-bit mask of error values. See xsysace_l.h for a description of possible +* values. The error identifiers are prefixed with XSA_ER_*. +* +* @note +* +* None. +* +******************************************************************************/ +u32 +XSysAce_GetErrors(XSysAce * InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + return XSysAce_mGetErrorReg(InstancePtr->BaseAddress); +} + +/*****************************************************************************/ +/** +* +* Stub for the asynchronous event callback. The stub is here in case the upper +* layers forget to set the handler. +* +* @param CallBackRef is a pointer to the upper layer callback reference +* @param Event is the event that occurs +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +StubEventHandler(void *CallBackRef, int Event) +{ + XASSERT_VOID_ALWAYS(); +} + +/*****************************************************************************/ +/** +* +* Looks up the device configuration based on the unique device ID. The table +* XSysAce_ConfigTable contains the configuration info for each device in the +* system. +* +* @param DeviceId is the unique device ID to look for. +* +* @return +* +* A pointer to the configuration data for the device, or NULL if no match is +* found. +* +* @note +* +* None. +* +******************************************************************************/ +XSysAce_Config * +XSysAce_LookupConfig(u16 DeviceId) +{ + extern XSysAce_Config XSysAce_ConfigTable[]; + XSysAce_Config *CfgPtr = NULL; + int i; + + for (i = 0; i < XPAR_XSYSACE_NUM_INSTANCES; i++) { + if (XSysAce_ConfigTable[i].DeviceId == DeviceId) { + CfgPtr = &XSysAce_ConfigTable[i]; + break; + } + } + + return CfgPtr; +} diff -urN -X bkexcl linux-2.4.22/drivers/block/xilinx_sysace/xsysace.h linuxppc-2.4/drivers/block/xilinx_sysace/xsysace.h --- linux-2.4.22/drivers/block/xilinx_sysace/xsysace.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/block/xilinx_sysace/xsysace.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,338 @@ +/* $Id: xsysace.h,v 1.8 2002/11/04 23:44:21 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xsysace.h +* +* The Xilinx System ACE driver. This driver supports the Xilinx System Advanced +* Configuration Environment (ACE) controller. It currently supports only the +* CompactFlash solution. The driver makes use of the Microprocessor (MPU) +* interface to communicate with the device. +* +* The driver provides a user the ability to access the CompactFlash through +* the System ACE device. The user can read and write CompactFlash sectors, +* identify the flash device, and reset the flash device. Also, the driver +* provides a user the ability to configure FPGA devices by selecting a +* configuration file (.ace file) resident on the CompactFlash, or directly +* configuring the FPGA devices via the MPU port and the configuration JTAG +* port of the controller. +* +* Bus Mode +* +* The System ACE device supports both 8-bit and 16-bit access to its registers. +* The driver defaults to 8-bit access, but can be changed to use 16-bit access +* at compile-time. The compile-time constant XPAR_XSYSACE_MEM_WIDTH must be +* defined equal to 16 to make the driver use 16-bit access. This constant is +* typically defined in xparameters.h. +* +* Endianness +* +* The System ACE device is little-endian. If being accessed by a big-endian +* processor, the endian conversion will be done by the device driver. The +* endian conversion is encapsulated inside the XSysAce_RegRead/Write functions +* so that it can be removed if the endian conversion is moved to hardware. +* +* Hardware Access +* +* The device driver expects the System ACE controller to be a memory-mapped +* device. Access to the System ACE controller is typically achieved through +* the External Memory Controller (EMC) IP core. The EMC is simply a pass-through +* device that allows access to the off-chip System ACE device. There is no +* software-based setup or configuration necessary for the EMC. +* +* The System ACE registers are expected to be byte-addressable. If for some +* reason this is not possible, the register offsets defined in xsysace_l.h must +* be changed accordingly. +* +* Reading or Writing CompactFlash +* +* The smallest unit that can be read from or written to CompactFlash is one +* sector. A sector is 512 bytes. The functions provided by this driver allow +* the user to specify a starting sector ID and the number of sectors to be read +* or written. At most 256 sectors can be read or written in one operation. The +* user must ensure that the buffer passed to the functions is big enough to +* hold (512 * NumSectors), where NumSectors is the number of sectors specified. +* +* Interrupt Mode +* +* By default, the device and driver are in polled mode. The user is required to +* enable interrupts using XSysAce_EnableInterrupt(). In order to use interrupts, +* it is necessary for the user to connect the driver's interrupt handler, +* XSysAce_InterruptHandler(), to the interrupt system of the application. This +* function does not save and restore the processor context. An event handler +* must also be set by the user, using XSysAce_SetEventHandler(), for the driver +* such that the handler is called when interrupt events occur. The handler is +* called from interrupt context and allows application-specific processing to +* be performed. +* +* In interrupt mode, the only available interrupt is data buffer ready, so +* the size of a data transfer between interrupts is 32 bytes (the size of the +* data buffer). +* +* Polled Mode +* +* The sector read and write functions are blocking when in polled mode. This +* choice was made over non-blocking since sector transfer rates are high +* (>20Mbps) and the user can limit the number of sectors transferred in a single +* operation to 1 when in polled mode, plus the API for non-blocking polled +* functions was a bit awkward. Below is some more information on the sector +* transfer rates given the current state of technology (year 2002). Although +* the seek times for CompactFlash cards is high, this average hit needs to be +* taken every time a new read/write operation is invoked by the user. So the +* additional few microseconds to transfer an entire sector along with seeking +* is miniscule. +* +* - Microdrives are slower than CompactFlash cards by a significant factor, +* especially if the MD is asleep. +* - Microdrive: +* - Power-up/wake-up time is approx. 150 to 1000 ms. +* - Average seek time is approx. 15 to 20 ms. +* - CompactFlash: +* - Power-up/reset time is approx. 50 to 400 ms and wake-up time is +* approx. 3 ms. +* - "Seek time" here means how long it takes the internal controller +* to process the command until the sector data is ready for transfer +* by the ACE controller. This time is approx. 2 ms per sector. +* +* - Once the sector data is ready in the CF device buffer (i.e., "seek time" is +* over) the ACE controller can read 2 bytes from the MD/CF device every 11 +* clock cycles, assuming no wait cycles happen. For instance, if the clock +* is 33 MHz, then then the max. rate that the ACE controller can transfer is +* 6 MB/sec. However, due to other overhead (e.g., time for data buffer +* transfers over MPU port, etc.), a better estimate is 3-5 MB/sec. +* +* Mutual Exclusion +* +* This driver is not thread-safe. The System ACE device has a single data +* buffer and therefore only one operation can be active at a time. The device +* driver does not prevent the user from starting an operation while a previous +* operation is still in progress. It is up to the user to provide this mutual +* exclusion. +* +* Errors +* +* Error causes are defined in xsysace_l.h using the prefix XSA_ER_*. The +* user can use XSysAce_GetErrors() to retrieve all outstanding errors. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rpm  06/17/02 work in progress
+* 
+* +******************************************************************************/ + +#ifndef XSYSACE_H /* prevent circular inclusions */ +#define XSYSACE_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xstatus.h" +#include "xsysace_l.h" + +/************************** Constant Definitions *****************************/ + +/** @name Asynchronous Events + * + * Asynchronous events passed to the event handler when in interrupt mode. + * + * Note that when an error event occurs, the only way to clear this condition + * is to reset the CompactFlash or the System ACE configuration controller, + * depending on where the error occurred. The driver does not reset either + * and leaves this task to the user. + * @{ + */ +#define XSA_EVENT_CFG_DONE 1 /**< Configuration of JTAG chain is done */ +#define XSA_EVENT_DATA_DONE 2 /**< Data transfer to/from CompactFlash is done */ +#define XSA_EVENT_ERROR 3 /**< An error occurred. Use XSysAce_GetErrors() + * to determine the cause of the error(s). + */ +/*@}*/ + +/**************************** Type Definitions *******************************/ + +/** + * Typedef for CompactFlash identify drive parameters. Use XSysAce_IdentifyCF() + * to retrieve this information from the CompactFlash storage device. + */ +typedef struct { + u16 Signature; /**< CompactFlash signature is 0x848a */ + u16 NumCylinders; /**< Default number of cylinders */ + u16 Reserved; + u16 NumHeads; /**< Default number of heads */ + u16 NumBytesPerTrack; + /**< Number of unformatted bytes per track */ + u16 NumBytesPerSector; + /**< Number of unformatted bytes per sector */ + u16 NumSectorsPerTrack; + /**< Default number of sectors per track */ + u32 NumSectorsPerCard; + /**< Default number of sectors per card */ + u16 VendorUnique; /**< Vendor unique */ + u8 SerialNo[20]; /**< ASCII serial number */ + u16 BufferType; /**< Buffer type */ + u16 BufferSize; /**< Buffer size in 512-byte increments */ + u16 NumEccBytes; /**< Number of ECC bytes on R/W Long cmds */ + u8 FwVersion[8]; /**< ASCII firmware version */ + u8 ModelNo[40]; /**< ASCII model number */ + u16 MaxSectors; /**< Max sectors on R/W Multiple cmds */ + u16 DblWord; /**< Double Word not supported */ + u16 Capabilities; /**< Device capabilities */ + u16 Reserved2; + u16 PioMode; /**< PIO data transfer cycle timing mode */ + u16 DmaMode; /**< DMA data transfer cycle timing mode */ + u16 TranslationValid; + /**< Translation parameters are valid */ + u16 CurNumCylinders;/**< Current number of cylinders */ + u16 CurNumHeads; /**< Current number of heads */ + u16 CurSectorsPerTrack; + /**< Current number of sectors per track */ + u32 CurSectorsPerCard; + /**< Current capacity in sectors */ + u16 MultipleSectors;/**< Multiple sector setting */ + u32 LbaSectors; /**< Number of addressable sectors in LBA mode */ + u8 Reserved3[132]; + u16 SecurityStatus; /**< Security status */ + u8 VendorUniqueBytes[62]; + /**< Vendor unique bytes */ + u16 PowerDesc; /**< Power requirement description */ + u8 Reserved4[190]; + +} XSysAce_CFParameters; + +/** + * Callback when an asynchronous event occurs during interrupt mode. + * + * @param CallBackRef is a callback reference passed in by the upper layer + * when setting the callback functions, and passed back to the upper + * layer when the callback is invoked. + * @param Event is the event that occurred. See xsysace.h and the event + * identifiers prefixed with XSA_EVENT_* for a description of possible + * events. + */ +typedef void (*XSysAce_EventHandler) (void *CallBackRef, int Event); + +/** + * This typedef contains configuration information for the device. + */ +typedef struct { + u16 DeviceId; /**< Unique ID of device */ + u32 BaseAddress;/**< Register base address */ + +} XSysAce_Config; + +/** + * The XSysAce driver instance data. The user is required to allocate a + * variable of this type for every System ACE device in the system. A + * pointer to a variable of this type is then passed to the driver API + * functions. + */ +typedef struct { + u32 BaseAddress; /* Base address of ACE device */ + u32 IsReady; /* Device is initialized and ready */ + + /* interrupt-related data */ + int NumRequested; /* Number of bytes to read/write */ + int NumRemaining; /* Number of bytes left to read/write */ + u8 *BufferPtr; /* Buffer being read/written */ + XSysAce_EventHandler EventHandler; /* Callback for asynchronous events */ + void *EventRef; /* Callback reference */ + +} XSysAce; + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/* + * Required functions in xsysace.c + */ +XStatus XSysAce_Initialize(XSysAce * InstancePtr, u16 DeviceId); +XStatus XSysAce_Lock(XSysAce * InstancePtr, u32 Force); +void XSysAce_Unlock(XSysAce * InstancePtr); +u32 XSysAce_GetErrors(XSysAce * InstancePtr); +XSysAce_Config *XSysAce_LookupConfig(u16 DeviceId); + +/* + * CompactFlash access functions in xsysace_compactflash.c + */ +XStatus XSysAce_ResetCF(XSysAce * InstancePtr); +XStatus XSysAce_AbortCF(XSysAce * InstancePtr); +XStatus XSysAce_IdentifyCF(XSysAce * InstancePtr, + XSysAce_CFParameters * ParamPtr); +u32 XSysAce_IsCFReady(XSysAce * InstancePtr); +XStatus XSysAce_SectorRead(XSysAce * InstancePtr, u32 StartSector, + int NumSectors, u8 * BufferPtr); +XStatus XSysAce_SectorWrite(XSysAce * InstancePtr, u32 StartSector, + int NumSectors, u8 * BufferPtr); +u16 XSysAce_GetFatStatus(XSysAce * InstancePtr); + +/* + * JTAG configuration interface functions in xsysace_jtagcfg.c + */ +void XSysAce_ResetCfg(XSysAce * InstancePtr); +void XSysAce_SetCfgAddr(XSysAce * InstancePtr, unsigned int Address); +void XSysAce_SetStartMode(XSysAce * InstancePtr, u32 ImmedOnReset, + u32 SetStart); +u32 XSysAce_IsCfgDone(XSysAce * InstancePtr); +u32 XSysAce_GetCfgSector(XSysAce * InstancePtr); +XStatus XSysAce_ProgramChain(XSysAce * InstancePtr, u8 * BufferPtr, + int NumBytes); + +/* + * General interrupt-related functions in xsysace_intr.c + */ +void XSysAce_EnableInterrupt(XSysAce * InstancePtr); +void XSysAce_DisableInterrupt(XSysAce * InstancePtr); +void XSysAce_SetEventHandler(XSysAce * InstancePtr, + XSysAce_EventHandler FuncPtr, void *CallBackRef); +void XSysAce_InterruptHandler(void *InstancePtr); /* interrupt handler */ + +/* + * Diagnostic functions in xsysace_selftest.c + */ +XStatus XSysAce_SelfTest(XSysAce * InstancePtr); +u16 XSysAce_GetVersion(XSysAce * InstancePtr); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/drivers/block/xilinx_sysace/xsysace_compactflash.c linuxppc-2.4/drivers/block/xilinx_sysace/xsysace_compactflash.c --- linux-2.4.22/drivers/block/xilinx_sysace/xsysace_compactflash.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/block/xilinx_sysace/xsysace_compactflash.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,786 @@ +/* $Id: xsysace_compactflash.c,v 1.9 2002/08/12 15:27:55 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xsysace_compactflash.c +* +* Contains functions to reset, read, and write the CompactFlash device via +* the System ACE controller. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rpm  06/17/02 work in progress
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xsysace.h" +#include "xsysace_l.h" + +/************************** Constant Definitions *****************************/ + +/* + * Indices into the parameter information from the CompactFlash. When the + * user calls XSysAce_IdentifyCF(), the parameter information is read into a + * byte buffer. The byte buffer is then mapped to a XSysAce_CFParameters + * structure using these indices into the byte buffer. + */ +#define XSA_CFPARM_SIGNATURE 0 +#define XSA_CFPARM_NUMCYLS 2 +#define XSA_CFPARM_RESERVED1 4 +#define XSA_CFPARM_NUMHEADS 6 +#define XSA_CFPARM_BYTES_TRACK 8 +#define XSA_CFPARM_BYTES_SECT 10 +#define XSA_CFPARM_SECTS_TRK 12 +#define XSA_CFPARM_SECTS_HI 14 +#define XSA_CFPARM_SECTS_LO 16 +#define XSA_CFPARM_VENDOR1 18 +#define XSA_CFPARM_SERIAL_NO 20 +#define XSA_CFPARM_BUFFER_TYPE 40 +#define XSA_CFPARM_BUFFER_SIZE 42 +#define XSA_CFPARM_ECC_BYTES 44 +#define XSA_CFPARM_FW_VERSION 46 +#define XSA_CFPARM_MODEL_NO 54 +#define XSA_CFPARM_MAX_SECTORS 94 +#define XSA_CFPARM_DBL_WORD 96 +#define XSA_CFPARM_CAPS 98 +#define XSA_CFPARM_RESERVED2 100 +#define XSA_CFPARM_PIO_MODE 102 +#define XSA_CFPARM_DMA_MODE 104 +#define XSA_CFPARM_TRANSLATE 106 +#define XSA_CFPARM_CURCYLS 108 +#define XSA_CFPARM_CURHEADS 110 +#define XSA_CFPARM_CURSECTS_TRK 112 +#define XSA_CFPARM_CURSECTS 114 +#define XSA_CFPARM_MULTIPLE 118 +#define XSA_CFPARM_LBA_SECTS 120 +#define XSA_CFPARM_RESERVED3 124 +#define XSA_CFPARM_SECURITY 256 +#define XSA_CFPARM_VENDOR2 258 +#define XSA_CFPARM_POWER 320 +#define XSA_CFPARM_RESERVED4 322 + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +static void ByteCopy(u8 * SourcePtr, u8 * DestPtr, int NumBytes); +static void FillParam(XSysAce_CFParameters * ParamPtr, u8 * BufPtr); + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** +* +* Reset the CompactFlash device. This function does not reset the System ACE +* controller. An ATA soft-reset of the CompactFlash is performed. +* +* An MPU lock, obtained using XSysAce_Lock(), must be granted before calling +* this function. If a lock has not been granted, no action is taken and an +* error is returned. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* +* @return +* +* - XST_SUCCESS if the reset was done successfully +* - XST_SYSACE_NO_LOCK if no MPU lock has yet been granted +* - XST_DEVICE_BUSY if the CompactFlash is not ready for a command +* +* @note +* +* None. +* +******************************************************************************/ +XStatus +XSysAce_ResetCF(XSysAce * InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* If a lock has not been granted, return an error */ + if (!XSysAce_mIsMpuLocked(InstancePtr->BaseAddress)) { + return XST_SYSACE_NO_LOCK; + } + + /* See if the CF is ready for a command */ + if (!XSysAce_mIsReadyForCmd(InstancePtr->BaseAddress)) { + return XST_DEVICE_BUSY; + } + + /* + * If interrupts are enabled, enable the error interrupt. A reset clears + * the error status, so we're going to re-enable the interrupt here so any + * new errors will be caught. + */ + if (XSysAce_mIsIntrEnabled(InstancePtr->BaseAddress)) { + XSysAce_mOrControlReg(InstancePtr->BaseAddress, + XSA_CR_ERRORIRQ_MASK); + } + + /* + * Send the reset command + */ + XSysAce_RegWrite16(InstancePtr->BaseAddress + XSA_SCCR_OFFSET, + XSA_SCCR_RESET_MASK); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Abort the CompactFlash operation currently in progress. +* +* An MPU lock, obtained using XSysAce_Lock(), must be granted before calling +* this function. If a lock has not been granted, no action is taken and an +* error is returned. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* +* @return +* +* - XST_SUCCESS if the abort was done successfully +* - XST_SYSACE_NO_LOCK if no MPU lock has yet been granted +* - XST_DEVICE_BUSY if the CompactFlash is not ready for a command +* +* @note +* +* According to the ASIC designer, the abort command has not been well tested. +* +******************************************************************************/ +XStatus +XSysAce_AbortCF(XSysAce * InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* If a lock has not been granted, return an error */ + if (!XSysAce_mIsMpuLocked(InstancePtr->BaseAddress)) { + return XST_SYSACE_NO_LOCK; + } + + /* + * See if the CF is ready for a command + * + * TODO: make sure this check works, or possibly the abort can be done + * if it is not ready for a command (e.g., that's what we're aborting)? + */ + if (!XSysAce_mIsReadyForCmd(InstancePtr->BaseAddress)) { + return XST_DEVICE_BUSY; + } + + /* + * Send the abort command + */ + XSysAce_RegWrite16(InstancePtr->BaseAddress + XSA_SCCR_OFFSET, + XSA_SCCR_ABORT_MASK); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Identify the CompactFlash device. Retrieves the parameters for the +* CompactFlash storage device. Note that this is a polled read of one sector +* of data. The data is read from the CompactFlash into a byte buffer, which +* is then copied into the XSysAce_CFParameters structure passed in by the +* user. The copy is necessary since we don't know how the compiler packs +* the XSysAce_CFParameters structure. +* +* An MPU lock, obtained using XSysAce_Lock(), must be granted before calling +* this function. If a lock has not been granted, no action is taken and an +* error is returned. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* @param ParamPtr is a pointer to a XSysAce_CFParameters structure where the +* information for the CompactFlash device will be stored. See xsysace.h +* for details on the XSysAce_CFParameters structure. +* +* @return +* +* - XST_SUCCESS if the identify was done successfully +* - XST_FAILURE if an error occurs. Use XSysAce_GetErrors() to determine cause. +* - XST_SYSACE_NO_LOCK if no MPU lock has yet been granted +* - XST_DEVICE_BUSY if the CompactFlash is not ready for a command +* +* @note +* +* None. +* +* @internal +* +* The identify command has the same protocol as the read sector command +* according to the CompactFlash specification. However, there is a discepency +* in that same specification on the size of the parameter structure. The word +* addresses defined in the spec indicate the parameter information is a full +* 512 bytes, the same size as a sector. The total bytes defined in the spec, +* however, indicate that the parameter information is only 500 bytes. We +* defined the parameter structure in xsysace.h assuming the parameters are the +* full 512 bytes since that makes sense, and therefore ignored the "Total +* Bytes" column in the spec. +* +* The SectorData variable was made static to avoid putting 512 bytes on the +* stack every time this function is called. +* +******************************************************************************/ +XStatus +XSysAce_IdentifyCF(XSysAce * InstancePtr, XSysAce_CFParameters * ParamPtr) +{ + int NumRead; + u32 InterruptsOn; + static u8 SectorData[XSA_CF_SECTOR_SIZE]; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(ParamPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* If a lock has not been granted, return an error */ + if (!XSysAce_mIsMpuLocked(InstancePtr->BaseAddress)) { + return XST_SYSACE_NO_LOCK; + } + + /* See if the CF is ready for a command */ + if (!XSysAce_mIsReadyForCmd(InstancePtr->BaseAddress)) { + return XST_DEVICE_BUSY; + } + + /* + * If interrupts are enabled, we disable them because we want to do this + * identify in polled mode - due to the buffer endian conversion and copy + * that takes place. + */ + InterruptsOn = XSysAce_mIsIntrEnabled(InstancePtr->BaseAddress); + if (InterruptsOn) { + XSysAce_DisableInterrupt(InstancePtr); + } + + /* + * Send the identify command + */ + XSysAce_RegWrite16(InstancePtr->BaseAddress + XSA_SCCR_OFFSET, + XSA_SCCR_IDENTIFY_MASK); + + /* + * Read a sector of data from the data buffer. The parameter info is + * the same size as a sector. + */ + NumRead = XSysAce_ReadDataBuffer(InstancePtr->BaseAddress, SectorData, + XSA_CF_SECTOR_SIZE); + + /* If interrupts were on, re-enable interrupts (regardless of error) */ + if (InterruptsOn) { + XSysAce_EnableInterrupt(InstancePtr); + } + + if (NumRead == 0) { + /* an error occurred */ + return XST_FAILURE; + } + + /* + * Copy the byte buffer to the parameter structure + */ + FillParam(ParamPtr, SectorData); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Check to see if the CompactFlash is ready for a command. The CompactFlash +* may delay after one operation before it is ready for the next. This function +* helps the user determine when it is ready before invoking a CompactFlash +* operation such as XSysAce_SectorRead() or XSysAce_SectorWrite(); +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* +* @return +* +* TRUE if the CompactFlash is ready for a command, and FALSE otherwise. +* +* @note +* +* None. +* +******************************************************************************/ +u32 +XSysAce_IsCFReady(XSysAce * InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + return XSysAce_mIsReadyForCmd(InstancePtr->BaseAddress); +} + +/*****************************************************************************/ +/** +* +* Read at least one sector of data from the CompactFlash. The user specifies +* the starting sector ID and the number of sectors to be read. The minimum unit +* that can be read from the CompactFlash is a sector, which is 512 bytes. +* +* In polled mode, this read is blocking. If there are other tasks in the system +* that must run, it is best to keep the number of sectors to be read to a +* minimum (e.g., 1). In interrupt mode, this read is non-blocking and an event, +* XSA_EVENT_DATA_DONE, is returned to the user in the asynchronous event +* handler when the read is complete. The user must call +* XSysAce_EnableInterrupt() to put the driver/device into interrupt mode. +* +* An MPU lock, obtained using XSysAce_Lock(), must be granted before calling +* this function. If a lock has not been granted, no action is taken and an +* error is returned. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* @param StartSector is the starting sector ID from where data will be read. +* Sector IDs range from 0 (first sector) to 0x10000000. +* @param NumSectors is the number of sectors to read. The range can be from +* 1 to 256. +* @param BufferPtr is a pointer to a buffer where the data will be stored. +* The user must ensure it is big enough to hold (512 * NumSectors) bytes. +* +* @return +* +* - XST_SUCCESS if the read was successful. In interrupt mode, this does not +* mean the read is complete, only that it has begun. An event is returned +* to the user when the read is complete. +* - XST_SYSACE_NO_LOCK if no MPU lock has yet been granted +* - XST_DEVICE_BUSY if the ACE controller is not ready for a command +* - XST_FAILURE if an error occurred during the read. The user should call +* XSysAce_GetErrors() to determine the cause of the error. +* +* @note +* +* None. +* +* @internal +* +* Polled mode is blocking under the assumption that a single sector can be +* transferred at a very fast rate (>20 Mbps). So, the user can choose to +* transfer only single sectors when in polled mode, thus allowing time for +* other work to be done. The biggest issue is that although data transfer +* rates are high, seek time for CompactFlash cards is slow (5-20 ms on +* average, depending on the type of device). We could move to a non-blocking +* solution that transfers 32 bytes at a time (the entire data buffer) and +* then returns. The user would then need to increment its buffer pointer +* appropriately and call the read/write again. The driver would need some way +* to know not to issue a new command to the CompactFlash, but instead continue +* with the previous command. This can be done either with a NumSectors argument +* of zero to indicate that there is already an operation in progress, or by +* having the driver keep state to know there is an operation in progress. The +* interface for either seems a bit awkward. Also, the hit for seek time needs +* to be taken regardless of the blocking or non-blocking nature of the call, so +* the additional few microseconds to transfer a sector of data seems acceptable. +* +******************************************************************************/ +XStatus +XSysAce_SectorRead(XSysAce * InstancePtr, u32 StartSector, + int NumSectors, u8 * BufferPtr) +{ + u16 SectorCmd; + int BytesToRecv; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(NumSectors > 0 + && NumSectors <= (XSA_SCCR_COUNT_MASK + 1)); + XASSERT_NONVOID(BufferPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* If a lock has not been granted, return an error */ + if (!XSysAce_mIsMpuLocked(InstancePtr->BaseAddress)) { + return XST_SYSACE_NO_LOCK; + } + + /* See if the CF is ready for a command */ + if (!XSysAce_mIsReadyForCmd(InstancePtr->BaseAddress)) { + return XST_DEVICE_BUSY; + } + + BytesToRecv = XSA_CF_SECTOR_SIZE * NumSectors; + + /* + * If in interrupt mode, set up the state variables and enable the + * data-buffer-ready interrupt. This needs to be done before the command + * is sent to the ACE, which will cause the interrupt to occur. + */ + if (XSysAce_mIsIntrEnabled(InstancePtr->BaseAddress)) { + InstancePtr->NumRequested = BytesToRecv; + InstancePtr->NumRemaining = BytesToRecv; + InstancePtr->BufferPtr = BufferPtr; + + XSysAce_mOrControlReg(InstancePtr->BaseAddress, + XSA_CR_DATARDYIRQ_MASK); + } + + /* Write the sector ID (LBA) */ + XSysAce_RegWrite32(InstancePtr->BaseAddress + XSA_MLR_OFFSET, + StartSector); + + /* + * Send the read command for the number of sectors specified + */ + SectorCmd = (NumSectors & XSA_SCCR_COUNT_MASK) | XSA_SCCR_READDATA_MASK; + XSysAce_RegWrite16(InstancePtr->BaseAddress + XSA_SCCR_OFFSET, + SectorCmd); + + /* + * If in polled mode, receive the entire amount requested + */ + if (!XSysAce_mIsIntrEnabled(InstancePtr->BaseAddress)) { + int NumRead; + + NumRead = + XSysAce_ReadDataBuffer(InstancePtr->BaseAddress, BufferPtr, + BytesToRecv); + if (NumRead != BytesToRecv) { + /* an error occurred, report this to the user */ + return XST_FAILURE; + } + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Write data to the CompactFlash. The user specifies the starting sector ID +* and the number of sectors to be written. The minimum unit that can be written +* to the CompactFlash is a sector, which is 512 bytes. +* +* In polled mode, this write is blocking. If there are other tasks in the +* system that must run, it is best to keep the number of sectors to be written +* to a minimum (e.g., 1). In interrupt mode, this write is non-blocking and an +* event, XSA_EVENT_DATA_DONE, is returned to the user in the asynchronous +* event handler when the write is complete. The user must call +* XSysAce_EnableInterrupt() to put the driver/device into interrupt mode. +* +* An MPU lock, obtained using XSysAce_Lock(), must be granted before calling +* this function. If a lock has not been granted, no action is taken and an +* error is returned. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* @param StartSector is the starting sector ID from where data will be written. +* Sector IDs range from 0 (first sector) to 0x10000000. +* @param NumSectors is the number of sectors to write. The range can be from +* 1 to 256. +* @param BufferPtr is a pointer to the data buffer to be written. This buffer +* must have at least (512 * NumSectors) bytes. +* +* @return +* +* - XST_SUCCESS if the write was successful. In interrupt mode, this does not +* mean the write is complete, only that it has begun. An event is returned +* to the user when the write is complete. +* - XST_SYSACE_NO_LOCK if no MPU lock has yet been granted +* - XST_DEVICE_BUSY if the ACE controller is not ready for a command +* - XST_FAILURE if an error occurred during the write. The user should call +* XSysAce_GetErrors() to determine the cause of the error. +* +* @note +* +* None. +* +* @internal +* +* Polled mode is blocking under the assumption that a single sector can be +* transferred at a very fast rate (>20 Mbps). So, the user can choose to +* transfer only single sectors when in polled mode, thus allowing time for +* other work to be done. The biggest issue is that although data transfer +* rates are high, seek time for CompactFlash cards is slow (5-20 ms on +* average, depending on the type of device). We could move to a non-blocking +* solution that transfers 32 bytes at a time (the entire data buffer) and +* then returns. The user would then need to increment its buffer pointer +* appropriately and call the read/write again. The driver would need some way +* to know not to issue a new command to the CompactFlash, but instead continue +* with the previous command. This can be done either with a NumSectors argument +* of zero to indicate that there is already an operation in progress, or by +* having the driver keep state to know there is an operation in progress. The +* interface for either seems a bit awkward. Also, the hit for seek time needs +* to be taken regardless of the blocking or non-blocking nature of the call, so +* the additional few microseconds to transfer a sector of data seems acceptable. +* +******************************************************************************/ +XStatus +XSysAce_SectorWrite(XSysAce * InstancePtr, u32 StartSector, + int NumSectors, u8 * BufferPtr) +{ + u16 SectorCmd; + int NumSent; + int BytesToSend; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(NumSectors > 0 + && NumSectors <= (XSA_SCCR_COUNT_MASK + 1)); + XASSERT_NONVOID(BufferPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* If a lock has not been granted, return an error */ + if (!XSysAce_mIsMpuLocked(InstancePtr->BaseAddress)) { + return XST_SYSACE_NO_LOCK; + } + + /* See if the CF is ready for a command */ + if (!XSysAce_mIsReadyForCmd(InstancePtr->BaseAddress)) { + return XST_DEVICE_BUSY; + } + + /* Write the sector ID (LBA) */ + XSysAce_RegWrite32(InstancePtr->BaseAddress + XSA_MLR_OFFSET, + StartSector); + + /* + * Send the write command for the number of sectors specified + */ + SectorCmd = + (NumSectors & XSA_SCCR_COUNT_MASK) | XSA_SCCR_WRITEDATA_MASK; + XSysAce_RegWrite16(InstancePtr->BaseAddress + XSA_SCCR_OFFSET, + SectorCmd); + + BytesToSend = XSA_CF_SECTOR_SIZE * NumSectors; + + /* + * If in interrupt mode, set up the state variables and enable the + * data-buffer-ready interrupt. We do this after the write command above + * is done in order to guarantee that the interrupt occurs only after the + * first data buffer write is done below (an interrupt may or may not occur + * after the write command is issued) + */ + if (XSysAce_mIsIntrEnabled(InstancePtr->BaseAddress)) { + /* + * Set the state variables. We're going to send one data buffer here in + * this routine, so adjust the buffer pointer and number remaining to + * reflect this. + */ + InstancePtr->NumRequested = BytesToSend; + InstancePtr->NumRemaining = BytesToSend - XSA_DATA_BUFFER_SIZE; + InstancePtr->BufferPtr = BufferPtr + XSA_DATA_BUFFER_SIZE; + + /* Send only one data buffer in interrupt mode */ + BytesToSend = XSA_DATA_BUFFER_SIZE; + + XSysAce_mOrControlReg(InstancePtr->BaseAddress, + XSA_CR_DATARDYIRQ_MASK); + } + + NumSent = XSysAce_WriteDataBuffer(InstancePtr->BaseAddress, BufferPtr, + BytesToSend); + if (NumSent != BytesToSend) { + /* an error occurred, report this to the user */ + return XST_FAILURE; + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Get the status of the FAT filesystem on the first valid partition of the +* CompactFlash device such as the boot record and FAT types found. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* +* @return +* +* A 16-bit mask of status values. These values are defined in xsysace_l.h +* with the prefix XSA_FAT_*. +* +* @note +* +* None. +* +******************************************************************************/ +u16 +XSysAce_GetFatStatus(XSysAce * InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + return XSysAce_RegRead16(InstancePtr->BaseAddress + XSA_FSR_OFFSET); +} + +/*****************************************************************************/ +/** +* +* This bit of ugliness allows us to present a structure to the user. The +* byte buffer which was read from the CompactFlash is converted into the +* XSysAce_CFParameters structure. The byte buffer is accessed by the indices +* of the fields as defined at the top of this file. We do not read from +* CompactFlash directly into the CF Parameter structure because of structure +* packing problems. +* +* Note that we also need to perform endian conversion here since the System +* ACE device gives us little endian data and we're (possibly) on a big endian +* processor. +* +* @param ParamPtr is the structure to fill +* @param BufPtr is the byte buffer containing the CF parameter data +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +FillParam(XSysAce_CFParameters * ParamPtr, u8 * BufPtr) +{ + u16 HiWord; + u16 LoWord; + + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_SIGNATURE]), + &ParamPtr->Signature); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_NUMCYLS]), + &ParamPtr->NumCylinders); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_RESERVED1]), + &ParamPtr->Reserved); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_NUMHEADS]), + &ParamPtr->NumHeads); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_BYTES_TRACK]), + &ParamPtr->NumBytesPerTrack); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_BYTES_SECT]), + &ParamPtr->NumBytesPerSector); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_SECTS_TRK]), + &ParamPtr->NumSectorsPerTrack); + + /* NumSectorsPerCard is stored as two half-words, MSW first */ + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_SECTS_HI]), + &HiWord); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_SECTS_LO]), + &LoWord); + ParamPtr->NumSectorsPerCard = ((u32) HiWord << 16) | (u32) LoWord; + + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_VENDOR1]), + &ParamPtr->VendorUnique); + + ByteCopy(&BufPtr[XSA_CFPARM_SERIAL_NO], ParamPtr->SerialNo, 20); + + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_BUFFER_TYPE]), + &ParamPtr->BufferType); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_BUFFER_SIZE]), + &ParamPtr->BufferSize); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_ECC_BYTES]), + &ParamPtr->NumEccBytes); + + ByteCopy(&BufPtr[XSA_CFPARM_FW_VERSION], ParamPtr->FwVersion, 8); + ByteCopy(&BufPtr[XSA_CFPARM_MODEL_NO], ParamPtr->ModelNo, 40); + + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_MAX_SECTORS]), + &ParamPtr->MaxSectors); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_DBL_WORD]), + &ParamPtr->DblWord); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_CAPS]), + &ParamPtr->Capabilities); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_RESERVED2]), + &ParamPtr->Reserved2); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_PIO_MODE]), + &ParamPtr->PioMode); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_DMA_MODE]), + &ParamPtr->DmaMode); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_TRANSLATE]), + &ParamPtr->TranslationValid); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_CURCYLS]), + &ParamPtr->CurNumCylinders); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_CURHEADS]), + &ParamPtr->CurNumHeads); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_CURSECTS_TRK]), + &ParamPtr->CurSectorsPerTrack); + XIo_FromLittleEndian32(*((u32 *) & BufPtr[XSA_CFPARM_CURSECTS]), + &ParamPtr->CurSectorsPerCard); + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_MULTIPLE]), + &ParamPtr->MultipleSectors); + XIo_FromLittleEndian32(*((u32 *) & BufPtr[XSA_CFPARM_LBA_SECTS]), + &ParamPtr->LbaSectors); + + ByteCopy(&BufPtr[XSA_CFPARM_RESERVED3], ParamPtr->Reserved3, 132); + + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_SECURITY]), + &ParamPtr->SecurityStatus); + + ByteCopy(&BufPtr[XSA_CFPARM_VENDOR2], ParamPtr->VendorUniqueBytes, 62); + + XIo_FromLittleEndian16(*((u16 *) & BufPtr[XSA_CFPARM_POWER]), + &ParamPtr->PowerDesc); + + ByteCopy(&BufPtr[XSA_CFPARM_RESERVED4], ParamPtr->Reserved4, 190); + +} + +/*****************************************************************************/ +/** +* +* Utility to copy bytes. +* +* @param SourcePtr is a pointer to the source byte buffer +* @param DestPtr is a pointer to the destination byte buffer +* @param NumBytes is the number of bytes to copy +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +ByteCopy(u8 * SourcePtr, u8 * DestPtr, int NumBytes) +{ + int i; + + for (i = 0; i < NumBytes; i++) { + DestPtr[i] = SourcePtr[i]; + } +} diff -urN -X bkexcl linux-2.4.22/drivers/block/xilinx_sysace/xsysace_g.c linuxppc-2.4/drivers/block/xilinx_sysace/xsysace_g.c --- linux-2.4.22/drivers/block/xilinx_sysace/xsysace_g.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/block/xilinx_sysace/xsysace_g.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,58 @@ + +/******************************************************************* +* +* CAUTION: This file is automatically generated by libgen. +* Version: Xilinx EDK 3.2.2 EDK_Cm.22 +* DO NOT EDIT. +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +* Description: Driver configuration +* +*******************************************************************/ + +#include "xparameters.h" +#include "xsysace.h" + +/* +* The configuration table for devices +*/ + +XSysAce_Config XSysAce_ConfigTable[] = { + { + XPAR_OPB_SYSACE_0_DEVICE_ID, + XPAR_OPB_SYSACE_0_BASEADDR} +}; diff -urN -X bkexcl linux-2.4.22/drivers/block/xilinx_sysace/xsysace_intr.c linuxppc-2.4/drivers/block/xilinx_sysace/xsysace_intr.c --- linux-2.4.22/drivers/block/xilinx_sysace/xsysace_intr.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/block/xilinx_sysace/xsysace_intr.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,431 @@ +/* $Id: xsysace_intr.c,v 1.5 2002/07/22 20:44:34 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xsysace_intr.c +* +* Contains functions related to System ACE interrupt mode. The driver's +* interrupt handler, XSysAce_InterruptHandler(), must be connected by the +* user to the interrupt controller. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rpm  06/17/02 work in progress
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xsysace.h" +#include "xsysace_l.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + +static void HandleDataBuffer(XSysAce * InstancePtr, u32 StatusReg); +static void DataComplete(XSysAce * InstancePtr); + +/*****************************************************************************/ +/** +* +* Enable System ACE interrupts. There are three interrupts that can be enabled. +* The error interrupt enable serves as the driver's means to determine whether +* interrupts have been enabled or not. The configuration-done interrupt is not +* enabled here, instead it is enabled during a reset - which can cause a +* configuration process to start. The data-buffer-ready interrupt is not enabled +* here either. It is enabled when a read or write operation is started. The +* reason for not enabling the latter two interrupts are because the status bits +* may be set as a leftover of an earlier occurrence of the interrupt. +* +* @param InstancePtr is a pointer to the XSysAce instance to work on. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XSysAce_EnableInterrupt(XSysAce * InstancePtr) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* By default, enable only the error interrupt */ + XSysAce_mOrControlReg(InstancePtr->BaseAddress, XSA_CR_ERRORIRQ_MASK); + + /* Clear the reset on the interrupt line if it was in reset */ + XSysAce_mAndControlReg(InstancePtr->BaseAddress, ~XSA_CR_RESETIRQ_MASK); +} + +/*****************************************************************************/ +/** +* +* Disable all System ACE interrupts and hold the interrupt request line of +* the device in reset. +* +* @param InstancePtr is a pointer to the XSysAce instance that just interrupted. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XSysAce_DisableInterrupt(XSysAce * InstancePtr) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* Put the interrupt request line in reset */ + XSysAce_mOrControlReg(InstancePtr->BaseAddress, XSA_CR_RESETIRQ_MASK); + + /* Clear the interrupt enable bits */ + XSysAce_mAndControlReg(InstancePtr->BaseAddress, + ~(XSA_CR_DATARDYIRQ_MASK | XSA_CR_ERRORIRQ_MASK | + XSA_CR_CFGDONEIRQ_MASK)); +} + +/*****************************************************************************/ +/** +* +* The interrupt handler for the System ACE driver. This handler must be +* connected by the user to an interrupt controller or source. This function +* does not save or restore context. +* +* This function continues reading or writing to the compact flash if such an +* operation is in progress, and notifies the upper layer software through +* the event handler once the operation is complete or an error occurs. On an +* error, any command currently in progress is aborted. +* +* @param InstancePtr is a pointer to the XSysAce instance that just interrupted. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XSysAce_InterruptHandler(void *InstancePtr) +{ + u32 StatusReg; + XSysAce *AcePtr = (XSysAce *) InstancePtr; + + XASSERT_VOID(InstancePtr != NULL); + + /* + * Get the status in order to process each interrupt that has occurred + */ + StatusReg = XSysAce_mGetStatusReg(AcePtr->BaseAddress); + + /* + * Reset the interrupt line to effectively clear the interrupt conditions. + * We need to set the bit to clear the interrupts, then clear the bit so + * that new interrupts can be generated. + */ + XSysAce_mOrControlReg(AcePtr->BaseAddress, XSA_CR_RESETIRQ_MASK); + XSysAce_mAndControlReg(AcePtr->BaseAddress, ~XSA_CR_RESETIRQ_MASK); + + /* + * Check for data buffer ready, which means an operation (either read or + * write) is in progress. + */ + if (StatusReg & XSA_SR_DATABUFRDY_MASK) { + /* + * Handles the data buffer, and invokes the callback to the user for + * data transfer completion. + */ + HandleDataBuffer(AcePtr, StatusReg); + } + + /* + * Check for completion of JTAG configuration and report the event up. + * We only do this if the CFGDONE interrupt is enabled since the CFGDONE + * status only gets cleared when the confguration controller is reset, + * which we do not do unless requested by the user because it may cause + * a configuration process to start. We could have gotten into this + * interrupt handler by another interrupt, yet have a leftover CFGDONE + * status from an earlier configuration process. + */ + if ((StatusReg & XSA_SR_CFGDONE_MASK) && + (XSysAce_mGetControlReg(AcePtr->BaseAddress) & + XSA_CR_CFGDONEIRQ_MASK)) { + /* + * Clear the bit indicating MPU is the source of configuration data + * since we're done configuring from the MPU for now. Also clear the + * force CFGMODE bit and the CFGSTART bit, basically undoing what was + * done in XSysAce_ProgramChain(). Disable the interrupts since the + * CFGDONE status does not get cleared unless a reset occurs - and in + * the meantime we may get into this interrupt handler again. + */ + XSysAce_mAndControlReg(AcePtr->BaseAddress, + ~(XSA_CR_CFGSEL_MASK | + XSA_CR_CFGSTART_MASK | + XSA_CR_CFGDONEIRQ_MASK | + XSA_CR_DATARDYIRQ_MASK | + XSA_CR_FORCECFGMODE_MASK)); + + AcePtr->EventHandler(AcePtr->EventRef, XSA_EVENT_CFG_DONE); + } + + /* + * Check for errors and report the event (the user is responsible for + * retrieving and interpreting the errors). We only do this if the error + * interrupt is enabled since the error status only gets cleared when the + * CompactFlash or confguration controller is reset, which we do not do + * because it may cause a configuration process to start. We could have + * entered this interrupt handler by another interrupt and have a leftover + * error status from a previous error. + */ + if ((StatusReg & (XSA_SR_CFGERROR_MASK | XSA_SR_CFCERROR_MASK)) && + (XSysAce_mGetControlReg(AcePtr->BaseAddress) & + XSA_CR_ERRORIRQ_MASK)) { + /* Clear the transfer state to effectively abort the operation */ + AcePtr->NumRequested = 0; + AcePtr->NumRemaining = 0; + AcePtr->BufferPtr = NULL; + + /* + * Disable the error interrupt since the only way to clear the + * error status is to reset the CF or the configuration controller, + * neither of which we want to do here since the consequences may + * be undesirable (i.e., may cause a reconfiguration). The user + * will need to perform the reset based on the error event. + */ + XSysAce_mAndControlReg(AcePtr->BaseAddress, + ~XSA_CR_ERRORIRQ_MASK); + + AcePtr->EventHandler(AcePtr->EventRef, XSA_EVENT_ERROR); + } +} + +/*****************************************************************************/ +/** +* +* Set the callback function for handling events. The upper layer software +* should call this function during initialization. The events are passed +* asynchronously to the upper layer software. The events are described in +* xsysace.h and are named XSA_EVENT_*. +* +* Note that the callback is invoked by the driver within interrupt context, so +* it needs to do its job quickly. If there are potentially slow operations +* within the callback, these should be done at task-level. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* @param FuncPtr is the pointer to the callback function. +* @param CallBackRef is a reference pointer to be passed back to the upper +* layer. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XSysAce_SetEventHandler(XSysAce * InstancePtr, XSysAce_EventHandler FuncPtr, + void *CallBackRef) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(FuncPtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + InstancePtr->EventHandler = FuncPtr; + InstancePtr->EventRef = CallBackRef; +} + +/*****************************************************************************/ +/** +* +* Handle a data-buffer-ready interrupt. If we get the interrupt when reading, +* it means there is still data to read since the interrupt does not occur after +* reading the last data buffer. If we get the interrupt when writing, there +* may or may not be data left to write since the interrupt does occur after the +* last data buffer is written. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* @param StatusReg is the contents of the status register, read at the start +* of the interrupt service routine. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +HandleDataBuffer(XSysAce * InstancePtr, u32 StatusReg) +{ + /* By default, transfer a whole data buffer */ + int BytesToTransfer = XSA_DATA_BUFFER_SIZE; + + /* + * Check to see if number of bytes remaining is less than the data buffer + * size. If it is, we need to adjust the remaining bytes to transfer. + */ + if (InstancePtr->NumRemaining < XSA_DATA_BUFFER_SIZE) { + BytesToTransfer = InstancePtr->NumRemaining; + } + + /* + * Transfer only one data buffer at a time, which is 32 bytes. Note that + * errors will be handled by an error interrupt occurring, so no need to + * check for them here. + */ + if (StatusReg & XSA_SR_DATABUFMODE_MASK) { + /* + * A write operation in progress, so if there is data remaining then + * write the buffer. If no data is remaining, clean up. + */ + if (InstancePtr->NumRemaining > 0) { + (void) XSysAce_WriteDataBuffer(InstancePtr->BaseAddress, + InstancePtr->BufferPtr, + BytesToTransfer); + + /* + * Decrement the number of bytes remaining to be transferred and + * adjust the buffer pointer appropriately. + */ + InstancePtr->NumRemaining -= BytesToTransfer; + InstancePtr->BufferPtr += BytesToTransfer; + } else { + /* Done writing data, so clean up */ + DataComplete(InstancePtr); + } + } else { + /* A read operation in progress, so read the buffer */ + (void) XSysAce_ReadDataBuffer(InstancePtr->BaseAddress, + InstancePtr->BufferPtr, + BytesToTransfer); + + /* + * Decrement the number of bytes remaining to be transferred and + * adjust the buffer pointer appropriately. If it was the last buffer, + * we're done and we can cleanup. + */ + InstancePtr->NumRemaining -= BytesToTransfer; + InstancePtr->BufferPtr += BytesToTransfer; + + if (InstancePtr->NumRemaining == 0) { + /* Done reading data, so clean up */ + DataComplete(InstancePtr); + } + } +} + +/*****************************************************************************/ +/** +* +* Handle cleanup when a data transfer is complete. This means intializing the +* state variables, disabling the data-buffer-ready interrupt, and sending the +* event to the user. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +DataComplete(XSysAce * InstancePtr) +{ + InstancePtr->NumRequested = 0; + InstancePtr->NumRemaining = 0; + InstancePtr->BufferPtr = NULL; + + /* + * Disable the data-buffer-ready interrupt. This isn't necessary when + * reading since the DATABUFRDY status bit is cleared by the ACE after + * the last data buffer is read. However, the ACE isn't currently + * smart enough to clear the DATABUFRDY status bit after the last data + * buffer is written during a write operation. So, we need to use the + * enable/disable interrupt bit to control its usefulness. + */ + XSysAce_mAndControlReg(InstancePtr->BaseAddress, + ~XSA_CR_DATARDYIRQ_MASK); + + /* + * The same code is executed for JTAG configuration as well as CompactFlash + * transfers, so we need to distinguish between JTAG config done and CF + * data transfer done. We look at the CFGSEL value in the control register + * to determine if an MPU JTAG config process has just completed. The + * CFG_DONE event is passed up later by the main interrupt handler. + */ + if ((XSysAce_mGetControlReg(InstancePtr->BaseAddress) + & XSA_CR_CFGSEL_MASK) == 0) { + /* no JTAG configuration in progress */ + InstancePtr->EventHandler(InstancePtr->EventRef, + XSA_EVENT_DATA_DONE); + } +} diff -urN -X bkexcl linux-2.4.22/drivers/block/xilinx_sysace/xsysace_jtagcfg.c linuxppc-2.4/drivers/block/xilinx_sysace/xsysace_jtagcfg.c --- linux-2.4.22/drivers/block/xilinx_sysace/xsysace_jtagcfg.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/block/xilinx_sysace/xsysace_jtagcfg.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,462 @@ +/* $Id: xsysace_jtagcfg.c,v 1.6 2002/07/24 21:01:49 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xsysace_jtagcfg.c +* +* Contains functions to control the configuration of the target FPGA chain on +* the System ACE via the JTAG configuration port. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rpm  06/17/02 work in progress
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xsysace.h" +#include "xsysace_l.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** +* +* Reset the JTAG configuration controller. This comprises a reset of the JTAG +* configuration controller and the CompactFlash controller (if it is currently +* being accessed by the configuration controller). Note that the MPU controller +* is not reset, meaning the MPU registers remain unchanged. The configuration +* controller is reset then released from reset in this function. +* +* The CFGDONE status (and therefore interrupt) is cleared when the configuration +* controller is reset. If interrupts have been enabled, we go ahead and enable +* the CFGDONE interrupt here. This means that if and when a configuration +* process starts as a result of this reset, an interrupt will be received when +* it is complete. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* This function is not thread-safe. +* +******************************************************************************/ +void +XSysAce_ResetCfg(XSysAce * InstancePtr) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* Reset the configuration controller */ + XSysAce_mOrControlReg(InstancePtr->BaseAddress, XSA_CR_CFGRESET_MASK); + + /* + * If in interrupt mode, enable the CFGDONE and error interrupts. + * A reset clears the CFGDONE and error statuses, so we're going to + * re-enable the interrupts here so any new errors or CFGDONEs will be + * caught. + */ + if (XSysAce_mIsIntrEnabled(InstancePtr->BaseAddress)) { + XSysAce_mOrControlReg(InstancePtr->BaseAddress, + XSA_CR_CFGDONEIRQ_MASK | + XSA_CR_ERRORIRQ_MASK); + } + + /* Release the reset of the configuration controller */ + XSysAce_mAndControlReg(InstancePtr->BaseAddress, ~XSA_CR_CFGRESET_MASK); +} + +/*****************************************************************************/ +/** +* +* Select the configuration address (or file) from the CompactFlash to be used +* for configuration of the target FPGA chain. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* @param Address is the address or file number to be used as the bitstream to +* configure the target FPGA devices. There are 8 possible files, so +* the value of this parameter can range from 0 to 7. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XSysAce_SetCfgAddr(XSysAce * InstancePtr, unsigned int Address) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(Address < 8); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Set the control register with the address and the bit that forces + * the use of the control register address bits instead of the address + * pins on the device. + */ + XSysAce_mSetCfgAddr(InstancePtr->BaseAddress, Address); +} + +/*****************************************************************************/ +/** +* +* Set the start mode for configuration of the target FPGA chain from +* CompactFlash. The configuration process only starts after a reset. The +* user can indicate that the configuration should start immediately after a +* reset, or the configuration process can be delayed until the user commands +* it to start (using this function). The configuration controller can be +* reset using XSysAce_ResetCfg(). +* +* The user can select which configuration file on the CompactFlash to use using +* the XSysAce_SetCfgAddr() function. If the user intends to configure the target +* FPGA chain directly from the MPU port, this function is not needed. Instead, +* the user would simply call XSysAce_ProgramChain(). +* +* The user can use XSysAce_IsCfgDone() when in polled mode to determine if +* the configuration is complete. If in interrupt mode, the event +* XSA_EVENT_CFG_DONE will be returned asynchronously to the user when the +* configuration is complete. The user must call XSysAce_EnableInterrupt() to put +* the device/driver into interrupt mode. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* @param ImmedOnReset can be set to TRUE to indicate the configuration process +* will start immediately after a reset of the ACE configuration +* controller, or it can be set to FALSE to indicate the configuration +* process is delayed after a reset until the user starts it (using this +* function). +* @param StartCfg is a boolean indicating whether to start the configuration +* process or not. When ImmedOnReset is set to TRUE, this value is +* ignored. When ImmedOnReset is set to FALSE, then this value controls +* when the configuration process is started. When set to TRUE the +* configuration process starts (assuming a reset of the device has +* occurred), and when set to FALSE the configuration process does not +* start. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XSysAce_SetStartMode(XSysAce * InstancePtr, u32 ImmedOnReset, u32 StartCfg) +{ + u32 Control; + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* Get the current contents of the control register */ + Control = XSysAce_mGetControlReg(InstancePtr->BaseAddress); + + /* + * Since the user has called this function, we want to tell the ACE + * controller to look at the CFGMODE bit of the control register rather + * than the CFGMODE pin of the device to determine when to start a + * configuration process. + */ + Control |= XSA_CR_FORCECFGMODE_MASK; + + /* Set or clear the CFGMODE bit of the control register */ + if (ImmedOnReset) { + Control |= XSA_CR_CFGMODE_MASK; /* immediate on reset */ + } else { + Control &= ~XSA_CR_CFGMODE_MASK; /* wait for start bit */ + } + + /* Set or clear the CFGSTART bit of the control register */ + if (StartCfg) { + Control |= XSA_CR_CFGSTART_MASK; + } else { + Control &= ~XSA_CR_CFGSTART_MASK; + } + + XSysAce_mSetControlReg(InstancePtr->BaseAddress, Control); +} + +/*****************************************************************************/ +/** +* +* Program the target FPGA chain through the configuration JTAG port. This +* allows the user to program the devices on the target FPGA chain from the MPU +* port instead of from CompactFlash. The user specifies a buffer and the number +* of bytes to write. The buffer should be equivalent to an ACE (.ace) file. +* +* Note that when loading the ACE file via the MPU port, the first sector of the +* ACE file is discarded. The CF filesystem controller in the System ACE device +* knows to skip the first sector when the ACE file comes from the CF, but the +* CF filesystem controller is bypassed when the ACE file comes from the MPU +* port. For this reason, this function skips the first sector of the buffer +* passed in. +* +* In polled mode, the write is blocking. In interrupt mode, the write is +* non-blocking and an event, XSA_EVENT_CFG_DONE, is returned to the user in +* the asynchronous event handler when the configuration is complete. +* +* An MPU lock, obtained using XSysAce_Lock(), must be granted before calling +* this function. If a lock has not been granted, no action is taken and an +* error is returned. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* @param BufferPtr is a pointer to a buffer that will be used to program +* the configuration JTAG devices. +* @param NumBytes is the number of bytes in the buffer. We assume that there +* is at least one sector of data in the .ace file, which is the +* information sector. +* +* @return +* +* - XST_SUCCESS if the write was successful. In interrupt mode, this does not +* mean the write is complete, only that it has begun. An event is returned +* to the user when the write is complete. +* - XST_SYSACE_NO_LOCK if no MPU lock has yet been granted +* - XST_FAILURE if an error occurred during the write. The user should call +* XSysAce_GetErrors() to determine the cause of the error. +* +* @note +* +* None. +* +* @internal +* +* The System ACE controller has a 32-byte buffer which holds data. The entire +* buffer must be written to ensure that it gets sent to the configuration +* JTAG port. If the number of bytes specified by the user is not a multiple +* of 32, the driver will pad the remaining bytes of the System ACE buffer with +* zeroes in order to write the entire buffer. +* +******************************************************************************/ +XStatus +XSysAce_ProgramChain(XSysAce * InstancePtr, u8 * BufferPtr, int NumBytes) +{ + u32 ControlMask; + int BytesToSend; + int NumSent; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(BufferPtr != NULL); + XASSERT_NONVOID(NumBytes > XSA_CF_SECTOR_SIZE); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* If a lock has not been granted, return an error */ + if (!XSysAce_mIsMpuLocked(InstancePtr->BaseAddress)) { + return XST_SYSACE_NO_LOCK; + } + + /* + * Set up the configuration controller to use the MPU port as the + * source of configuration data (instead of the CF). The following + * control flow comes directly from the System ACE specification, except + * the reset was moved to after the other control register bits are set. + * Putting it into reset before the bits are set seemed to produce + * configuration errors occasionally. + */ + ControlMask = XSysAce_mGetControlReg(InstancePtr->BaseAddress); + + /* Select MPU as the source */ + ControlMask |= XSA_CR_CFGSEL_MASK; + XSysAce_mSetControlReg(InstancePtr->BaseAddress, ControlMask); + + /* Tell controller to wait for start bit from MPU */ + ControlMask |= XSA_CR_FORCECFGMODE_MASK; + ControlMask &= ~XSA_CR_CFGMODE_MASK; + XSysAce_mSetControlReg(InstancePtr->BaseAddress, ControlMask); + + /* Set the start bit */ + ControlMask |= XSA_CR_CFGSTART_MASK; + XSysAce_mSetControlReg(InstancePtr->BaseAddress, ControlMask); + + /* Put the configuration controller into a reset condition */ + ControlMask |= XSA_CR_CFGRESET_MASK; + XSysAce_mSetControlReg(InstancePtr->BaseAddress, ControlMask); + + /* Clear the reset condition, which starts the process */ + ControlMask &= ~XSA_CR_CFGRESET_MASK; + XSysAce_mSetControlReg(InstancePtr->BaseAddress, ControlMask); + + /* + * Set up number of bytes to send. Default to the entire buffer, which + * will be true in polled mode. In interrupt mode, modify this value to + * send only one data buffer of data. Always skip the first sector per + * the comment above. + */ + BytesToSend = NumBytes - XSA_CF_SECTOR_SIZE; + + /* + * The number of bytes to write depends on interrupt or polled mode + */ + if (XSysAce_mIsIntrEnabled(InstancePtr->BaseAddress)) { + /* + * In interrupt mode, so enable the data-buffer-ready and + * configuration-done interrupts. Also, set up the state variables for + * the interrupt handler to transfer the remaining data after the + * initial write below. We need to write one data buffer here in this + * function in order to cause the data-buffer-ready interrupt to occur + * for subsequent writes. + */ + ControlMask |= XSA_CR_DATARDYIRQ_MASK | XSA_CR_CFGDONEIRQ_MASK; + XSysAce_mSetControlReg(InstancePtr->BaseAddress, ControlMask); + + /* Send only one data buffer to begin with (if there is that much) */ + if (BytesToSend > XSA_DATA_BUFFER_SIZE) { + BytesToSend = XSA_DATA_BUFFER_SIZE; + } + + /* + * Setup state variables for the interrupt handler. Skip the first + * sector per the comment above, and also skip the first data buffer + * since it is written below. + */ + InstancePtr->NumRequested = NumBytes - XSA_CF_SECTOR_SIZE; + InstancePtr->BufferPtr = + BufferPtr + XSA_CF_SECTOR_SIZE + BytesToSend; + InstancePtr->NumRemaining = + NumBytes - XSA_CF_SECTOR_SIZE - BytesToSend; + } + + NumSent = XSysAce_WriteDataBuffer(InstancePtr->BaseAddress, + BufferPtr + XSA_CF_SECTOR_SIZE, + BytesToSend); + if (NumSent != BytesToSend) { + /* an error occurred, report this to the user */ + return XST_FAILURE; + } + + /* + * If in polled mode, restore the control register to the way it was + */ + if (!XSysAce_mIsIntrEnabled(InstancePtr->BaseAddress)) { + /* + * Unselect MPU as the source, tell controller to use CFGMODE pin, + * and clear the start bit. + */ + ControlMask &= ~(XSA_CR_CFGSEL_MASK | XSA_CR_FORCECFGMODE_MASK | + XSA_CR_CFGSTART_MASK); + XSysAce_mSetControlReg(InstancePtr->BaseAddress, ControlMask); + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Check to see if the configuration of the target FPGA chain is complete. This +* function is typically only used in polled mode. In interrupt mode, an event +* (XSA_EVENT_CFG_DONE) is returned to the user in the asynchronous event +* handler. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* +* @return +* +* TRUE if the configuration is complete. FALSE otherwise. +* +* @note +* +* None. +* +******************************************************************************/ +u32 +XSysAce_IsCfgDone(XSysAce * InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* Use the layer 0 macro by the same name */ + + return XSysAce_mIsCfgDone(InstancePtr->BaseAddress); +} + +/*****************************************************************************/ +/** +* +* Get the sector ID of the CompactFlash sector being used for configuration of +* the target FPGA chain. This sector ID (or logical block address) only affects +* transfers between the ACE configuration logic and the CompactFlash card. +* This function is typically used for debug purposes to determine which sector +* was being accessed when an error occurred. +* +* @param InstancePtr is a pointer to the XSysAce instance to be worked on. +* +* @return +* +* The sector ID (logical block address) being used for data transfers between +* the ACE configuration logic and the CompactFlash. Sector IDs range from 0 +* to 0x10000000. +* +* @note +* +* None. +* +******************************************************************************/ +u32 +XSysAce_GetCfgSector(XSysAce * InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + return XSysAce_RegRead32(InstancePtr->BaseAddress + XSA_CLR_OFFSET); +} diff -urN -X bkexcl linux-2.4.22/drivers/block/xilinx_sysace/xsysace_l.c linuxppc-2.4/drivers/block/xilinx_sysace/xsysace_l.c --- linux-2.4.22/drivers/block/xilinx_sysace/xsysace_l.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/block/xilinx_sysace/xsysace_l.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,595 @@ +/* $Id: xsysace_l.c,v 1.8 2002/11/04 23:44:21 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xsysace_l.c +* +* This file contains low-level functions to read and write CompactFlash +* sectors and ACE controller registers. These functions can be used when only +* the low-level functionality of the driver is desired. The user would +* typically use the high-level driver functions defined in xsysace.h. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rpm  06/14/02 work in progress
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xsysace_l.h" + +/************************** Constant Definitions *****************************/ + +/* + * Set up the access width of the MPU registers based on compile-time constants. + * If hardware requires 32-bit aligned addresses (XSA_ADDR_ALIGN=4) to access + * the MPU registers, then access all of them as 32 bits. If hardware allows + * 8-bit aligned addresses (XSA_ADDR_ALIGN=1, or not 4) to access the MPU + * registers, access them as 8 or 16 bits depending on the bus mode of the ACE + * controller. + */ +#if (XSA_ADDR_ALIGN == 4) + +#define XIo_In XIo_In32 +#define XIo_Out XIo_Out32 + +#else + +#if (XPAR_XSYSACE_MEM_WIDTH == 16) +#define XIo_In XIo_In16 +#define XIo_Out XIo_Out16 +#else /* XPAR_XSYSACE_MEM_WIDTH */ +#define XIo_In XIo_In8 +#define XIo_Out XIo_Out8 +#endif /* XPAR_XSYSACE_MEM_WIDTH */ + +#endif /* (XSA_ADDR_ALIGN == 4) */ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** +* +* Read a 32-bit value from the given address. Based on a compile-time +* constant, do the read in two 16-bit reads or four 8-bit reads. +* +* @param Address is the address to read from. +* +* @return The 32-bit value of the address. +* +* @note No need for endian conversion in 8-bit mode since this function +* gets the bytes into their proper lanes in the 32-bit word. +* +******************************************************************************/ +#if (XPAR_XSYSACE_MEM_WIDTH == 16) +u32 +XSysAce_RegRead32(u32 Address) +{ + u16 Data; + u16 ConvertedData; + u32 Value = 0; + + /* + * Need to endian convert each 32-bit value. The ACE registers are little- + * endian, so we read the two LSBs first, endian convert, then put them + * in the LSB lanes of the 32-bit word. etc... + */ + Data = (u16) XIo_In(Address); + XIo_FromLittleEndian16(Data, &ConvertedData); + Value = (u32) ConvertedData; + + Data = (u16) XIo_In(Address + (2 * XSA_ADDR_ALIGN)); + XIo_FromLittleEndian16(Data, &ConvertedData); + Value |= ((u32) ConvertedData << 16); + + return Value; +} +#else +u32 +XSysAce_RegRead32(u32 Address) +{ + u32 Value = 0; + + /* + * The ACE registers are little-endian always. This code reads each 8-bit + * register value, in order from LSB to MSB, and shifts it to the correct + * byte lane of the 32-bit word. This code should work on both + * little-endian and big-endian processors. + */ + Value = (u32) XIo_In(Address); + Value |= ((u32) XIo_In(Address + (1 * XSA_ADDR_ALIGN)) << 8); + Value |= ((u32) XIo_In(Address + (2 * XSA_ADDR_ALIGN)) << 16); + Value |= ((u32) XIo_In(Address + (3 * XSA_ADDR_ALIGN)) << 24); + + return Value; +} +#endif + +/*****************************************************************************/ +/** +* +* Read a 16-bit value from the given address. Based on a compile-time +* constant, do the read in one 16-bit read or two 8-bit reads. +* +* @param Address is the address to read from. +* +* @return The 16-bit value of the address. +* +* @note No need for endian conversion in 8-bit mode since this function +* gets the bytes into their proper lanes in the 16-bit word. +* +******************************************************************************/ +#if (XPAR_XSYSACE_MEM_WIDTH == 16) +u16 +XSysAce_RegRead16(u32 Address) +{ + u16 Data; + u16 ConvertedData; + + /* + * Need to endian convert the 16-bit value. The ACE registers are little- + * endian. + */ + Data = (u16) XIo_In(Address); + XIo_FromLittleEndian16(Data, &ConvertedData); + return ConvertedData; +} +#else +u16 +XSysAce_RegRead16(u32 Address) +{ + u16 Value = 0; + + /* + * The ACE registers are little-endian always. This code reads each 8-bit + * register value, in order from LSB to MSB, and shifts it to the correct + * byte lane of the 32-bit word. This code should work on both + * little-endian and big-endian processors. + */ + Value = (u16) XIo_In(Address); + Value |= ((u16) XIo_In(Address + (1 * XSA_ADDR_ALIGN)) << 8); + + return Value; +} +#endif + +/*****************************************************************************/ +/** +* +* Write a 32-bit value to the given address. Based on a compile-time +* constant, do the write in two 16-bit writes or four 8-bit writes. +* +* @param Address is the address to write to. +* @param Data is the value to write +* +* @return None. +* +* @note No need for endian conversion in 8-bit mode since this function +* writes the bytes into their proper lanes based on address. +* +******************************************************************************/ +#if (XPAR_XSYSACE_MEM_WIDTH == 16) +void +XSysAce_RegWrite32(u32 Address, u32 Data) +{ + u16 Hword; + u16 ConvertedData; + + /* + * The ACE registers are little-endian always. This code takes each 16-bit + * value of the incoming 32-bit word and endian converts it, then writes it + * to the ACE register. + */ + Hword = (u16) Data; + XIo_ToLittleEndian16(Hword, &ConvertedData); + XIo_Out(Address, ConvertedData); + + Hword = (u16) (Data >> 16); + XIo_ToLittleEndian16(Hword, &ConvertedData); + XIo_Out(Address + (2 * XSA_ADDR_ALIGN), ConvertedData); +} +#else +void +XSysAce_RegWrite32(u32 Address, u32 Data) +{ + /* + * The ACE registers are little-endian always. This code reads each 8-bit + * register value, in order from LSB to MSB, and shifts it to the correct + * byte lane of the 32-bit word. This code should work on both + * little-endian and big-endian processors. + */ + XIo_Out(Address, (u8) Data); + XIo_Out(Address + (1 * XSA_ADDR_ALIGN), (u8) (Data >> 8)); + XIo_Out(Address + (2 * XSA_ADDR_ALIGN), (u8) (Data >> 16)); + XIo_Out(Address + (3 * XSA_ADDR_ALIGN), (u8) (Data >> 24)); +} +#endif + +/*****************************************************************************/ +/** +* +* Write a 16-bit value to the given address. Based on a compile-time +* constant, do the write in one 16-bit write or two 8-bit writes. +* +* @param Address is the address to write to. +* @param Data is the value to write +* +* @return None. +* +* @note No need for endian conversion in 8-bit mode since this function +* writes the bytes into their proper lanes based on address. +* +******************************************************************************/ +#if (XPAR_XSYSACE_MEM_WIDTH == 16) +void +XSysAce_RegWrite16(u32 Address, u16 Data) +{ + u16 ConvertedData; + + /* + * The ACE registers are little-endian always. This code takes the incoming + * 16-bit and endian converts it, then writes it to the ACE register. + */ + XIo_ToLittleEndian16(Data, &ConvertedData); + XIo_Out(Address, ConvertedData); +} +#else +void +XSysAce_RegWrite16(u32 Address, u16 Data) +{ + /* + * The ACE registers are little-endian always. This code reads each 8-bit + * register value, in order from LSB to MSB, and shifts it to the correct + * byte lane of the 32-bit word. This code should work on both + * little-endian and big-endian processors. + */ + XIo_Out(Address, (u8) Data); + XIo_Out(Address + (1 * XSA_ADDR_ALIGN), (u8) (Data >> 8)); +} +#endif + +/*****************************************************************************/ +/** +* +* Read a CompactFlash sector. This is a blocking, low-level function which +* does not return until the specified sector is read. +* +* @param BaseAddress is the base address of the device +* @param SectorId is the id of the sector to read +* @param BufferPtr is a pointer to a buffer where the data will be stored. +* +* @return +* +* The number of bytes read. If this number is not equal to the sector size, +* 512 bytes, then an error occurred. +* +* @note +* +* None. +* +******************************************************************************/ +int +XSysAce_ReadSector(u32 BaseAddress, u32 SectorId, u8 * BufferPtr) +{ + int NumRead; + + /* Request and wait for the lock */ + XSysAce_mWaitForLock(BaseAddress); + + /* See if the CF is ready for a command */ + if (!XSysAce_mIsReadyForCmd(BaseAddress)) { + return 0; + } + + /* Write the sector ID (LBA) */ + XSysAce_RegWrite32(BaseAddress + XSA_MLR_OFFSET, SectorId); + + /* Send a read command of one sector to the controller */ + XSysAce_RegWrite16(BaseAddress + XSA_SCCR_OFFSET, + XSA_SCCR_READDATA_MASK | 1); + + /* Reset configuration controller (be sure to keep the lock) */ + XSysAce_mOrControlReg(BaseAddress, XSA_CR_CFGRESET_MASK); + + /* Read a sector of data from the data buffer */ + NumRead = XSysAce_ReadDataBuffer(BaseAddress, BufferPtr, + XSA_CF_SECTOR_SIZE); + + /* Clear reset of configuration controller and locks */ + XSysAce_mAndControlReg(BaseAddress, ~(XSA_CR_CFGRESET_MASK | + XSA_CR_LOCKREQ_MASK)); + + return NumRead; +} + +/*****************************************************************************/ +/** +* +* Write a CompactFlash sector. This is a blocking, low-level function which +* does not return until the specified sector is written in its entirety. +* +* @param BaseAddress is the base address of the device +* @param SectorId is the id of the sector to write +* @param BufferPtr is a pointer to a buffer used to write the sector. +* +* @return +* +* The number of bytes written. If this number is not equal to the sector size, +* 512 bytes, then an error occurred. +* +* @note +* +* None. +* +******************************************************************************/ +int +XSysAce_WriteSector(u32 BaseAddress, u32 SectorId, u8 * BufferPtr) +{ + int NumSent; + + /* Get the lock */ + XSysAce_mWaitForLock(BaseAddress); + + /* See if the CF is ready for a command */ + if (!XSysAce_mIsReadyForCmd(BaseAddress)) { + return 0; + } + + /* Write the sector ID (LBA) */ + XSysAce_RegWrite32(BaseAddress + XSA_MLR_OFFSET, SectorId); + + /* Send a write command of one sector to the controller */ + XSysAce_RegWrite16(BaseAddress + XSA_SCCR_OFFSET, + XSA_SCCR_READDATA_MASK | 1); + + /* Reset configuration controller (be sure to keep the lock) */ + XSysAce_mOrControlReg(BaseAddress, XSA_CR_CFGRESET_MASK); + + /* Write a sector of data to the data buffer */ + NumSent = XSysAce_WriteDataBuffer(BaseAddress, BufferPtr, + XSA_CF_SECTOR_SIZE); + + /* Clear reset of configuration controller and locks */ + XSysAce_mAndControlReg(BaseAddress, ~(XSA_CR_CFGRESET_MASK | + XSA_CR_LOCKREQ_MASK)); + + return NumSent; +} + +/*****************************************************************************/ +/** +* +* Read the specified number of bytes from the data buffer of the ACE +* controller. The data buffer, which is 32 bytes, can only be read two bytes +* at a time. Once the data buffer is read, we wait for it to be filled again +* before reading the next buffer's worth of data. +* +* @param BaseAddress is the base address of the device +* @param BufferPtr is a pointer to a buffer in which to store data. +* @param Size is the number of bytes to read +* +* @return +* +* The total number of bytes read, or 0 if an error occurred. +* +* @note +* +* If Size is not aligned with the size of the data buffer (32 bytes), this +* function will read the entire data buffer, dropping the extra bytes on the +* floor since the user did not request them. This is necessary to get the +* data buffer to be ready again. +* +******************************************************************************/ +int +XSysAce_ReadDataBuffer(u32 BaseAddress, u8 * BufferPtr, int Size) +{ + int DataBytes; /* number of data bytes written */ + int BufferBytes; + u16 Data; + + /* + * Read data two bytes at a time. We need to wait for the data + * buffer to be ready before reading the buffer. + */ + BufferBytes = 0; + for (DataBytes = 0; DataBytes < Size;) { + /* + * If at any point during this read an error occurs, exit early + */ + if (XSysAce_mGetErrorReg(BaseAddress) != 0) { + return 0; + } + + if (BufferBytes == 0) { + /* + * Wait for CF data buffer to ready, then reset buffer byte count + */ + while ((XSysAce_mGetStatusReg(BaseAddress) + & XSA_SR_DATABUFRDY_MASK) == 0) ; + + BufferBytes = XSA_DATA_BUFFER_SIZE; + } + + /* + * Need to read two bytes. Put the first one in the output buffer + * because if we're here we know one more is needed. Put the second one + * in the output buffer if there is still room, or just drop it on the + * floor if the requested number of bytes have already been read. + */ + Data = XSysAce_RegRead16(BaseAddress + XSA_DBR_OFFSET); + *BufferPtr++ = (u8) Data; + DataBytes++; + + if (DataBytes < Size) { + /* Still more room in the output buffer */ + *BufferPtr++ = (u8) (Data >> 8); + DataBytes++; + } + + BufferBytes -= 2; + } + + /* + * If a complete data buffer was not read, read and ignore the remaining + * bytes + */ + while (BufferBytes != 0) { + /* + * If at any point during this read an error occurs, exit early + */ + if (XSysAce_mGetErrorReg(BaseAddress) != 0) { + return 0; + } + + (void) XSysAce_RegRead16(BaseAddress + XSA_DBR_OFFSET); + BufferBytes -= 2; + } + + return DataBytes; +} + +/*****************************************************************************/ +/** +* +* Write the specified number of bytes to the data buffer of the ACE controller. +* The data buffer, which is 32 bytes, can only be written two bytes at a time. +* Once the data buffer is written, we wait for it to be empty again before +* writing the next buffer's worth of data. If the size of the incoming buffer +* is not aligned with the System ACE data buffer size (32 bytes), then this +* routine pads out the data buffer with zeros so the entire data buffer is +* written. This is necessary for the ACE controller to process the data buffer. +* +* @param BaseAddress is the base address of the device +* @param BufferPtr is a pointer to a buffer used to write to the controller. +* @param Size is the number of bytes to write +* +* @return +* +* The total number of bytes written (not including pad bytes), or 0 if an +* error occurs. +* +* @note +* +* None. +* +******************************************************************************/ +int +XSysAce_WriteDataBuffer(u32 BaseAddress, u8 * BufferPtr, int Size) +{ + int DataBytes; /* number of data bytes written */ + int BufferBytes; + u16 Data; + + /* + * Write a sector two bytes at a time. We need to wait for the data + * buffer to be ready before writing the buffer. + */ + BufferBytes = 0; + for (DataBytes = 0; DataBytes < Size;) { + /* + * If at any point during this write an error occurs, exit early + */ + if (XSysAce_mGetErrorReg(BaseAddress) != 0) { + return 0; + } + + if (BufferBytes == 0) { + /* + * Wait for CF read data buffer to ready, then reset buffer byte + * count + */ + while ((XSysAce_mGetStatusReg(BaseAddress) + & XSA_SR_DATABUFRDY_MASK) == 0) ; + + BufferBytes = XSA_DATA_BUFFER_SIZE; + } + + /* + * Need to send two bytes. Grab the first one from the incoming buffer + * because if we're here we know one more exists. Grab the second one + * from the incoming buffer if there are still any bytes remaining, or + * send a pad byte if the incoming buffer has been expired. + */ + Data = *BufferPtr++; + DataBytes++; + + if (DataBytes < Size) { + /* Still more data in the incoming buffer */ + Data |= ((u16) * BufferPtr++ << 8); + DataBytes++; + } else { + /* No more data in the incoming buffer, send a pad byte of 0 */ + Data |= ((u16) 0 << 8); + } + + XSysAce_RegWrite16(BaseAddress + XSA_DBR_OFFSET, Data); + + BufferBytes -= 2; + } + + /* + * If a complete data buffer was not filled, fill it with pad bytes (zeros) + */ + while (BufferBytes != 0) { + /* + * If at any point during this write an error occurs, exit early + */ + if (XSysAce_mGetErrorReg(BaseAddress) != 0) { + return 0; + } + + XSysAce_RegWrite16(BaseAddress + XSA_DBR_OFFSET, 0); + BufferBytes -= 2; + } + + return DataBytes; +} diff -urN -X bkexcl linux-2.4.22/drivers/block/xilinx_sysace/xsysace_l.h linuxppc-2.4/drivers/block/xilinx_sysace/xsysace_l.h --- linux-2.4.22/drivers/block/xilinx_sysace/xsysace_l.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/block/xilinx_sysace/xsysace_l.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,493 @@ +/* $Id: xsysace_l.h,v 1.13 2002/10/24 20:51:00 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xsysace_l.h +* +* Defines identifiers and low-level macros/functions for the XSysAce driver. +* These identifiers include register offsets and bit masks. A high-level driver +* interface is defined in xsysace.h. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rpm  06/14/02 work in progress
+* 
+* +******************************************************************************/ + +#ifndef XSYSACE_L_H /* prevent circular inclusions */ +#define XSYSACE_L_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xio.h" + +/************************** Constant Definitions *****************************/ + +/* + * Constant used to align the register offsets to the proper address. This was + * used during development to handle both byte-addressable (alignment=1) and + * word addressable (alignment=4) registers. The #ifndef allows the user to + * modify this at compile-time. + */ +#ifndef XSA_ADDR_ALIGN +#define XSA_ADDR_ALIGN 1 +#endif + +/** @name Register Offsets + * System ACE register offsets + * @{ + */ +#define XSA_BMR_OFFSET (XSA_ADDR_ALIGN * 0) /**< Bus mode (BUSMODEREG) */ +#define XSA_SR_OFFSET (XSA_ADDR_ALIGN * 4) /**< Status (STATUSREG) */ +#define XSA_ER_OFFSET (XSA_ADDR_ALIGN * 8) /**< Error (ERRORREG) */ +#define XSA_CLR_OFFSET (XSA_ADDR_ALIGN * 12) /**< Config LBA (CFGLBAREG) */ +#define XSA_MLR_OFFSET (XSA_ADDR_ALIGN * 16) /**< MPU LBA (MPULBAREG) */ +#define XSA_SCCR_OFFSET (XSA_ADDR_ALIGN * 20) /**< Sector cnt (SECCNTCMDREG) */ +#define XSA_VR_OFFSET (XSA_ADDR_ALIGN * 22) /**< Version (VERSIONREG) */ +#define XSA_CR_OFFSET (XSA_ADDR_ALIGN * 24) /**< Control (CONTROLREG) */ +#define XSA_FSR_OFFSET (XSA_ADDR_ALIGN * 28) /**< FAT status (FATSTATREG) */ +#define XSA_DBR_OFFSET (XSA_ADDR_ALIGN * 64) /**< Data buffer (DATABUFREG) */ +/*@}*/ + +/* + * Bus Mode Register masks + */ +#define XSA_BMR_16BIT_MASK 0x0101 /**< 16-bit access to ACE controller */ + +/** @name Status Values + * Status Register masks + * @{ + */ +#define XSA_SR_CFGLOCK_MASK 0x00000001 /**< Config port lock status */ +#define XSA_SR_MPULOCK_MASK 0x00000002 /**< MPU port lock status */ +#define XSA_SR_CFGERROR_MASK 0x00000004 /**< Config port error status */ +#define XSA_SR_CFCERROR_MASK 0x00000008 /**< CF error status */ +#define XSA_SR_CFDETECT_MASK 0x00000010 /**< CF detect flag */ +#define XSA_SR_DATABUFRDY_MASK 0x00000020 /**< Data buffer ready status */ +#define XSA_SR_DATABUFMODE_MASK 0x00000040 /**< Data buffer mode status */ +#define XSA_SR_CFGDONE_MASK 0x00000080 /**< Configuration done status */ +#define XSA_SR_RDYFORCMD_MASK 0x00000100 /**< Ready for CF command */ +#define XSA_SR_CFGMODE_MASK 0x00000200 /**< Configuration mode status */ +#define XSA_SR_CFGADDR_MASK 0x0000E000 /**< Configuration address */ +#define XSA_SR_CFBSY_MASK 0x00020000 /**< CF busy (BSY bit) */ +#define XSA_SR_CFRDY_MASK 0x00040000 /**< CF ready (RDY bit) */ +#define XSA_SR_CFDWF_MASK 0x00080000 /**< CF data write fault (DWF bit) */ +#define XSA_SR_CFDSC_MASK 0x00100000 /**< CF ready (DSC bit) */ +#define XSA_SR_CFDRQ_MASK 0x00200000 /**< CF data request (DRQ) */ +#define XSA_SR_CFCORR_MASK 0x00400000 /**< CF correctable error (CORR bit) */ +#define XSA_SR_CFERR_MASK 0x00800000 /**< CF error (ERR bit) */ +/*@}*/ + +/** @name Error Values + * Error Register masks. + * @{ + */ +#define XSA_ER_CARD_RESET 0x00000001 /**< CF card failed to reset */ +#define XSA_ER_CARD_READY 0x00000002 /**< CF card failed to ready */ +#define XSA_ER_CARD_READ 0x00000004 /**< CF read command failed */ +#define XSA_ER_CARD_WRITE 0x00000008 /**< CF write command failed */ +#define XSA_ER_SECTOR_READY 0x00000010 /**< CF sector failed to ready */ +#define XSA_ER_CFG_ADDR 0x00000020 /**< Cfg address is invalid */ +#define XSA_ER_CFG_FAIL 0x00000040 /**< Failed to configure a device */ +#define XSA_ER_CFG_READ 0x00000080 /**< Cfg read of CF failed */ +#define XSA_ER_CFG_INSTR 0x00000100 /**< Invalid instruction during cfg */ +#define XSA_ER_CFG_INIT 0x00000200 /**< CFGINIT pin error - did not + * go high within 500ms of start */ +#define XSA_ER_RESERVED 0x00000400 /**< reserved */ +#define XSA_ER_BAD_BLOCK 0x00000800 /**< CF bad block detected */ +#define XSA_ER_UNCORRECTABLE 0x00001000 /**< CF uncorrectable error */ +#define XSA_ER_SECTOR_ID 0x00002000 /**< CF sector ID not found */ +#define XSA_ER_ABORT 0x00004000 /**< CF command aborted */ +#define XSA_ER_GENERAL 0x00008000 /**< CF general error */ +/*@}*/ + +/** + * Config LBA Register - address mask + */ +#define XSA_CLR_LBA_MASK 0x0FFFFFFF /* Logical Block Address mask */ + +/** + * MPU LBA Register - address mask + */ +#define XSA_MLR_LBA_MASK 0x0FFFFFFF /* Logical Block Address mask */ + +/** @name Sector Cound/Command Values + * Sector Count Command Register masks + * @{ + */ +#define XSA_SCCR_COUNT_MASK 0x00FF /**< Sector count mask */ +#define XSA_SCCR_RESET_MASK 0x0100 /**< Reset CF card command */ +#define XSA_SCCR_IDENTIFY_MASK 0x0200 /**< Identify CF card command */ +#define XSA_SCCR_READDATA_MASK 0x0300 /**< Read CF card command */ +#define XSA_SCCR_WRITEDATA_MASK 0x0400 /**< Write CF card command */ +#define XSA_SCCR_ABORT_MASK 0x0600 /**< Abort CF command */ +#define XSA_SCCR_CMD_MASK 0x0700 /**< Command mask */ +/*@}*/ + +/* + * Version Register masks + */ +#define XSA_VR_BUILD_MASK 0x00FF /* Revision/build number */ +#define XSA_VR_MINOR_MASK 0x0F00 /* Minor version number */ +#define XSA_VR_MAJOR_MASK 0xF000 /* Major version number */ + +/** @name Control Values + * Control Register masks + * @{ + */ +#define XSA_CR_FORCELOCK_MASK 0x00000001 /**< Force lock request */ +#define XSA_CR_LOCKREQ_MASK 0x00000002 /**< MPU lock request */ +#define XSA_CR_FORCECFGADDR_MASK 0x00000004 /**< Force CFG address */ +#define XSA_CR_FORCECFGMODE_MASK 0x00000008 /**< Force CFG mode */ +#define XSA_CR_CFGMODE_MASK 0x00000010 /**< CFG mode */ +#define XSA_CR_CFGSTART_MASK 0x00000020 /**< CFG start */ +#define XSA_CR_CFGSEL_MASK 0x00000040 /**< CFG select */ +#define XSA_CR_CFGRESET_MASK 0x00000080 /**< CFG reset */ +#define XSA_CR_DATARDYIRQ_MASK 0x00000100 /**< Enable data ready IRQ */ +#define XSA_CR_ERRORIRQ_MASK 0x00000200 /**< Enable error IRQ */ +#define XSA_CR_CFGDONEIRQ_MASK 0x00000400 /**< Enable CFG done IRQ */ +#define XSA_CR_RESETIRQ_MASK 0x00000800 /**< Reset IRQ line */ +#define XSA_CR_CFGPROG_MASK 0x00001000 /**< Inverted CFGPROG pin */ +#define XSA_CR_CFGADDR_MASK 0x0000E000 /**< Config address mask */ +#define XSA_CR_CFGADDR_SHIFT 13 /**< Config address shift */ +/*@}*/ + +/** @name FAT Status + * + * FAT filesystem status masks. The first valid partition of the CF + * is a FAT partition. + * @{ + */ +#define XSA_FAT_VALID_BOOT_REC 0x0001 /**< Valid master boot record */ +#define XSA_FAT_VALID_PART_REC 0x0002 /**< Valid partition boot record */ +#define XSA_FAT_12_BOOT_REC 0x0004 /**< FAT12 in master boot rec */ +#define XSA_FAT_12_PART_REC 0x0008 /**< FAT12 in parition boot rec */ +#define XSA_FAT_16_BOOT_REC 0x0010 /**< FAT16 in master boot rec */ +#define XSA_FAT_16_PART_REC 0x0020 /**< FAT16 in partition boot rec */ +#define XSA_FAT_12_CALC 0x0040 /**< Calculated FAT12 from clusters */ +#define XSA_FAT_16_CALC 0x0080 /**< Calculated FAT16 from clusters */ +/*@}*/ + +#define XSA_DATA_BUFFER_SIZE 32 /**< Size of System ACE data buffer */ +#define XSA_CF_SECTOR_SIZE 512 /**< Number of bytes in a CF sector */ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/***************************************************************************** +* +* Low-level driver macros and functions. The list below provides signatures +* to help the user use the macros. +* +* u32 XSysAce_mGetControlReg(u32 BaseAddress) +* void XSysAce_mSetControlReg(u32 BaseAddress, u32 Data) +* void XSysAce_mOrControlReg(u32 BaseAddress, u32 Data) +* void XSysAce_mAndControlReg(u32 BaseAddress, u32 Data) +* u32 XSysAce_mGetErrorReg(u32 BaseAddress) +* u32 XSysAce_mGetStatusReg(u32 BaseAddress) +* +* void XSysAce_mSetCfgAddr(u32 BaseAddress, unsigned int Address) +* void XSysAce_mWaitForLock(u32 BaseAddress) +* void XSysAce_mEnableIntr(u32 BaseAddress, u32 Mask) +* void XSysAce_mDisableIntr(u32 BaseAddress, u32 Mask) +* +* u32 XSysAce_mIsReadyForCmd(u32 BaseAddress) +* u32 XSysAce_mIsCfgDone(u32 BaseAddress) +* u32 XSysAce_mIsMpuLocked(u32 BaseAddress) +* u32 XSysAce_mIsIntrEnabled(u32 BaseAddress) +* +*****************************************************************************/ + +/*****************************************************************************/ +/** +* +* Get the contents of the control register. +* +* @param BaseAddress is the base address of the device. +* +* @return The 32-bit value of the control register. +* +* @note None. +* +******************************************************************************/ +#define XSysAce_mGetControlReg(BaseAddress) \ + XSysAce_RegRead32((BaseAddress) + XSA_CR_OFFSET) + +/*****************************************************************************/ +/** +* +* Set the contents of the control register. +* +* @param BaseAddress is the base address of the device. +* @param Data is the 32-bit value to write to the register. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +#define XSysAce_mSetControlReg(BaseAddress, Data) \ + XSysAce_RegWrite32((BaseAddress) + XSA_CR_OFFSET, (Data)) + +/*****************************************************************************/ +/** +* +* Set the contents of the control register to the value specified OR'ed with +* its current contents. +* +* @param BaseAddress is the base address of the device. +* @param Data is the 32-bit value to OR with the register. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +#define XSysAce_mOrControlReg(BaseAddress, Data) \ + XSysAce_mSetControlReg((BaseAddress), \ + XSysAce_mGetControlReg(BaseAddress) | (Data)) + +/*****************************************************************************/ +/** +* +* Set the contents of the control register to the value specified AND'ed with +* its current contents. +* +* @param BaseAddress is the base address of the device. +* @param Data is the 32-bit value to AND with the register. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +#define XSysAce_mAndControlReg(BaseAddress, Data) \ + XSysAce_mSetControlReg((BaseAddress), \ + XSysAce_mGetControlReg(BaseAddress) & (Data)) + +/*****************************************************************************/ +/** +* +* Get the contents of the error register. +* +* @param BaseAddress is the base address of the device. +* +* @return The 32-bit value of the register. +* +* @note None. +* +******************************************************************************/ +#define XSysAce_mGetErrorReg(BaseAddress) \ + XSysAce_RegRead32((BaseAddress) + XSA_ER_OFFSET) + +/*****************************************************************************/ +/** +* +* Get the contents of the status register. +* +* @param BaseAddress is the base address of the device. +* +* @return The 32-bit value of the register. +* +* @note None. +* +******************************************************************************/ +#define XSysAce_mGetStatusReg(BaseAddress) \ + XSysAce_RegRead32((BaseAddress) + XSA_SR_OFFSET) + +/*****************************************************************************/ +/** +* +* Set the configuration address, or file, of the CompactFlash. This address +* indicates which .ace bitstream to use to configure the target FPGA chain. +* +* @param BaseAddress is the base address of the device. +* @param Address ranges from 0 to 7 and represents the eight possible .ace +* bitstreams that can reside on the CompactFlash. +* +* @return None. +* +* @note Used cryptic var names to avoid conflict with caller's var names. +* +******************************************************************************/ +#define XSysAce_mSetCfgAddr(BaseAddress, Address) \ +{ \ + u32 A66rMask = ((Address) << XSA_CR_CFGADDR_SHIFT) & XSA_CR_CFGADDR_MASK; \ + u32 C0ntr0l = XSysAce_mGetControlReg(BaseAddress); \ + C0ntr0l &= ~XSA_CR_CFGADDR_MASK; /* clear current address */ \ + C0ntr0l |= (A66rMask | XSA_CR_FORCECFGADDR_MASK); \ + XSysAce_mSetControlReg((BaseAddress), C0ntr0l); \ +} + +/*****************************************************************************/ +/** +* +* Request then wait for the MPU lock. This is not a forced lock, so we must +* contend with the configuration controller. +* +* @param BaseAddress is the base address of the device. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +#define XSysAce_mWaitForLock(BaseAddress) \ +{ \ + XSysAce_mOrControlReg((BaseAddress), XSA_CR_LOCKREQ_MASK); \ + while ((XSysAce_mGetStatusReg(BaseAddress) & XSA_SR_MPULOCK_MASK) == 0); \ +} + +/*****************************************************************************/ +/** +* +* Enable ACE controller interrupts. +* +* @param BaseAddress is the base address of the device. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +#define XSysAce_mEnableIntr(BaseAddress, Mask) \ + XSysAce_mOrControlReg((BaseAddress), (Mask)); + +/*****************************************************************************/ +/** +* +* Disable ACE controller interrupts. +* +* @param BaseAddress is the base address of the device. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +#define XSysAce_mDisableIntr(BaseAddress, Mask) \ + XSysAce_mAndControlReg((BaseAddress), ~(Mask)); + +/*****************************************************************************/ +/** +* +* Is the CompactFlash ready for a command? +* +* @param BaseAddress is the base address of the device. +* +* @return TRUE if it is ready, FALSE otherwise. +* +* @note None. +* +******************************************************************************/ +#define XSysAce_mIsReadyForCmd(BaseAddress) \ + (XSysAce_mGetStatusReg(BaseAddress) & XSA_SR_RDYFORCMD_MASK) + +/*****************************************************************************/ +/** +* +* Is the ACE controller locked for MPU access? +* +* @param BaseAddress is the base address of the device. +* +* @return TRUE if it is locked, FALSE otherwise. +* +* @note None. +* +******************************************************************************/ +#define XSysAce_mIsMpuLocked(BaseAddress) \ + (XSysAce_mGetStatusReg(BaseAddress) & XSA_SR_MPULOCK_MASK) + +/*****************************************************************************/ +/** +* +* Is the CompactFlash configuration of the target FPGA chain complete? +* +* @param BaseAddress is the base address of the device. +* +* @return TRUE if it is ready, FALSE otherwise. +* +* @note None. +* +******************************************************************************/ +#define XSysAce_mIsCfgDone(BaseAddress) \ + (XSysAce_mGetStatusReg(BaseAddress) & XSA_SR_CFGDONE_MASK) + +/*****************************************************************************/ +/** +* +* Have interrupts been enabled by the user? We look for the interrupt reset +* bit to be clear (meaning interrupts are armed, even though none may be +* individually enabled). +* +* @param BaseAddress is the base address of the device. +* +* @return TRUE if it is enabled, FALSE otherwise. +* +* @note None. +* +******************************************************************************/ +#define XSysAce_mIsIntrEnabled(BaseAddress) \ + ((XSysAce_mGetControlReg(BaseAddress) & XSA_CR_RESETIRQ_MASK) == 0) + +/************************** Function Prototypes ******************************/ + +int XSysAce_ReadSector(u32 BaseAddress, u32 SectorId, u8 * BufferPtr); +int XSysAce_WriteSector(u32 BaseAddress, u32 SectorId, u8 * BufferPtr); + +/* + * Utility functions to read and write registers and data buffer + */ +u32 XSysAce_RegRead32(u32 Address); +u16 XSysAce_RegRead16(u32 Address); +void XSysAce_RegWrite32(u32 Address, u32 Data); +void XSysAce_RegWrite16(u32 Address, u16 Data); + +int XSysAce_ReadDataBuffer(u32 BaseAddress, u8 * BufferPtr, int NumBytes); +int XSysAce_WriteDataBuffer(u32 BaseAddress, u8 * BufferPtr, int NumBytes); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/drivers/char/Config.in linuxppc-2.4/drivers/char/Config.in --- linux-2.4.22/drivers/char/Config.in 2003-08-26 14:24:58.000000000 +1000 +++ linuxppc-2.4/drivers/char/Config.in 2003-08-05 09:59:45.000000000 +1000 @@ -227,6 +227,9 @@ fi tristate ' Eurotech CPU-1220/1410 Watchdog Timer' CONFIG_EUROTECH_WDT tristate ' IB700 SBC Watchdog Timer' CONFIG_IB700_WDT + if [ "$CONFIG_40x" = "y" ]; then + tristate ' IBM PPC 405 Watchdog Timer' CONFIG_PPC405_WDT + fi tristate ' ICP ELectronics Wafer 5823 Watchdog' CONFIG_WAFER_WDT if [ "$CONFIG_SGI_IP22" = "y" ]; then dep_tristate ' Indy/I2 Hardware Watchdog' CONFIG_INDYDOG $CONFIG_SGI_IP22 @@ -283,6 +286,9 @@ if [ "$CONFIG_EXPERIMENTAL" = "y" -a "$CONFIG_X86" = "y" -a "$CONFIG_X86_64" != "y" ]; then dep_tristate 'Sony Vaio Programmable I/O Control Device support (EXPERIMENTAL)' CONFIG_SONYPI $CONFIG_PCI fi +if [ "$CONFIG_IBM_OCP" = "y" ]; then + tristate 'IBM GPIO' CONFIG_IBM_OCP_GPIO +fi mainmenu_option next_comment comment 'Ftape, the floppy tape device driver' @@ -345,4 +351,14 @@ tristate 'ACP Modem (Mwave) support' CONFIG_MWAVE fi +if [ "$CONFIG_XILINX_OCP" = "y" ]; then + tristate 'Xilinx on-chip GPIO' CONFIG_XILINX_GPIO + tristate 'Xilinx touchscreen' CONFIG_XILINX_TS +fi + +if [ "$CONFIG_ARCTIC2" = "y" ]; then + tristate 'Arctic II Semtech Touchscreen controller' CONFIG_IBMTS_SEMTECH + tristate 'Arctic II miscellaneous support (buttons, frontlight)' CONFIG_ARCTIC2_MISC +fi + endmenu diff -urN -X bkexcl linux-2.4.22/drivers/char/Makefile linuxppc-2.4/drivers/char/Makefile --- linux-2.4.22/drivers/char/Makefile 2003-07-05 10:50:40.000000000 +1000 +++ linuxppc-2.4/drivers/char/Makefile 2003-07-05 10:57:57.000000000 +1000 @@ -24,7 +24,8 @@ export-objs := busmouse.o console.o keyboard.o sysrq.o \ misc.o pty.o random.o selection.o serial.o \ sonypi.o tty_io.o tty_ioctl.o generic_serial.o \ - au1000_gpio.o hp_psaux.o nvram.o scx200.o + au1000_gpio.o hp_psaux.o nvram.o scx200.o \ + ibm_ocp_gpio.o mod-subdirs := joystick ftape drm drm-4.0 pcmcia @@ -302,6 +303,24 @@ obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_AMD7XX_TCO) += amd7xx_tco.o +obj-$(CONFIG_PPC405_WDT) += ppc405_wdt.o +obj-$(CONFIG_IBM_OCP_GPIO) += ibm_ocp_gpio.o +obj-$(CONFIG_IBMTS_SEMTECH) += ibmts_semtech.o +obj-$(CONFIG_ARCTIC2_MISC) += arctic2_misc.o + +mod-subdirs += xilinx_gpio xilinx_ts +subdir-$(CONFIG_XILINX_GPIO) += xilinx_gpio +subdir-$(CONFIG_XILINX_TS) += xilinx_ts +obj-$(CONFIG_XILINX_GPIO) += xilinx_gpio/xilinx_gpio.o +obj-$(CONFIG_XILINX_TS) += xilinx_ts/xilinx_ts.o +ifeq ($(CONFIG_VIRTEX_II_PRO),y) + ifeq ($(CONFIG_VT),y) + ifeq ($(CONFIG_PC_KEYBOARD),y) + subdir-$(CONFIG_VT) += xilinx_keyb + obj-$(CONFIG_VT) += xilinx_keyb/xilinx_keyb.o + endif + endif +endif subdir-$(CONFIG_MWAVE) += mwave ifeq ($(CONFIG_MWAVE),y) diff -urN -X bkexcl linux-2.4.22/drivers/char/arctic2_misc.c linuxppc-2.4/drivers/char/arctic2_misc.c --- linux-2.4.22/drivers/char/arctic2_misc.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/arctic2_misc.c 2003-06-25 21:15:52.000000000 +1000 @@ -0,0 +1,668 @@ +/* IBM Arctic-2 miscellaneous device support + * + * Char driver for /dev/arcmsc, which handles the on-case buttons, + * frontlight control, programmable LED and dock/undock detection (so + * far) + * + * 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 + * + * Copyright (C) 2002 K. Iwasaki, Ken Inoue, David Gibson IBM Corporation. + * + * Ken Inoue + * IBM Thomas J. Watson Research Center + * + * + * David Gibson + * IBM OzLabs, Canberra, Australia. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#define ARCTIC2_MISC_MINOR 117 + +#define ARCTIC_FPGA_INT 27 +#define ARCTIC_FPGA_INT_MASK (0x80000000 >> ARCTIC_FPGA_INT) + +#define ARCTIC2_FPGA_IRQS (ARCTIC2_FPGA_IRQ_PWR | ARCTIC2_FPGA_IRQ_MIC \ + | ARCTIC2_FPGA_IRQ_BTN) + +static struct i2c_adapter *arctic_i2c = NULL; + +#define DBG(x) + +/* Our driver might be initialized before the IIC driver is, + * so we lazily find the I2C adapter */ + +static int find_i2c_adapter() +{ + if (! arctic_i2c) { + struct ocp_device *ocp_iic; + + ocp_iic = ocp_find_device(OCP_VENDOR_IBM, OCP_FUNC_IIC, 0); + if (!ocp_iic) + return 0; + + /* + * FIXME: This assumes that there's a "struct i2c_adapter" + * as the first thing in the driver data, which is + * private to i2c-ibm-iic. + */ + + arctic_i2c = (struct i2c_adapter *) + ocp_get_drvdata(ocp_iic); + } + + return 1; +} + +/* Simple wrappers for i2c_transfer() - the ones in the I2C layer + * (i2c_master_send() etc.) impose a stupid structure on the + * driver. */ +static int i2c_write(u16 addr, const char *buf, int len) +{ + struct i2c_msg msg = { + .addr = addr, + .flags = 0, + .len = len, + .buf = (char *)buf, + }; + + /* Our driver might be initialized before the IIC driver is, + * so we lazily find the I2C adapter */ + if (! find_i2c_adapter()) + return -EBUSY; + + return i2c_transfer(arctic_i2c, &msg, 1); +} + +static int i2c_writeread(u16 addr, const char *wbuf, size_t wlen, + char *rbuf, size_t rlen) +{ + struct i2c_msg msg[2] = { + { .addr = addr, + .flags = 0, + .len = wlen, + .buf = (char *)wbuf, + }, + { .addr = addr, + .flags = I2C_M_RD, + .len = rlen, + .buf = rbuf + }, + }; + + /* Our driver might be initialized before the IIC driver is, + * so we lazily find the I2C adapter */ + if (! find_i2c_adapter()) + return -EBUSY; + + return i2c_transfer(arctic_i2c, msg, 2); +} + +/************************************************************************/ +/* Button handling code */ +/************************************************************************/ + +#define ARCTIC_BUTTON_BUF_SIZE 256 +#define BTN_DELAY (HZ/10) /* poll 10 times per second */ + +struct arctic2_button_state { + struct fasync_struct *fasync; + wait_queue_head_t wq; + + /* Last known values (used for polling) */ + u8 btn, mway; + + struct timer_list timer; + + spinlock_t qlock; + unsigned long qhead; + unsigned char buf[ARCTIC_BUTTON_BUF_SIZE]; +}; + +static struct arctic2_button_state btn_state; + +static inline int queue_empty(loff_t off) +{ + return (off >= btn_state.qhead); +} + +static void put_in_queue(u8 data) +{ + unsigned long flags; + unsigned long pos; + + spin_lock_irqsave(&btn_state.qlock, flags); + + pos = btn_state.qhead % ARCTIC_BUTTON_BUF_SIZE; + + btn_state.buf[pos] = data; + btn_state.qhead++; + + kill_fasync(&btn_state.fasync, SIGIO, POLL_IN); + wake_up_interruptible(&btn_state.wq); + + spin_unlock_irqrestore(&btn_state.qlock, flags); +} + +static int get_from_queue(loff_t *tail, unsigned char *data) +{ + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&btn_state.qlock, flags); + + /* If the reader didn't keep up, tough, they lose events */ + if (*tail + ARCTIC_BUTTON_BUF_SIZE < btn_state.qhead) + *tail = btn_state.qhead - ARCTIC_BUTTON_BUF_SIZE; + + if (*tail < btn_state.qhead) { /* non-empty */ + *data = btn_state.buf[*tail % ARCTIC_BUTTON_BUF_SIZE]; + (*tail)++; + } else { + ret = 0; + } + + spin_unlock_irqrestore(&btn_state.qlock, flags); + + return ret; +} + +#ifndef CONFIG_405LP_PM_BUTTON +static void do_powerbutton(void) +{ + /* Power off button pressed */ + int i; + + /* FIXME: we can't turn off while the power button is still + * depressed - the hardware will just switch us on agan. It + * would be nice to wait for the release to do the powerdown, + * but apparently jitter makes this difficult. Instead we + * just put a 3s delay between blanking the display and + * turning off the unit, and hope the user releases the button + * in the meantime */ + + /* Change display active area to just 1 pixel */ + mtdcr(DCRN_LCD0_CFGADDR, DCRN_LCD0_ADSR); + mtdcr(DCRN_LCD0_CFGDATA, 0x00010001); + /* Then wait for at least 1 screen refresh cycle */ + for (i = 0; i < 1000; i++) { + udelay(1000); + } /* 1s should be enough... */ + + /* Disable LCD */ + mtdcr(DCRN_LCD0_CFGADDR, DCRN_LCD0_DER); + mtdcr(DCRN_LCD0_CFGDATA, 0); + udelay(100); + /* Power off LCD */ + arctic2_set_lcdpower(0); + + /* FIXME: do we need this stuff...? */ + asm volatile("sync"); + + for (i = 0; i < 2000; i++) { + udelay(1000); /* 1 ms */ + } /* 2 sec delay */ + + asm volatile("sync"); + + arctic2_poweroff(); /* never returns */ +} +#endif /* CONFIG_405LP_PM_BUTTON */ + +static void poll_buttons(unsigned long data __attribute__((unused))) +{ + u8 btn, mway; + + btn = readb(ARCTIC2_FPGA_BUTTONS) & 0x3F; + mway = readb(ARCTIC2_FPGA_MULTIWAY) & 0x1F; + + if (btn || mway) { + /* At least one button is pressed */ +#ifndef CONFIG_405LP_PM_BUTTON + if ((btn & ~btn_state.btn) & ARCTIC2_FPGA_BTN_PWR) + do_powerbutton(); /* never returns */ +#endif /* CONFIG_405LP_PM_BUTTON */ + + if ((btn & ~btn_state.btn) & 0x01) + put_in_queue(0x03); + else if ((btn & ~btn_state.btn) & 0x02) + put_in_queue(0x01); + else if ((btn & ~btn_state.btn) & 0x04) + put_in_queue(0x02); + else if ((btn & ~btn_state.btn) & 0x08) + put_in_queue(0x04); + else if ((btn & ~btn_state.btn) & ARCTIC2_FPGA_BTN_MIC) + put_in_queue(0x05); + + if ((mway & ~btn_state.mway) + & ARCTIC2_FPGA_MULTIWAY_PUSH) { + /* We prioritize push, so it gets handled in + * preference to any jiggling of the lever + * that happens at the same time */ + put_in_queue(0x06); + } else { + /* We translate the odd 45deg rotated switches + * into the cardinal directions. Have to + * handle the masking of the old value a bit + * differently for that. */ + if ((mway == ARCTIC2_FPGA_MULTIWAY_N) + && !(btn_state.mway == ARCTIC2_FPGA_MULTIWAY_N)) + put_in_queue(0x07); + if ((mway == ARCTIC2_FPGA_MULTIWAY_E) + && !(btn_state.mway == ARCTIC2_FPGA_MULTIWAY_E)) + put_in_queue(0x08); + if ((mway == ARCTIC2_FPGA_MULTIWAY_S) + && !(btn_state.mway == ARCTIC2_FPGA_MULTIWAY_S)) + put_in_queue(0x09); + if ((mway == ARCTIC2_FPGA_MULTIWAY_W) + && !(btn_state.mway == ARCTIC2_FPGA_MULTIWAY_W)) + put_in_queue(0x0a); + } + + /* While a button is pressed, the FPGA continues to + * assert the interrupt line, so the only way to look + * for more presses (or releases) is to poll :-( */ + writeb(0, ARCTIC2_FPGA_IRQ_ENABLE); + btn_state.timer.expires = jiffies + BTN_DELAY; + add_timer(&btn_state.timer); + } else { + /* Nothing pressed any more, re-enable the interrupt + * and stop polling */ + writeb(ARCTIC2_FPGA_IRQS, ARCTIC2_FPGA_IRQ_ENABLE); + } + + btn_state.btn = btn; + btn_state.mway = btn; +} + +static void arctic_button_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + poll_buttons(0); + /* TODO: TCPA (assume shared interrupt) */ +} + +static int arctic_button_fasync(int fd, struct file *filp, int on) +{ + int ret; + + ret = fasync_helper(fd, filp, on, &btn_state.fasync); + if (ret < 0) + return ret; + return 0; +} + +static ssize_t arctic_button_read(struct file *filp, char *buf, size_t count, + loff_t *pos) +{ + ssize_t bytes_read = 0; + int ret; + + if (queue_empty(filp->f_pos)) { /* wait for an event */ + if (filp->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + + ret = wait_event_interruptible(btn_state.wq, + !queue_empty(filp->f_pos)); + if (ret) + return ret; + } + + while (bytes_read < count) { + u8 c; + + if (! get_from_queue(&filp->f_pos, &c)) + break; + + if (put_user(c, buf + bytes_read)) + return -EFAULT; + + bytes_read++; + } + + return bytes_read; +} + +static unsigned int arctic_button_poll(struct file *filp, poll_table *wait) +{ + poll_wait(filp, &btn_state.wq, wait); + if (!queue_empty(filp->f_pos)) + return POLLIN | POLLRDNORM; + return 0; +} + +/************************************************************************/ +/* Frontlight handling code */ +/************************************************************************/ + +#define FRONTLIGHT_I2C_ADDR 0x28 + +static spinlock_t arctic2_fl_lock = SPIN_LOCK_UNLOCKED; +static u8 arctic2_frontlight_status = 0x20; /* Maximum brightness */ + +static int set_frontlight(u8 val) +{ + int ret; + + spin_lock_irq(&arctic2_fl_lock); + + if ( (val > 3) && (arctic2_frontlight_status < 4) ) { + /* Turn on the LCD */ + mtdcri(DCRN_LCD0, DER, 0); + udelay(1000); /* 1ms delay */ + } + + ret = i2c_write(FRONTLIGHT_I2C_ADDR, &val, 1); + if (ret >= 0) + ret = 0; + + if ( (val > 3) && (arctic2_frontlight_status < 4) ) { + udelay(1000); /* 1ms delay */ + mtdcri(DCRN_LCD0, DER, 1); + } + + arctic2_frontlight_status = val; + + spin_unlock_irq(&arctic2_fl_lock); + + return ret; +} + +static ssize_t arctic2_frontlight_write(struct file *filp, const char *buf, + size_t count, loff_t *ppos) +{ + u8 data; + int err; + + if (count == 0) + return 0; + + if (get_user(data, buf)) + return -EFAULT; + + /* clamp */ + if (data > 0x1f) + data = 0x1f; + + err = set_frontlight(data); + if (err) + return err; + + return 1; /* always process 1 byte at a time */ +} + +/************************************************************************/ +/* Programmable LED control */ +/************************************************************************/ + +#define LED_CTRL_ADDR 0x0D +#define LED_RED_ADDR 0x2D +#define LED_GREEN_ADDR 0x4D +#define LED_BLUE_ADDR 0x6D + +static void write_led_reg(u8 high, u8 low, u8 addr) +{ + writeb(high, ARCTIC2_FPGA_LED_DATA_HI); + writeb(low, ARCTIC2_FPGA_LED_DATA_LOW); + writeb(addr, ARCTIC2_FPGA_LED_ADDR); + writeb(0x01, ARCTIC2_FPGA_LED_CTRL); + /* FIXME: should we wait for the sending bit to clear? */ +} + +/************************************************************************/ +/* Battery fuel guage */ +/************************************************************************/ + +#define FUELGAUGE_I2C_ADDR 0x48 + +static int read_battery_voltage(void) +{ + int ret; + u8 zero = 0x00; /* command byte to write to the I2C bus */ + u16 buf[4]; + + memset(buf, 0, sizeof(buf)); + + /* The ADS7823 makes and returns 4 samples */ + ret = i2c_writeread(FUELGAUGE_I2C_ADDR, &zero, 1, + (char *)buf, sizeof(buf)); + if (ret < 0) + return ret; + + printk(KERN_DEBUG "arctic2_misc: (%d) fuelguage returned: %hx/%hx/%hx/%hx\n", + ret, + be16_to_cpu(buf[0]), be16_to_cpu(buf[1]), + be16_to_cpu(buf[2]), be16_to_cpu(buf[3])); + + return be16_to_cpu(buf[0]); +} + +/************************************************************************/ +/* Common device code */ +/************************************************************************/ + +static int arctic2_misc_open(struct inode *inode, struct file *filp) +{ + filp->f_pos = 0; + return 0; +} + +static int arctic2_misc_close(struct inode *inode, struct file *filp) +{ + return 0; +} + +static int arctic_misc_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + /* So far all ioctl()s require write permission to the file */ + if (! (filp->f_mode & FMODE_WRITE)) + return -EPERM; + + switch (cmd) { + /* frontlight ioctl()s */ + case 0: /* backwards compatibility with old versions */ + case ARCTIC2_MISC_FLSTATUS: + ret = mfdcri(DCRN_LCD0, DER) << 8; + ret |= arctic2_frontlight_status; + break; + + /* power ioctls()s */ + case 1: /* backwards compatibility with old versions */ + case ARCTIC2_MISC_POWEROFF: + arctic2_poweroff(); + break; + + case ARCTIC2_MISC_PWRSTATUS: { + int voltage = read_battery_voltage(); + u32 status; + + status = voltage & 0xfff; + + if (put_user(status, (u32 *)arg)) + ret = -EFAULT; + + break; + } + + /* multicolor LED ioctl()s */ + case ARCTIC2_MISC_LEDSTANDBY: { + int tmp; + u8 reg; + + if (get_user(tmp, (int *)arg)) + return -EFAULT; + + if ( (tmp != 0) && (tmp != 1) ) + return -EINVAL; + + reg = tmp << 1; + writeb(reg, ARCTIC2_FPGA_LED_CTRL); + break; + } + + case ARCTIC2_MISC_LEDCTRL: { + u32 tmp; + + if (get_user(tmp, (u32 *)arg)) { + ret = -EFAULT; + break; + } + + if (tmp & ~0xff) { + ret = -EINVAL; /* overkill? */ + break; + } + + write_led_reg((tmp >> 8) & 0xff, tmp & 0xff, LED_CTRL_ADDR); + break; + } + + case ARCTIC2_MISC_LEDTIMING: { + u32 tmp; + u8 on, off, color; + + if (get_user(tmp, (u32 *)arg)) { + ret = -EFAULT; + break; + } + + if (tmp & ~0xff007f7f) { + ret = -EINVAL; /* overkill? */ + break; + } + + color = (tmp & ARCTIC2_LED_TIMING_COLOR) >> 24; + on = (tmp & ARCTIC2_LED_TIMING_ON) >> 8; + off = (tmp & ARCTIC2_LED_TIMING_OFF); + + if ( (color > 2) || (on > 127) || (off > 127) || (on >= off) ) + return -EINVAL; /* overkill? */ + + write_led_reg(on, off, LED_CTRL_ADDR + ((color+1) << 5)); + break; + } + + default: + ret = -ENOIOCTLCMD; + } + + return ret; +} + +static struct file_operations arctic_misc_fops = { + .open = arctic2_misc_open, + .release = arctic2_misc_close, + .read = arctic_button_read, + .write = arctic2_frontlight_write, + .poll = arctic_button_poll, + .ioctl = arctic_misc_ioctl, + .fasync = arctic_button_fasync, +}; + +static struct miscdevice arctic_button_miscdev = { + ARCTIC2_MISC_MINOR, "arcmsc", &arctic_misc_fops +}; + +MODULE_AUTHOR("K.Iwasaki, K. Inoue, David Gibson"); +MODULE_DESCRIPTION("Arctic-2 miscellaneous device driver"); + +int __init init_arctic2_misc(void) +{ + int irq_requested = 0; + int err; + + /* we need to setup all the same registers bios should have */ + /* already setup for us because there is a chance that the */ + /* user could have a different bios that isn't just right... */ + + /* set IRQ to level-triggered, high */ + mtdcr(DCRN_UIC0_TR, mfdcr(DCRN_UIC0_TR) & ~ARCTIC_FPGA_INT_MASK); + mtdcr(DCRN_UIC0_PR, mfdcr(DCRN_UIC0_PR) | ARCTIC_FPGA_INT_MASK); + mtdcr(DCRN_UIC0_SR, ARCTIC_FPGA_INT_MASK); + + memset(&btn_state, 0, sizeof(btn_state)); + /* don't forget to init the waitqueue or else we oops on read */ + init_waitqueue_head(&btn_state.wq); + spin_lock_init(&btn_state.qlock); + + init_timer(&btn_state.timer); + btn_state.timer.function = poll_buttons; + + err = request_irq(ARCTIC_FPGA_INT, arctic_button_irq, SA_SHIRQ, + "arctic2_misc", NULL); + if (err) { + printk(KERN_ERR "arctic2_misc: Couldn't register IRQ (%d)\n", + err); + goto fail; + } + irq_requested = 1; + + writeb(ARCTIC2_FPGA_IRQS, ARCTIC2_FPGA_IRQ_ENABLE); + + /* register under major 10, minor specified in arctic_button_miscdev */ + err = misc_register(&arctic_button_miscdev); + if (err) { + printk(KERN_ERR "arctic2_misc: Couldn't register misc dev (%d)\n", + err); + goto fail; + } + + return 0; + fail: + misc_deregister(&arctic_button_miscdev); /* fails safely if + * not registered */ + if (irq_requested) + free_irq(ARCTIC_FPGA_INT, NULL); + return err; +} + +void cleanup_arctic2_misc(void) +{ + misc_deregister(&arctic_button_miscdev); + free_irq(ARCTIC_FPGA_INT, NULL); +} + +module_init(init_arctic2_misc); +module_exit(cleanup_arctic2_misc); diff -urN -X bkexcl linux-2.4.22/drivers/char/ibm_ocp_gpio.c linuxppc-2.4/drivers/char/ibm_ocp_gpio.c --- linux-2.4.22/drivers/char/ibm_ocp_gpio.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/ibm_ocp_gpio.c 2003-03-31 18:46:12.000000000 +1000 @@ -0,0 +1,399 @@ +/* + * FILE NAME ibm_ocp_gpio.c + * + * BRIEF MODULE DESCRIPTION + * API for IBM PowerPC 4xx GPIO device. + * Driver for IBM PowerPC 4xx GPIO device. + * + * Armin Kuster akuster@pacbell.net + * Sept, 2001 + * + * Orignial driver + * Author: MontaVista Software, Inc. + * Frank Rowand + * Debbie Chu + * + * Copyright 2000,2001,2002 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Version: 02/01/12 - Armin + * converted to ocp and using ioremap + * + * 1.2 02/21/01 - Armin + * minor compiler warning fixes + * + * 1.3 02/22/01 - Armin + * added apm + * + * 1.4 05/07/02 - Armin/David Mueller + * coverted to core_ocp[]; + * + * 1.5 05/25/02 - Armin + * name change from *_driver to *_dev + * + * 1.6 06/04/02 - Matt Porter + * ioremap paddr. Comment as 4xx generic driver. + * Fix header to be userland safe and locate in + * an accessible area. Add ioctl to configure + * multiplexed GPIO pins. + * + * 1.7 07/25/02 - Armin + * added CPM to enable/disable in init/exit + * + * TODO: - devfs (Armin) + * - Move selection DCRN to "additional" infos instead + * of #ifdefs. Sort out that pm_access crap. + * + * + */ + +#define VUFX "07.25.02" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct miscdevice ibm_gpio_miscdev; +static struct gpio_regs *gpiop; +static struct ocp_device *gpio_ocpdev; + +#ifdef _CONFIG_PM_FIXME_ +static struct pm_dev *pm_gpio; +#endif + +static int +ibm_gpio_save_state(struct ocp_device *dev, u32 state) +{ + return 0; +} + +static int +ibm_gpio_suspend(struct ocp_device *dev, u32 state) +{ + ocp_force_power_off(gpio_ocpdev); + return 0; +} + +static int +ibm_gpio_resume(struct ocp_device *dev) +{ + ocp_force_power_on(gpio_ocpdev); + return 0; +} + +int +ibm_gpio_config(__u32 device, __u32 mask, __u32 data) +{ + u32 cfg_reg; + + if (device != 0) + return -ENXIO; +#ifdef _CONFIG_PM_FIXME_ + pm_access(pm_gpio); +#endif + +#ifdef CONFIG_40x +#ifdef DCRN_CHCR0 + /* + * PPC405 uses CPC0_CR0 to select multiplexed GPIO pins. + */ + cfg_reg = mfdcr(DCRN_CHCR0); + cfg_reg = (cfg_reg & ~mask) | (data & mask); + mtdcr(DCRN_CHCR0, cfg_reg); +#endif +#elif CONFIG_440 + /* + * PPC440 uses CPC0_GPIO to select multiplexed GPIO pins. + */ + cfg_reg = mfdcr(DCRN_CPC0_GPIO); + cfg_reg = (cfg_reg & ~mask) | (data & mask); + mtdcr(DCRN_CPC0_GPIO, cfg_reg); +#else +#error This driver is only supported on PPC40x and PPC440 CPUs +#endif + + return 0; +} + +int +ibm_gpio_tristate(__u32 device, __u32 mask, __u32 data) +{ + if (device != 0) + return -ENXIO; +#ifdef _CONFIG_PM_FIXME_ + pm_access(pm_gpio); +#endif + gpiop->tcr = (gpiop->tcr & ~mask) | (data & mask); + return 0; +} + +int +ibm_gpio_open_drain(__u32 device, __u32 mask, __u32 data) +{ + if (device != 0) + return -ENXIO; +#ifdef _CONFIG_PM_FIXME_ + pm_access(pm_gpio); +#endif + gpiop->odr = (gpiop->odr & ~mask) | (data & mask); + + return 0; +} + +int +ibm_gpio_in(__u32 device, __u32 mask, volatile __u32 * data) +{ + if (device != 0) + return -ENXIO; +#ifdef _CONFIG_PM_FIXME_ + pm_access(pm_gpio); +#endif + gpiop->tcr = gpiop->tcr & ~mask; + eieio(); + + /* + ** If the previous state was OUT, and gpiop->ir is read once, then the + ** data that was being OUTput will be read. One way to get the right + ** data is to read gpiop->ir twice. + */ + + *data = gpiop->ir; + *data = gpiop->ir & mask; + eieio(); + return 0; +} + +int +ibm_gpio_out(__u32 device, __u32 mask, __u32 data) +{ + if (device != 0) + return -ENXIO; +#ifdef _CONFIG_PM_FIXME_ + pm_access(pm_gpio); +#endif + gpiop->or = (gpiop->or & ~mask) | (data & mask); + eieio(); + gpiop->tcr = gpiop->tcr | mask; + eieio(); + return 0; +} + +static int +ibm_gpio_open(struct inode *inode, struct file *file) +{ + MOD_INC_USE_COUNT; + + return 0; +} + +static int +ibm_gpio_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + + return 0; +} + +static int +ibm_gpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + static struct ibm_gpio_ioctl_data ioctl_data; + int status; + + if (gpio_ocpdev == NULL) + return -ENODEV; + + switch (cmd) { + case IBMGPIO_IN: + if (copy_from_user(&ioctl_data, + (struct ibm_gpio_ioctl_data *) arg, + sizeof (ioctl_data))) { + return -EFAULT; + } + + status = ibm_gpio_in(ioctl_data.device, + ioctl_data.mask, &ioctl_data.data); + if (status != 0) + return status; + + if (copy_to_user((struct ibm_gpio_ioctl_data *) arg, + &ioctl_data, sizeof (ioctl_data))) { + return -EFAULT; + } + + break; + + case IBMGPIO_OUT: + if (copy_from_user(&ioctl_data, + (struct ibm_gpio_ioctl_data *) arg, + sizeof (ioctl_data))) { + return -EFAULT; + } + + return ibm_gpio_out(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + + break; + + case IBMGPIO_OPEN_DRAIN: + if (copy_from_user(&ioctl_data, + (struct ibm_gpio_ioctl_data *) arg, + sizeof (ioctl_data))) { + return -EFAULT; + } + + return ibm_gpio_open_drain(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + + break; + + case IBMGPIO_TRISTATE: + if (copy_from_user(&ioctl_data, + (struct ibm_gpio_ioctl_data *) arg, + sizeof (ioctl_data))) + return -EFAULT; + + return ibm_gpio_tristate(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + + break; + + case IBMGPIO_CFG: + if (copy_from_user(&ioctl_data, + (struct ibm_gpio_ioctl_data *) arg, + sizeof (ioctl_data))) + return -EFAULT; + + return ibm_gpio_config(ioctl_data.device, + ioctl_data.mask, ioctl_data.data); + + break; + + default: + return -ENOIOCTLCMD; + + } + return 0; +} + +static struct file_operations ibm_gpio_fops = { + .owner = THIS_MODULE, + .ioctl = ibm_gpio_ioctl, + .open = ibm_gpio_open, + .release = ibm_gpio_release, +}; + +static int +ibm_gpio_probe(struct ocp_device *ocpdev) +{ + int rc; + + /* We suppport only one instance */ + if (gpio_ocpdev != NULL) + return -ENODEV; + + ibm_gpio_miscdev.minor = 185; /*GPIO_MINOR; */ + ibm_gpio_miscdev.name = "ibm_gpio"; + ibm_gpio_miscdev.fops = &ibm_gpio_fops; + rc = misc_register(&ibm_gpio_miscdev); /*ibm_gpio_miscdev); */ + if (rc != 0) + return rc; + gpiop = (struct gpio_regs *)ioremap(ocpdev->def->paddr, + sizeof(struct gpio_regs)); + ocp_force_power_on(ocpdev); + printk("GPIO #%d at 0x%lx\n", ocpdev->def->index, (unsigned long) gpiop); + gpio_ocpdev = ocpdev; + + return 0; +} + +static void +ibm_gpio_remove(struct ocp_device *ocpdev) +{ + if (gpio_ocpdev != ocpdev) + return; + gpio_ocpdev = NULL; + misc_deregister(&ibm_gpio_miscdev); + ocp_force_power_off(ocpdev); +} + +/* Structure for a device driver */ +static struct ocp_device_id ibm_gpio_ids[] = +{ + { .vendor = OCP_ANY_ID, .function = OCP_FUNC_GPIO }, + { .vendor = OCP_VENDOR_INVALID } +}; + +static struct ocp_driver ibm_gpio_driver = +{ + .name = "gpio", + .id_table = ibm_gpio_ids, + + .probe = ibm_gpio_probe, + .remove = ibm_gpio_remove, + .suspend = ibm_gpio_suspend, + .resume = ibm_gpio_resume, + .save_state = ibm_gpio_save_state, +}; + +static int __init +ibm_gpio_init(void) +{ + int rc; + + printk("IBM gpio driver version %s\n", VUFX); + + rc = ocp_register_driver(&ibm_gpio_driver); + if (rc == 0) { + ocp_unregister_driver(&ibm_gpio_driver); + return -ENODEV; + } + + return 0; +} + +static void __exit +ibm_gpio_exit(void) +{ + ocp_unregister_driver(&ibm_gpio_driver); +} + +module_init(ibm_gpio_init); +module_exit(ibm_gpio_exit); + +EXPORT_SYMBOL(ibm_gpio_tristate); +EXPORT_SYMBOL(ibm_gpio_open_drain); +EXPORT_SYMBOL(ibm_gpio_in); +EXPORT_SYMBOL(ibm_gpio_out); + +MODULE_LICENSE("GPL"); diff -urN -X bkexcl linux-2.4.22/drivers/char/ibmts_semtech.c linuxppc-2.4/drivers/char/ibmts_semtech.c --- linux-2.4.22/drivers/char/ibmts_semtech.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/ibmts_semtech.c 2003-06-04 15:21:24.000000000 +1000 @@ -0,0 +1,653 @@ +/* IBM Arctic-2 Semtech touch panel controller driver for /dev/ibmts interface + * + * 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 + * + * Copyright (C) 2002 IBM Corporation. + * + * Ken Inoue + * IBM Thomas J. Watson Research Center + * + * + * David Gibson + * IBM OzLabs, Canberra, Australia. + * + * + * John F. Davis + * IBM RTP + * + * + * Todd Poynor (UART access code from serial.c) + * 2003 (c) MontaVista Software, Inc. + */ + +#include /* Get my module mojo */ +#include /* int16_t ... */ +#include /* polling macros */ +#include /* Tasklets */ +#include /* wait_on, etc */ +#include /* wait queues */ +#include /* module_init() macro */ +#include /* mutex structures/code */ +#include /* struct file, struct inode */ +#include +#include +#include +#include +#include + +#define IBMTS_DEVICE_NAME "ibmts" +#define IBMTS_MAJOR 254 + +#define IBMTS_SAMPLES 128 +#define IBMTS_TIMEOUT_INTERVAL 10 + +#define PEN_RELEASE 0 +#define PEN_GLIDE 2 +#define PEN_PRESS 1 + +#define DEFAULT_SAMPLE_MASK 0xFFFFFFFE /* can be configured via proc file at runtime. */ +static int sample_mask = DEFAULT_SAMPLE_MASK; + +struct ts_event { + short pressure; /* Pressure of stylus - 0 for release, positive otherwise */ + int x; /* X coordinate stylus - -1 for PEN_RELEASE */ + int y; /* Y coordinate stylus - -1 for PEN_RELEASE */ + int millisecs; /* A timestamp */ + unsigned int flags; /* Not used for now. */ +}; + +typedef struct ibmts_semtech_event { + struct ibmts_semtech_event *pNext; + struct ts_event event; +} ibmts_semtech_event; + +// HACK: FIXME: put the following in a dynamically allocated struct + +static int IsOpenForRead = 0; + +static unsigned char pen_down = 0; + +static wait_queue_head_t ibmts_semtech_proc_list; + +static spinlock_t ibmts_semtech_lock = SPIN_LOCK_UNLOCKED; + +#define SEMTECH_FSM_RESET 0 +#define SEMTECH_FSM_DATA_READY 4 + +#define SEMTECH_SYNC_FLAG 0x80 +#define SEMTECH_ID 0x81 + +static unsigned char semtech_FSM = SEMTECH_FSM_RESET; +static unsigned char semtech_data_index = 0; +static unsigned char semtech_data[4]; +static unsigned char semtech_FSM_next[] = { + (SEMTECH_FSM_RESET + 1), (SEMTECH_FSM_RESET + 2), + (SEMTECH_FSM_RESET + 3), SEMTECH_FSM_DATA_READY +}; + +static ibmts_semtech_event events[IBMTS_SAMPLES]; + +static ibmts_semtech_event *ibmts_semtech_rdp = NULL; +static ibmts_semtech_event *ibmts_semtech_wrp = NULL; + +static unsigned long uart_base; + +static void ibmts_semtech_handle_read(unsigned char data); +static void ibmts_uart_init(void); + +/* UART driver for ibmts_semtech */ + +/* + * Read bytes, passing them to the semtech protocol handler. + */ + +static void ibmts_uart_rx(unsigned char *status) +{ + unsigned char ch; + + do { + ch = readb(uart_base + UART_RX); + + if (! (*status & (UART_LSR_FE | UART_LSR_PE | UART_LSR_OE))) + ibmts_semtech_handle_read(ch); + + *status = readb(uart_base + UART_LSR); + } while (*status & UART_LSR_DR); + + return; +} + +/* + * Serial interrupt handler. + */ + +static void ibmts_uart_interrupt(int irq, void *info, struct pt_regs *regs) +{ + unsigned char iir, status; + int max_loops = 50; + + iir = readb(uart_base + UART_IIR); + + do { + status = readb(uart_base + UART_LSR); + + if (status & UART_LSR_DR) + ibmts_uart_rx(&status); + + iir = readb(uart_base + UART_IIR); + } while (((iir & UART_IIR_NO_INT) == 0) && (--max_loops != 0)); + + return; +} + +/* + * Initialize the UART: 19200 baud, FIFO. + */ + +static void ibmts_uart_init(void) +{ + unsigned long flags; + unsigned char scratch; + unsigned char save_lcr, mcr; + unsigned char cval, quot; + + uart_base = UART1_IO_BASE; + + save_flags(flags); cli(); + + mcr = readb(uart_base + UART_MCR); + save_lcr = readb(uart_base + UART_LCR); + writeb(0xBF, uart_base + UART_LCR); /* set up for StarTech test */ + writeb(0, uart_base + UART_EFR); /* EFR is the same as FCR */ + writeb(0, uart_base + UART_LCR); + writeb(UART_FCR_ENABLE_FIFO, uart_base + UART_FCR); + scratch = readb(uart_base + UART_IIR) >> 6; + + /* + * Reset the UART. + */ + + writeb(save_lcr, uart_base + UART_LCR); + writeb(mcr, uart_base + UART_MCR); + writeb(UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT, uart_base + UART_FCR); + writeb(0, uart_base + UART_FCR); + scratch = readb(uart_base + UART_RX); + + request_irq(UART1_INT, ibmts_uart_interrupt, 0, + "touchscreen", NULL); + + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in a moment) + */ + writeb(UART_FCR_ENABLE_FIFO, uart_base + UART_FCR); + writeb(UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT, uart_base + UART_FCR); + writeb(0, uart_base + UART_FCR); + + /* + * Clear the interrupt registers. + */ + scratch = readb(uart_base + UART_LSR); + scratch = readb(uart_base + UART_RX); + scratch = readb(uart_base + UART_IIR); + scratch = readb(uart_base + UART_MSR); + + /* + * Now, initialize the UART + */ + writeb(UART_LCR_WLEN8, uart_base + UART_LCR); /* reset DLAB */ + mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; + writeb(mcr, uart_base + UART_MCR); + + /* + * Enable receive interrupts. + */ + + writeb(UART_IER_RLSI | UART_IER_RDI, uart_base + UART_IER); + + /* + * And clear the interrupt registers again for luck. + */ + + scratch = readb(uart_base + UART_LSR); + scratch = readb(uart_base + UART_RX); + scratch = readb(uart_base + UART_IIR); + scratch = readb(uart_base + UART_MSR); + + /* + * Set the UART divisor registers to match the baud rate of + * 19200. + */ + + cval = 0x03; + quot = 0x24; + + writeb(cval | UART_LCR_DLAB, uart_base + UART_LCR); /* set DLAB */ + writeb(quot & 0xff, uart_base + UART_DLL); /* LS of divisor */ + writeb(quot >> 8, uart_base + UART_DLM); /* MS of divisor */ + writeb(cval, uart_base + UART_LCR); /* reset DLAB */ + writeb(UART_FCR_ENABLE_FIFO, uart_base + UART_FCR); + writeb(UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8, + uart_base + UART_FCR); + + restore_flags(flags); + return; +} + +static int queue_empty(void) +{ + return (ibmts_semtech_rdp == ibmts_semtech_wrp); +} + +struct ts_event *ibmts_semtech_get_event(void) +{ + unsigned long flags; + ibmts_semtech_event *result; + + spin_lock_irqsave(&ibmts_semtech_lock, flags); + + if ( (ibmts_semtech_rdp == NULL) || queue_empty() ) + return NULL; + + result = ibmts_semtech_rdp; + ibmts_semtech_rdp = ibmts_semtech_rdp->pNext; + + spin_unlock_irqrestore(&ibmts_semtech_lock, flags); + return &(result->event); +}; + +static void ibmts_semtech_put_event(struct ts_event *pEvent) +{ + if ( ibmts_semtech_rdp == NULL) + return; + + if (! IsOpenForRead) + return; + + ibmts_semtech_wrp->event.pressure = pEvent->pressure; + ibmts_semtech_wrp->event.x = pEvent->x; + ibmts_semtech_wrp->event.y = pEvent->y; + ibmts_semtech_wrp->event.millisecs = pEvent->millisecs; + ibmts_semtech_wrp->event.flags = pEvent->flags; + + ibmts_semtech_wrp = ibmts_semtech_wrp->pNext; + if (ibmts_semtech_wrp == ibmts_semtech_rdp) /* Wrap */ + ibmts_semtech_rdp = ibmts_semtech_rdp->pNext; /* Write over */ + else + if (waitqueue_active(&ibmts_semtech_proc_list)) + wake_up_interruptible(&ibmts_semtech_proc_list); +} + +static int ibmts_semtech_open(struct inode *inode, struct file *file) +{ + int i; + + printk("ibmts: %d.%d\n", + inode->i_rdev >> 8, inode->i_rdev & 0xFF); + if (file->f_flags & O_NONBLOCK) + printk("ibmts: Non-blocked mode\n"); + else + printk("ibmts: Blocked mode\n"); + + if ((file-> f_mode & FMODE_READ)) { + if (IsOpenForRead) + return -EBUSY; + IsOpenForRead++; + } + + + /* Initialize the structs. */ + ibmts_semtech_rdp = &(events[0]); + ibmts_semtech_wrp = ibmts_semtech_rdp; + for (i=0; i f_mode & FMODE_READ)) + IsOpenForRead--; + + MOD_DEC_USE_COUNT; + + return 0; +} + +static ssize_t ibmts_semtech_read(struct file *pFile, char *buffer, + size_t count, loff_t *ppos) +{ + ssize_t bytes_read = 0; + struct ts_event *pWork; + + while (queue_empty()) { /* wait for an event */ + if (pFile->f_flags & O_NONBLOCK) + return -EWOULDBLOCK; + + if (wait_event_interruptible(ibmts_semtech_proc_list, ! queue_empty())) + return -ERESTARTSYS; + } + + while ( (bytes_read < count) && (! queue_empty()) ) { + pWork = ibmts_semtech_get_event(); + if (pWork) { + if (copy_to_user (buffer + bytes_read, (void *) pWork, + sizeof(struct ts_event))) { + bytes_read = -EFAULT; + break; + } + bytes_read += sizeof(struct ts_event); + if (count - bytes_read < sizeof(struct ts_event)) break; + } + } + + if (bytes_read > 0) + return bytes_read; + + if (signal_pending(current)) + return -ERESTARTSYS; + + return bytes_read; +} + +static ssize_t ibmts_semtech_write(struct file *file, const char *buffer, + size_t length, loff_t *ppos) +{ + return -EINVAL; +} + +static int ibmts_semtech_ioctl(struct inode * dev_inode, struct file *filep, + unsigned int cmd, unsigned long arg) +{ + return -EINVAL; +} + +/* + Poll for input - return code indicating if there is input to read. + */ + +static unsigned int +ibmts_semtech_poll (struct file* filp, poll_table * wait) +{ + poll_wait(filp, &ibmts_semtech_proc_list, wait); + return queue_empty() ? 0 : (POLLIN | POLLRDNORM); +} + +/* Module Declarations ***************************** */ + +static struct file_operations ibmts_semtech_fops = { + .owner = THIS_MODULE, + .open = ibmts_semtech_open, + .read = ibmts_semtech_read, + .write = ibmts_semtech_write, + .ioctl = ibmts_semtech_ioctl, + .poll = ibmts_semtech_poll, + .release = ibmts_semtech_release +}; + +static void semtech_parse_data(unsigned char *pData) +{ + struct ts_event event; + unsigned int dx, dy; + static int x=0; /* last x value */ + static int y=0; /* last y value */ + static int id_printed = 0; + + if (pData[0] == SEMTECH_ID) { + unsigned char class = pData[1] & 0x0f; + unsigned char family = ((pData[1] & 0x40) >> 2) | + (pData[2] & 0x0f); + unsigned char sub = (pData[2] & 0x70) >> 4; + unsigned int version = ((pData[1] & 0x30) << 3) | pData[3]; + + if (! id_printed++) + printk("Semtech class 0x%x family 0x%x sub-product 0x%x version 0x%x.\n", + class, family, sub, version); + return; + } + +/* #define IBMTS_SEMTECH_XYSWAP */ + +#if defined(IBMTS_SEMTECH_XYSWAP) + dx = (((unsigned int)pData[1] & 0x70) << 3) | ((unsigned int)pData[3] & 0x7f); + dy = (((unsigned int)pData[1] & 0x07) << 7) | ((unsigned int)pData[2] & 0x7f); +#else + dy = (((unsigned int)pData[1] & 0x70) << 3) | ((unsigned int)pData[3] & 0x7f); + dx = (((unsigned int)pData[1] & 0x07) << 7) | ((unsigned int)pData[2] & 0x7f); +#endif + + if (! pen_down) { /* Pen up until now */ + if (( pData[0] & 0x1f)) { /* Pen still up */ + return; + } else { + event.x = dx; + event.y = dy; + event.pressure = 500; + event.millisecs = jiffies*10; + pen_down = 1; + } + } else { /* Pen was down */ + if (pData[0] & 0x07f) { /* Pen up */ + event.x = -1; + event.y = -1; + event.pressure = 0; + event.millisecs = jiffies*10; + pen_down = 0; + } else { /* Pen Down */ + /* The pen is down and gliding, however omit + * values that are not of significant difference. + */ + dx &= sample_mask; + dy &= sample_mask; + if ( (dx != x) || (dy != y) ) { + /* create events for the glide */ + event.x = dx; + event.y = dy; + event.pressure = 500; + event.millisecs = jiffies * 10; + } else { + /* not a significan difference to warrant adding to the + * event queue. + */ + return; + } + } /* Pen Down End */ + } + + /* save old values */ + x = dx; + y = dy; + + ibmts_semtech_put_event(&event); /* Wakeup handled in put_event() */ +} + + +static void ibmts_semtech_handle_read(unsigned char data) +{ + /* Between packets & sync flag on, or In packet & sync flag off */ + if ( ( (semtech_FSM == SEMTECH_FSM_RESET) && (data & SEMTECH_SYNC_FLAG) ) || + ( (semtech_FSM != SEMTECH_FSM_RESET) && !(data & SEMTECH_SYNC_FLAG) ) ) { + /* Record and proceed to next stage */ + semtech_data[semtech_data_index++] = data; + semtech_FSM = semtech_FSM_next[semtech_FSM]; + } else { /* Out of sync */ + semtech_data_index = 0; + + if (data & SEMTECH_SYNC_FLAG) { + semtech_data[semtech_data_index++] = data; + semtech_FSM = semtech_FSM_next[semtech_FSM]; + } else { + semtech_FSM = SEMTECH_FSM_RESET; + } + } + + /* Full packet received, process it now */ + if (semtech_FSM == SEMTECH_FSM_DATA_READY) { + semtech_parse_data(semtech_data); + semtech_FSM = SEMTECH_FSM_RESET; + semtech_data_index = 0; + } + +} + +/* proc fs entries for the driver. */ +static struct proc_dir_entry *proc_semtech_ts; +static struct proc_dir_entry *proc_semtech_ts_mask; + +static int read_proc_mask(char *page, char **start, off_t offset, int count, int *eof, void *data) { + + int len = 0; + + len += sprintf(page+len, "%x\n",sample_mask); + *eof = 1; + return len; + +} + +#define isdigit(c) (c >= '0' && c <= '9') +__inline static int atoi( char *s) { + int i = 0; + while (isdigit(*s)) + i = i*10 + *(s++) - '0'; + return i; +} +static int write_proc_mask(struct file *file, const char *buffer, unsigned long count, void *data) { + + char *mybuf; + + if (current->uid != 0) + return -EACCES; + + if (count == 0) + return 0; + + if (!(mybuf = kmalloc(count+1, GFP_KERNEL))) + return -ENOMEM; + + if(copy_from_user(mybuf,buffer,count)) { + kfree(mybuf); + return -EFAULT; + } + + + mybuf[count] = '\0'; /* remove the trailing newline. */ + + /* Convert the mask to decimal and update the mask. + * I agree this code should accept hex input as well. + */ + sample_mask = atoi(mybuf); + + kfree(mybuf); + return count; + +} +void proc_init(void) { + + proc_semtech_ts = proc_mkdir("driver/semtech_ts",NULL); + + if (proc_semtech_ts == NULL) { + printk(KERN_ERR "Attempt to create proc entry for semtech touchscreen failed.\n"); + return; + } + +// proc_semtech_ts_mask = create_proc_entry("mask",S_IWUSR,proc_semtech_ts); +// if (proc_semtech_ts == NULL) { +// printk(KERN_ERR "Attempt to create proc entry for sensitivity mask failed.\n"); +// return; +// } else { +// proc_semtech_ts_mask->write_proc = write_proc_mask; +// } + + proc_semtech_ts_mask = create_proc_read_entry("mask",S_IRUGO|S_IWUSR,proc_semtech_ts,read_proc_mask,NULL); + if (proc_semtech_ts == NULL) { + printk(KERN_ERR "Attempt to create proc entry for sensitivity mask failed.\n"); + return; + } else { + proc_semtech_ts_mask->write_proc = write_proc_mask; + + } +} +void proc_cleanup(void) { + + + if (proc_semtech_ts_mask) { + remove_proc_entry("mask",proc_semtech_ts); + proc_semtech_ts_mask = NULL; + } + if (proc_semtech_ts) { + remove_proc_entry("driver/semtech_ts",NULL); + proc_semtech_ts = NULL; + } +} + + + + +MODULE_AUTHOR("Ken Inoue"); +MODULE_DESCRIPTION("IBM Arctic-2 Semtech touch panel controller driver\n"); + +int __init init_ibmts_semtech_module(void) +{ + int err; + + init_waitqueue_head(&ibmts_semtech_proc_list); + + err = register_chrdev(IBMTS_MAJOR, IBMTS_DEVICE_NAME, &ibmts_semtech_fops); + + if (err < 0) { + printk("ibmts_semtech: register_chrdev failed with %d\n", err); + return err; + } + + printk("ibmts_semtech: v0.03 e-8 device major %d \n", IBMTS_MAJOR); + ibmts_uart_init(); + +#ifdef CONFIG_PROC_FS + /* Initialize the proc filesystem. */ + proc_init(); +#endif + + return 0; +} + +void cleanup_ibmts_semtech_module(void) +{ + int ret; + + ret = unregister_chrdev(IBMTS_MAJOR, IBMTS_DEVICE_NAME); + if (ret < 0) + printk("Error : unregister_chrdev: %d\n", ret); + +#ifdef CONFIG_PROC_FS + /* clean up the proc filesystem. */ + proc_cleanup(); +#endif + +} + +module_init(init_ibmts_semtech_module); +module_exit(cleanup_ibmts_semtech_module); diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_gpio/Makefile linuxppc-2.4/drivers/char/xilinx_gpio/Makefile --- linux-2.4.22/drivers/char/xilinx_gpio/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_gpio/Makefile 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,20 @@ +# +# Makefile for the Xilinx GPIO driver +# + +EXTRA_CFLAGS += -I$(TOPDIR)/arch/ppc/platforms/xilinx_ocp + +list-multi := xilinx_gpio.o + +# The Linux adapter for the Xilinx driver code. +xilinx_gpio-objs += adapter.o + +# The Xilinx OS independent code. +xilinx_gpio-objs += xgpio.o xgpio_g.o + +obj-$(CONFIG_XILINX_GPIO) := xilinx_gpio.o + +xilinx_gpio.o: $(xilinx_gpio-objs) + $(LD) -r -o $@ $(xilinx_gpio-objs) + +include $(TOPDIR)/Rules.make diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_gpio/adapter.c linuxppc-2.4/drivers/char/xilinx_gpio/adapter.c --- linux-2.4.22/drivers/char/xilinx_gpio/adapter.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_gpio/adapter.c 2003-06-19 09:59:52.000000000 +1000 @@ -0,0 +1,335 @@ +/* + * adapter.c + * + * Xilinx GPIO Adapter component to interface GPIO component to Linux + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +/* + * This driver is a bit unusual in that it is composed of two logical + * parts where one part is the OS independent code and the other part is + * the OS dependent code. Xilinx provides their drivers split in this + * fashion. This file represents the Linux OS dependent part known as + * the Linux adapter. The other files in this directory are the OS + * independent files as provided by Xilinx with no changes made to them. + * The names exported by those files begin with XGpio_. All functions + * in this file that are called by Linux have names that begin with + * xgpio_. Any other functions are static helper functions. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Use the IBM OCP definitions for compatibility. */ +#include + +#include +#include "xgpio.h" +#include "xgpio_i.h" + +MODULE_AUTHOR("MontaVista Software, Inc. "); +MODULE_DESCRIPTION("Xilinx GPIO driver"); +MODULE_LICENSE("GPL"); + +/* Our private per interface data. */ +struct xgpio_instance { + struct xgpio_instance *next_inst; /* The next instance in inst_list */ + int index; /* Which interface is this */ + u32 save_BaseAddress; /* Saved physical base address */ + /* + * The underlying OS independent code needs space as well. A + * pointer to the following XGpio structure will be passed to + * any XGpio_ function that requires it. However, we treat the + * data as an opaque object in this file (meaning that we never + * reference any of the fields inside of the structure). + */ + XGpio Gpio; +}; +/* List of instances we're handling. */ +static struct xgpio_instance *inst_list = NULL; + +/* SAATODO: This function will be moved into the Xilinx code. */ +/*****************************************************************************/ +/** +* +* Lookup the device configuration based on the GPIO instance. The table +* XGpio_ConfigTable contains the configuration info for each device in the system. +* +* @param Instance is the index of the interface being looked up. +* +* @return +* +* A pointer to the configuration table entry corresponding to the given +* device ID, or NULL if no match is found. +* +* @note +* +* None. +* +******************************************************************************/ +XGpio_Config *XGpio_GetConfig(int Instance) +{ + if (Instance < 0 || Instance >= XPAR_XGPIO_NUM_INSTANCES) + { + return NULL; + } + + return &XGpio_ConfigTable[Instance]; +} + +/* SAATODO: This function will be moved into the Xilinx code. */ +/****************************************************************************/ +/** +* Get the input/output direction of all discrete signals. +* +* @param InstancePtr is a pointer to an XGpio instance to be worked on. +* +* @return Current copy of the tristate (direction) register. +* +* @note +* +* None +* +*****************************************************************************/ +u32 +XGpio_GetDataDirection(XGpio * InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + return XGpio_mReadReg(InstancePtr->BaseAddress, XGPIO_TRI_OFFSET); +} + +static int +xgpio_open(struct inode *inode, struct file *file) +{ + MOD_INC_USE_COUNT; + + return 0; +} + +static int +xgpio_release(struct inode *inode, struct file *file) +{ + MOD_DEC_USE_COUNT; + + return 0; +} + +static int +ioctl_setup(unsigned long arg, + struct ibm_gpio_ioctl_data *ioctl_data, + struct xgpio_instance **match) +{ + struct xgpio_instance *inst; + + if (copy_from_user(ioctl_data, (void *) arg, sizeof (*ioctl_data))) + return -EFAULT; + + inst = inst_list; + while (inst && inst->index != ioctl_data->device) + inst = inst->next_inst; + + *match = inst; + return inst ? 0 : -ENODEV; +} + +static int +xgpio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ibm_gpio_ioctl_data ioctl_data; + struct xgpio_instance *inst; + int status; + u32 r; + + switch (cmd) { + case IBMGPIO_IN: + status = ioctl_setup(arg, &ioctl_data, &inst); + if (status < 0) + return status; + + /* Ensure that the GPIO bits in the mask are tristated. */ + r = XGpio_GetDataDirection(&inst->Gpio); + XGpio_SetDataDirection(&inst->Gpio, r & ~ioctl_data.mask); + + ioctl_data.data = (XGpio_DiscreteRead(&inst->Gpio) + & ioctl_data.mask); + if (copy_to_user((struct ibm_gpio_ioctl_data *) arg, + &ioctl_data, sizeof (ioctl_data))) { + return -EFAULT; + } + break; + + case IBMGPIO_OUT: + status = ioctl_setup(arg, &ioctl_data, &inst); + if (status < 0) + return status; + + /* Get the prior value. */ + r = XGpio_DiscreteRead(&inst->Gpio); + /* Clear the bits that we're going to put in. */ + r &= ~ioctl_data.mask; + /* Set the bits that were provided. */ + r |= (ioctl_data.mask & ioctl_data.data); + + XGpio_DiscreteWrite(&inst->Gpio, r); + + /* Ensure that the GPIO bits in the mask are not tristated. */ + r = XGpio_GetDataDirection(&inst->Gpio); + XGpio_SetDataDirection(&inst->Gpio, r | ioctl_data.mask); + + break; + + case IBMGPIO_TRISTATE: + status = ioctl_setup(arg, &ioctl_data, &inst); + if (status < 0) + return status; + + /* Get the prior value. */ + r = XGpio_GetDataDirection(&inst->Gpio); + /* Clear the bits that we're going to put in. */ + r &= ~ioctl_data.mask; + /* Set the bits that were provided. */ + r |= (ioctl_data.mask & ioctl_data.data); + + XGpio_SetDataDirection(&inst->Gpio, r); + break; + + default: + return -ENOIOCTLCMD; + + } + return 0; +} + +static void +remove_head_inst(void) +{ + struct xgpio_instance *inst; + XGpio_Config *cfg; + + /* Pull the head off of inst_list. */ + inst = inst_list; + inst_list = inst->next_inst; + + cfg = XGpio_GetConfig(inst->index); + iounmap((void *) cfg->BaseAddress); + cfg->BaseAddress = inst->save_BaseAddress; +} + +static struct file_operations xfops = { + owner:THIS_MODULE, + ioctl:xgpio_ioctl, + open:xgpio_open, + release:xgpio_release, +}; +/* + * We get to all of the GPIOs through one minor number. Here's the + * miscdevice that gets registered for that minor number. + */ +static struct miscdevice miscdev = { + minor:GPIO_MINOR, + name:"xgpio", + fops:&xfops +}; + +static int __init +probe(int index) +{ + static const unsigned long remap_size + = XPAR_GPIO_0_HIGHADDR - XPAR_GPIO_0_BASEADDR + 1; + struct xgpio_instance *inst; + XGpio_Config *cfg; + + /* Find the config for our instance. */ + cfg = XGpio_GetConfig(index); + if (!cfg) + return -ENODEV; + + /* Allocate the inst and zero it out. */ + inst = (struct xgpio_instance *) kmalloc(sizeof (struct xgpio_instance), + GFP_KERNEL); + if (!inst) { + printk(KERN_ERR "%s #%d: Could not allocate instance.\n", + miscdev.name, index); + return -ENOMEM; + } + memset(inst, 0, sizeof (struct xgpio_instance)); + inst->index = index; + + /* Make it the head of inst_list. */ + inst->next_inst = inst_list; + inst_list = inst; + + /* Change the addresses to be virtual; save the old ones to restore. */ + inst->save_BaseAddress = cfg->BaseAddress; + cfg->BaseAddress = (u32) ioremap(inst->save_BaseAddress, remap_size); + + /* Tell the Xilinx code to bring this GPIO interface up. */ + if (XGpio_Initialize(&inst->Gpio, cfg->DeviceId) != XST_SUCCESS) { + printk(KERN_ERR "%s #%d: Could not initialize instance.\n", + miscdev.name, inst->index); + remove_head_inst(); + return -ENODEV; + } + + printk(KERN_INFO "%s #%d at 0x%08X mapped to 0x%08X\n", + miscdev.name, inst->index, + inst->save_BaseAddress, cfg->BaseAddress); + + return 0; +} + +static int __init +xgpio_init(void) +{ + int rtn, index = 0; + + while (probe(index++) == 0) ; + + if (index > 1) { + /* We found at least one instance. */ + + /* Register the driver with misc and report success. */ + rtn = misc_register(&miscdev); + if (rtn) { + printk(KERN_ERR "%s: Could not register driver.\n", + miscdev.name); + while (inst_list) + remove_head_inst(); + return rtn; + } + + /* Report success. */ + return 0; + } else { + /* No instances found. */ + return -ENODEV; + } + +} + +static void __exit +xgpio_cleanup(void) +{ + while (inst_list) + remove_head_inst(); + + misc_deregister(&miscdev); +} + +EXPORT_NO_SYMBOLS; + +module_init(xgpio_init); +module_exit(xgpio_cleanup); diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_gpio/xgpio.c linuxppc-2.4/drivers/char/xilinx_gpio/xgpio.c --- linux-2.4.22/drivers/char/xilinx_gpio/xgpio.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_gpio/xgpio.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,219 @@ +/* $Id: xgpio.c,v 1.8 2002/09/19 20:35:52 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/** +* @file xgpio.c +* +* The implementation of the XGpio component's basic functionality. See xgpio.h +* for more information about the component. +* +* @note +* +* None +* +*****************************************************************************/ + +/***************************** Include Files ********************************/ +#include "xparameters.h" +#include "xgpio.h" +#include "xgpio_i.h" +#include "xstatus.h" + +/************************** Constant Definitions ****************************/ + +/**************************** Type Definitions ******************************/ + +/***************** Macros (Inline Functions) Definitions ********************/ + +/************************** Variable Definitions ****************************/ + +/************************** Function Prototypes *****************************/ + +/****************************************************************************/ +/** +* Initialize the XGpio instance provided by the caller based on the +* given DeviceID. +* +* Nothing is done except to initialize the InstancePtr. +* +* @param InstancePtr is a pointer to an XGpio instance. The memory the pointer +* references must be pre-allocated by the caller. Further calls to +* manipulate the component through the XGpio API must be made with this +* pointer. +* +* @param DeviceId is the unique id of the device controlled by this XGpio +* component. Passing in a device id associates the generic XGpio +* instance to a specific device, as chosen by the caller or application +* developer. +* +* @return +* +* - XST_SUCCESS Initialization was successfull. +* - XST_DEVICE_NOT_FOUND Device configuration data was not found for a device +* with the supplied device ID. +* +* NOTES: +* +* None +* +*****************************************************************************/ +XStatus +XGpio_Initialize(XGpio * InstancePtr, u16 DeviceId) +{ + XGpio_Config *ConfigPtr; + + /* + * Assert arguments + */ + XASSERT_NONVOID(InstancePtr != NULL); + + /* + * Lookup configuration data in the device configuration table. + * Use this configuration info down below when initializing this component. + */ + ConfigPtr = XGpio_LookupConfig(DeviceId); + if (ConfigPtr == (XGpio_Config *) NULL) { + InstancePtr->IsReady = 0; + return (XST_DEVICE_NOT_FOUND); + } + + /* + * Set some default values. + */ + InstancePtr->BaseAddress = ConfigPtr->BaseAddress; + + /* + * Indicate the instance is now ready to use, initialized without error + */ + InstancePtr->IsReady = XCOMPONENT_IS_READY; + return (XST_SUCCESS); +} + +/******************************************************************************/ +/** +* Lookup the device configuration based on the unique device ID. The table +* ConfigTable contains the configuration info for each device in the system. +* +* @param DeviceID is the device identifier to lookup. +* +* @return +* +* - XGpio configuration structure pointer if DeviceID is found. +* - NULL if DeviceID is not found. +* +******************************************************************************/ +XGpio_Config * +XGpio_LookupConfig(u16 DeviceId) +{ + XGpio_Config *CfgPtr = NULL; + + int i; + + for (i = 0; i < XPAR_XGPIO_NUM_INSTANCES; i++) { + if (XGpio_ConfigTable[i].DeviceId == DeviceId) { + CfgPtr = &XGpio_ConfigTable[i]; + break; + } + } + + return CfgPtr; +} + +/****************************************************************************/ +/** +* Set the input/output direction of all discrete signals. +* +* @param InstancePtr is a pointer to an XGpio instance to be worked on. +* @param DirectionMask is a bitmask specifying which discretes are input and +* which are output. Bits set to 0 are output and bits set to 1 are input. +* +* @note +* +* None +* +*****************************************************************************/ +void +XGpio_SetDataDirection(XGpio * InstancePtr, u32 DirectionMask) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + XGpio_mWriteReg(InstancePtr->BaseAddress, XGPIO_TRI_OFFSET, + DirectionMask); +} + +/****************************************************************************/ +/** +* Read state of discretes. +* +* @param InstancePtr is a pointer to an XGpio instance to be worked on. +* +* @return Current copy of the discretes register. +* +* @note +* +* None +* +*****************************************************************************/ +u32 +XGpio_DiscreteRead(XGpio * InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + return (XGpio_mReadReg(InstancePtr->BaseAddress, XGPIO_DATA_OFFSET)); +} + +/****************************************************************************/ +/** +* Write to discretes register +* +* @param InstancePtr is a pointer to an XGpio instance to be worked on. +* @param Data is the value to be written to the discretes register. +* +* @note +* +* See also XGpio_DiscreteSet() and XGpio_DiscreteClear(). +* +*****************************************************************************/ +void +XGpio_DiscreteWrite(XGpio * InstancePtr, u32 Data) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + XGpio_mWriteReg(InstancePtr->BaseAddress, XGPIO_DATA_OFFSET, Data); +} diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_gpio/xgpio.h linuxppc-2.4/drivers/char/xilinx_gpio/xgpio.h --- linux-2.4.22/drivers/char/xilinx_gpio/xgpio.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_gpio/xgpio.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,122 @@ +/* $Id: xgpio.h,v 1.4 2002/05/02 20:44:36 linnj Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* @file xgpio.h +* +* This file contains the software API definition of the Xilinx General Purpose +* I/O (XGpio) component. +* +* The Xilinx GPIO controller is a soft IP core designed for Xilinx FPGAs and +* contains the following general features: +* - Support for 8, 16, or 32 I/O discretes +* - Each of the discretes can be configured for input or output. +* +* @note +* +* This API utilizes 32 bit I/O to the GPIO registers. With 16 and 8 bit GPIO +* components, the unused bits from registers are read as zero and written as +* don't cares. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rmm  03/13/02 First release
+* 
+*****************************************************************************/ + +#ifndef XGPIO_H /* prevent circular inclusions */ +#define XGPIO_H /* by using protection macros */ + +/***************************** Include Files ********************************/ + +#include "xbasic_types.h" +#include "xstatus.h" +#include "xgpio_l.h" + +/************************** Constant Definitions ****************************/ + +/**************************** Type Definitions ******************************/ + +/* + * This typedef contains configuration information for the device. + */ +typedef struct { + u16 DeviceId; /* Unique ID of device */ + u32 BaseAddress; /* Device base address */ +} XGpio_Config; + +/** + * The XGpio driver instance data. The user is required to allocate a + * variable of this type for every GPIO device in the system. A pointer + * to a variable of this type is then passed to the driver API functions. + */ +typedef struct { + u32 BaseAddress; /* Device base address */ + u32 IsReady; /* Device is initialized and ready */ +} XGpio; + +/***************** Macros (Inline Functions) Definitions ********************/ + +/************************** Function Prototypes *****************************/ + +/* + * API Basic functions implemented in xgpio.c + */ +XStatus XGpio_Initialize(XGpio * InstancePtr, u16 DeviceId); +void XGpio_SetDataDirection(XGpio * InstancePtr, u32 DirectionMask); +u32 XGpio_DiscreteRead(XGpio * InstancePtr); +void XGpio_DiscreteWrite(XGpio * InstancePtr, u32 Mask); + +XGpio_Config *XGpio_LookupConfig(u16 DeviceId); + +/* + * API Functions implemented in xgpio_extra.c + */ +void XGpio_DiscreteSet(XGpio * InstancePtr, u32 Mask); +void XGpio_DiscreteClear(XGpio * InstancePtr, u32 Mask); + +/* + * API Functions implemented in xgpio_selftest.c + */ +XStatus XGpio_SelfTest(XGpio * InstancePtr); +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_gpio/xgpio_g.c linuxppc-2.4/drivers/char/xilinx_gpio/xgpio_g.c --- linux-2.4.22/drivers/char/xilinx_gpio/xgpio_g.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_gpio/xgpio_g.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,62 @@ + +/******************************************************************* +* +* CAUTION: This file is automatically generated by libgen. +* Version: Xilinx EDK 3.2.2 EDK_Cm.22 +* DO NOT EDIT. +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +* Description: Driver configuration +* +*******************************************************************/ + +#include "xparameters.h" +#include "xgpio.h" + +/* +* The configuration table for devices +*/ + +XGpio_Config XGpio_ConfigTable[] = { + { + XPAR_MY_OPB_GPIO_0_DEVICE_ID, + XPAR_MY_OPB_GPIO_0_BASEADDR} + , + { + XPAR_MY_OPB_GPIO_1_DEVICE_ID, + XPAR_MY_OPB_GPIO_1_BASEADDR} +}; diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_gpio/xgpio_i.h linuxppc-2.4/drivers/char/xilinx_gpio/xgpio_i.h --- linux-2.4.22/drivers/char/xilinx_gpio/xgpio_i.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_gpio/xgpio_i.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,79 @@ +/* $Id: xgpio_i.h,v 1.3 2002/05/02 20:44:36 linnj Exp $: */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/******************************************************************************/ +/** +* @file xgpio_i.h +* +* This header file contains internal identifiers, which are those shared +* between the files of the driver. It is intended for internal use only. +* +* NOTES: +* +* None. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rmm  03/13/02 First release
+* 
+******************************************************************************/ + +#ifndef XGPIO_I_H /* prevent circular inclusions */ +#define XGPIO_I_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xgpio.h" + +/************************** Constant Definitions ****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions ****************************/ + +extern XGpio_Config XGpio_ConfigTable[]; + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_gpio/xgpio_l.h linuxppc-2.4/drivers/char/xilinx_gpio/xgpio_l.h --- linux-2.4.22/drivers/char/xilinx_gpio/xgpio_l.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_gpio/xgpio_l.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,192 @@ +/* $Id: xgpio_l.h,v 1.1 2002/05/02 20:39:10 linnj Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xgpio_l.h +* +* This header file contains identifiers and low-level driver functions (or +* macros) that can be used to access the device. The user should refer to the +* hardware device specification for more details of the device operation. +* High-level driver functions are defined in xgpio.h. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00b jhl  04/24/02 First release
+* 
+* +******************************************************************************/ + +#ifndef XGPIO_L_H /* prevent circular inclusions */ +#define XGPIO_L_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xio.h" + +/************************** Constant Definitions *****************************/ + +/** @name Registers + * + * Register offsets for this device. This device does not utilize IPIF registers. + * @{ + */ +/** + * - XGPIO_DATA_OFFSET Data register + * - XGPIO_TRI_OFFSET Three state register (sets input/output direction) + * 0 configures pin for output and 1 for input. + */ +#define XGPIO_DATA_OFFSET 0x00000000 +#define XGPIO_TRI_OFFSET 0x00000004 +/* @} */ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/***************************************************************************** +* +* Low-level driver macros. The list below provides signatures to help the +* user use the macros. +* +* u32 XGpio_mReadReg(u32 BaseAddress, int RegOffset, u32 Data) +* void XGpio_mWriteReg(u32 BaseAddress, int RegOffset, u32 Data) +* +* void XGpio_mSetDataDirection(u32 BaseAddress, u32 DirectionMask) +* +* u32 XGpio_mGetDataReg(u32 BaseAddress) +* void XGpio_mSetDataReg(u32 BaseAddress, u32 Mask) +* +*****************************************************************************/ + +/****************************************************************************/ +/** + * + * Write a value to a GPIO register. A 32 bit write is performed. If the + * GPIO component is implemented in a smaller width, only the least + * significant data is written. + * + * @param BaseAddress is the base address of the GPIO device. + * @param RegOffset is the register offset from the base to write to. + * @param Data is the data written to the register. + * + * @return None. + * + * @note None. + * + ****************************************************************************/ +#define XGpio_mWriteReg(BaseAddress, RegOffset, Data) \ + (XIo_Out32((BaseAddress) + (RegOffset), (u32)(Data))) + +/****************************************************************************/ +/** + * + * Read a value from a GPIO register. A 32 bit read is performed. If the + * GPIO component is implemented in a smaller width, only the least + * significant data is read from the register. The most significant data + * will be read as 0. + * + * @param BaseAddress is the base address of the GPIO device. + * @param Register is the register offset from the base to write to. + * @param Data is the data written to the register. + * + * @return None. + * + * @note None. + * + ****************************************************************************/ +#define XGpio_mReadReg(BaseAddress, RegOffset) \ + (XIo_In32((BaseAddress) + (RegOffset))) + +/***************************************************************************** +* +* Set the input/output direction of all signals. +* +* @param BaseAddress contains the base address of the GPIO device. +* @param DirectionMask is a bitmask specifying which discretes are input and +* which are output. Bits set to 0 are output and bits set to 1 are +* input. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +#define XGpio_mSetDataDirection(BaseAddress, DirectionMask) \ + XGpio_mWriteReg((BaseAddress), XGPIO_TRI_OFFSET, (DirectionMask)) + +/****************************************************************************/ +/** +* Get the data register. +* +* @param BaseAddress contains the base address of the GPIO device. +* +* @return The contents of the data register. +* +* @note None. +* +*****************************************************************************/ +#define XGpio_mGetDataReg(BaseAddress) \ + XGpio_mReadReg(BaseAddress, XGPIO_DATA_OFFSET) + +/****************************************************************************/ +/** +* Set the data register. +* +* @param BaseAddress contains the base address of the GPIO device. +* @param Data is the value to be written to the data register. +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +#define XGpio_mSetDataReg(BaseAddress, Data) \ + XGpio_mWriteReg((BaseAddress), XGPIO_DATA_OFFSET, (Data)); + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_keyb/Makefile linuxppc-2.4/drivers/char/xilinx_keyb/Makefile --- linux-2.4.22/drivers/char/xilinx_keyb/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_keyb/Makefile 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,21 @@ +# +# Makefile for the Xilinx PS/2 driver +# + +EXTRA_CFLAGS += -I$(TOPDIR)/arch/ppc/platforms/xilinx_ocp + +list-multi := xilinx_keyb.o + +# The code to expose the Xilinx PS/2 ports out as an 8042 keyboard controller +xilinx_keyb-objs += x8042.o +export-objs += x8042.o + +# The Xilinx OS independent code. +xilinx_keyb-objs += xps2.o xps2_g.o xps2_intr.o xps2_l.o + +obj-$(CONFIG_VT) := xilinx_keyb.o + +xilinx_keyb.o: $(xilinx_keyb-objs) + $(LD) -r -o $@ $(xilinx_keyb-objs) + +include $(TOPDIR)/Rules.make diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_keyb/x8042.c linuxppc-2.4/drivers/char/xilinx_keyb/x8042.c --- linux-2.4.22/drivers/char/xilinx_keyb/x8042.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_keyb/x8042.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,446 @@ +/* + * x8042.c + * + * Expose the Xilinx PS/2 components as a typical PC 8042 keyboard controller + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +/* + * This code is a bit unusual in a few ways. The first is that it is + * emulating a hardware component: the typical PC's 8042 keyboard + * controller. With the current implementation of the keyboard code in + * Linux, it seemed to make the most sense to not have yet another + * hacked up pc_keyb.c to handle the unique interface that has been + * exposed by Xilinx. They basically expose the PS/2 interfaces just + * like serial ports. + * + * This code defines four functions that are called by the typical PC + * keyboard code when it wants to talk to the 8042. These functions in + * turn expose the Xilinx PS/2 interfaces via the typical 8042 + * interface. + * + * This brings us to another unusual aspect of this code. Xilinx + * provides drivers with their FPGA IP. Their drivers are composed of + * two logical parts where one part is the OS independent code and the + * other part is the OS dependent code. This file exposes their OS + * independent functions as an 8042 keyboard controller. The other + * files in this directory are the OS independent files as provided by + * Xilinx with no changes made to them. The names exported by those + * files begin with XPs2_. All functions in this file that are called + * by Linux have names that begin with x8042_. The functions in this + * file that have Handler in their name are registered as callbacks with + * the underlying Xilinx OS independent layer. Any other functions are + * static helper functions. + * + * One more thing should be noted. The words input and output can be + * confusing in this context. It depends upon whether you look at it + * from the perspective of the main CPU or from the perspective of the + * 8042 keyboard controller. The KBD_STAT_OBF, KBD_STAT_MOUSE_OBF and + * KBD_STAT_IBF defines from pc_keyb.h are from the perspective of the + * 8042: OBF means Output Buffer Full which means that there is a byte + * available to be read from the keyboard controller. However, + * pc_keyb.c also calls the functions kbd_read_input, kbd_read_status, + * kbd_write_output and kbd_write_command. The naming of these + * functions is from the other perspective, that of the main CPU. + * + * There are some lurking issues that I have not addressed yet. I haven't seen + * them raise their heads, but I believe that the potential for a problem is + * there. The first issue is that nothing prevents the mouse from trashing the + * keyboard and vice-versa. I've tried to make this happen by holding down a + * key while mucking with the mouse under X, but didn't see any problem, but it + * still should probably be addressed. + * + * The second issue is that whenever we set an Output Buffer Full flag as the + * result of a command, we should probably generate an interrupt. I don't + * think this is actually a problem because whenever pc_keyb.c sends a command + * where a response is expected, it appears that it goes into a polling loop. + * Something to remember though if you run into a problem. + * + * The final issue is that KBD_MODE_DISABLE_KBD and KBD_MODE_DISABLE_MOUSE are + * not yet handled. DISABLE_KBD is not used and DISABLE_MOUSE is only used + * during startup and shutdown. In short, this is another thing that should be + * addressed. + * + * SAATODO: Address these issues. + */ + +#include +#include +#include +#include + +#include + +#include +#include "xps2.h" +#include "xps2_i.h" + +#define VERSION 0x00 +/* Note that if you change the following PS2_INDEXes, you will also need + * to change the definition of KEYBOARD_IRQ and AUX_IRQ to match. */ +#define KEYBOARD_PS2_INDEX 1 /* Keyboard is PS/2 #1 */ +#define MOUSE_PS2_INDEX 0 /* Mouse is PS/2 #0 */ + +/* Our private per device data. */ +struct xkeyb_dev { + int index; /* Which interface is this */ + int initialized; /* Successfully initialized? */ + u32 save_BaseAddress; /* Saved physical base address */ + /* + * The underlying OS independent code needs space as well. A + * pointer to the following XPs2 structure will be passed to + * any XPs2_ function that requires it. However, we treat the + * data as an opaque object in this file (meaning that we never + * reference any of the fields inside of the structure). + */ + XPs2 Ps2; +}; +static struct xkeyb_dev keyboard = { index:KEYBOARD_PS2_INDEX }; +static struct xkeyb_dev mouse = { index:MOUSE_PS2_INDEX }; + +/* We need to keep track of some global flags. We use this struct to do so. */ +static struct { + unsigned int uninitialized:1; + unsigned int mouse_enabled:1; +} gflags = { +1, 0}; + +/* The emulated 8042's registers */ +static unsigned char from_host; /* Last byte written from the host. */ +static unsigned char to_host; /* Next byte to be read by the host. */ +static unsigned char status_reg = 0; /* Status. */ +static unsigned char command_byte; /* The 8042's command byte. */ + +/* SAATODO: This function will be moved into the Xilinx code. */ +/*****************************************************************************/ +/** +* +* Lookup the device configuration based on the PS/2 instance. The table +* XPs2_ConfigTable contains the configuration info for each device in the system. +* +* @param Instance is the index of the PS/2 interface being looked up. +* +* @return +* +* A pointer to the configuration table entry corresponding to the given +* device ID, or NULL if no match is found. +* +* @note +* +* None. +* +******************************************************************************/ +XPs2_Config *XPs2_GetConfig(int Instance) +{ + if (Instance < 0 || Instance >= XPAR_XPS2_NUM_INSTANCES) + { + return NULL; + } + + return &XPs2_ConfigTable[Instance]; +} + +static void +keybHandler(void *CallbackRef, u32 Event, unsigned int EventData) +{ + struct xkeyb_dev *dev = (struct xkeyb_dev *) CallbackRef; + + switch (Event) { + case XPS2_EVENT_SENT_DATA: + status_reg &= ~KBD_STAT_IBF; + break; + case XPS2_EVENT_RECV_DATA: + if (XPs2_Recv(&dev->Ps2, &to_host, 1) != 1) { + printk(KERN_ERR "Xilinx PS/2 received data missing.\n"); + break; + } + status_reg |= KBD_STAT_OBF; + break; + case XPS2_EVENT_RECV_ERROR: + case XPS2_EVENT_RECV_OVF: + status_reg |= KBD_STAT_PERR; + break; + case XPS2_EVENT_SENT_NOACK: + case XPS2_EVENT_TIMEOUT: + status_reg |= KBD_STAT_GTO; + break; + default: + printk("PS2 #%d unrecognized event %u\n", dev->index, Event); + break; + } +} + +static void +mouseHandler(void *CallbackRef, u32 Event, unsigned int EventData) +{ + struct xkeyb_dev *dev = (struct xkeyb_dev *) CallbackRef; + + switch (Event) { + case XPS2_EVENT_SENT_DATA: + status_reg &= ~KBD_STAT_IBF; + break; + case XPS2_EVENT_RECV_DATA: + if (XPs2_Recv(&dev->Ps2, &to_host, 1) != 1) { + printk(KERN_ERR "Xilinx PS/2 received data missing.\n"); + break; + } + status_reg |= AUX_STAT_OBF; + break; + case XPS2_EVENT_RECV_ERROR: + case XPS2_EVENT_RECV_OVF: + case XPS2_EVENT_SENT_NOACK: + case XPS2_EVENT_TIMEOUT: + /* + * It's not clear to me if the mouse should affect the status + * register. I'm presuming it should not based upon how + * pc_keyb.c handles the condition. If this is wrong, code + * should be copied from keybHandler. + */ + break; + default: + printk("PS2 #%d unrecognized event %u\n", dev->index, Event); + break; + } +} + +static int +initdev(struct xkeyb_dev *dev, XPs2_Handler handler) +{ + static const unsigned long remap_size + = XPAR_PS2_0_HIGHADDR - XPAR_PS2_0_BASEADDR + 1; + XPs2_Config *cfg; + + /* Find the config for our device. */ + cfg = XPs2_GetConfig(dev->index); + if (!cfg) + return -ENODEV; + + /* Change the addresses to be virtual; save the old ones to restore. */ + dev->save_BaseAddress = cfg->BaseAddress; + cfg->BaseAddress = (u32) ioremap(dev->save_BaseAddress, remap_size); + + if (XPs2_Initialize(&dev->Ps2, cfg->DeviceId) != XST_SUCCESS) { + printk(KERN_ERR + "Xilinx PS/2 #%d: Could not initialize device.\n", + dev->index); + return -ENODEV; + } + + /* Set up the interrupt handler. */ + XPs2_SetHandler(&dev->Ps2, handler, dev); + + dev->initialized = 1; + printk(KERN_INFO "Xilinx PS/2 #%d at 0x%08X mapped to 0x%08X\n", + dev->index, dev->save_BaseAddress, cfg->BaseAddress); + + return 0; +} + +static void +init_all(void) +{ + (void) initdev(&keyboard, keybHandler); + (void) initdev(&mouse, mouseHandler); + gflags.uninitialized = 0; +} + +unsigned char +x8042_read_input(void) +{ + if (gflags.uninitialized) + init_all(); + + status_reg &= ~AUX_STAT_OBF; + return to_host; +} + +EXPORT_SYMBOL(x8042_read_input); + +unsigned char +x8042_read_status(void) +{ + unsigned char rtn; + if (gflags.uninitialized) + init_all(); + + /* + * This is not very pretty. Typically, I would have the + * interrupts come into this code and then call + * XPs2_InterruptHandler to take care of them. However, in this + * case, pc_keyb.c receives the interrupts and then calls its + * handle_kbd_event to take care of both mouse and keyboard + * interrupts. This in turn calls kbd_read_status which is + * #define'ed to be this function. Both handle_kbd_event and + * XPs2_InterruptHandler handle the case of finding nothing to + * do well, so that leads to the following code that just gives + * the underlying Xilinx code a chance to handle the interrupts + * everytime this function is called. + */ + XPs2_InterruptHandler(&keyboard.Ps2); + XPs2_InterruptHandler(&mouse.Ps2); + + rtn = status_reg; + status_reg &= ~(KBD_STAT_GTO | KBD_STAT_PERR); + return rtn; +} + +EXPORT_SYMBOL(x8042_read_status); + +void +x8042_write_output(unsigned char val) +{ + XPs2 *dest_ps2 = &keyboard.Ps2; + unsigned char prior_command, old_command_byte, changed; + + if (gflags.uninitialized) + init_all(); + + prior_command = (status_reg & KBD_STAT_CMD) ? from_host : 0; + from_host = val; + status_reg &= ~KBD_STAT_CMD; + + /* Check to see if this byte is a parameter for the last command. */ + switch (prior_command) { + case KBD_CCMD_WRITE_MODE: + old_command_byte = command_byte; + /* There are eight different flags in the command byte. Of + * these eight, we force three of them (KBD_MODE_NO_KEYLOCK, + * KBD_MODE_KCC, and KBD_MODE_RFU) to always be zero. + * NO_KEYLOCK is an older AT thing and we don't have an inhibit + * keyboard switch anyway. KCC tells the keyboard controller + * to do scan code conversion, which we don't handle. RFU is + * apparently reserved for future use. + * + * KBD_MODE_SYS apparently is for the system's use so we just + * let it be toggled on and off without any action on our + * part. + * + * KBD_MODE_KBD_INT and KBD_MODE_MOUSE_INT are checked here to + * see when they are turned on and off so the underlying Xilinx + * routine can be called to turn interrupts on and off. + * + * This leaves KBD_MODE_DISABLE_KBD and KBD_MODE_DISABLE_MOUSE + * which are not handled yet. + */ + + command_byte = val & (KBD_MODE_KBD_INT + | KBD_MODE_MOUSE_INT + | KBD_MODE_SYS + | KBD_MODE_DISABLE_KBD + | KBD_MODE_DISABLE_MOUSE); + changed = old_command_byte ^ command_byte; + if (changed & KBD_MODE_KBD_INT) { + if (command_byte & KBD_MODE_KBD_INT) + XPs2_EnableInterrupt(&keyboard.Ps2); + else + XPs2_DisableInterrupt(&keyboard.Ps2); + } + if (changed & KBD_MODE_MOUSE_INT) { + if (command_byte & KBD_MODE_MOUSE_INT) + XPs2_EnableInterrupt(&mouse.Ps2); + else + XPs2_DisableInterrupt(&mouse.Ps2); + } + + return; /* We're done here. */ + case KBD_CCMD_WRITE_AUX_OBUF: + to_host = val; + status_reg |= AUX_STAT_OBF; + return; /* We're done here. */ + case KBD_CCMD_WRITE_MOUSE: + /* Send this to the mouse. */ + dest_ps2 = &mouse.Ps2; + break; + } + + /* We need to send this byte to one of the PS/2 interfaces. */ + status_reg |= KBD_STAT_IBF; + XPs2_Send(dest_ps2, &val, 1); +} + +EXPORT_SYMBOL(x8042_write_output); + +void +x8042_write_command(unsigned char val) +{ + if (gflags.uninitialized) + init_all(); + + /* Commands operate in three different ways: + * 1) A command byte is written and then another byte is written + * to provide a parameter for the command. + * 2) A command byte is written and then a byte is read. + * 3) The command is self-contained and no other bytes are read + * or written. + * The commands that fall into the last category are handled + * immediately in this function. The other two are handled in + * x8042_write_output() and x8042_read_input() when the next + * byte comes in. Those functions look at KBD_STAT_CMD within + * status_reg to know that a command is pending. + */ + + status_reg |= KBD_STAT_CMD | KBD_STAT_IBF; + from_host = val; + + switch (val) { + case KBD_CCMD_READ_MODE: + to_host = command_byte; + status_reg |= KBD_STAT_OBF; + break; + case KBD_CCMD_GET_VERSION: + to_host = VERSION; + status_reg |= KBD_STAT_OBF; + break; + case KBD_CCMD_MOUSE_DISABLE: + gflags.mouse_enabled = 0; + break; + case KBD_CCMD_MOUSE_ENABLE: + gflags.mouse_enabled = 1; + break; + case KBD_CCMD_TEST_MOUSE: + /* If we failed initialization, return an error. */ + to_host = mouse.initialized ? 0x00 : 0x04; + status_reg |= KBD_STAT_OBF; + break; + case KBD_CCMD_SELF_TEST: + /* Say our self test passed. */ + to_host = 0x55; + status_reg |= (KBD_STAT_OBF | KBD_STAT_SELFTEST); + break; + case KBD_CCMD_KBD_TEST: + /* If we failed initialization, return an error. */ + to_host = keyboard.initialized ? 0x00 : 0x04; + status_reg |= KBD_STAT_OBF; + break; + case KBD_CCMD_KBD_DISABLE: + if (!(command_byte & KBD_MODE_DISABLE_KBD)) { + /* Keyboard was enabled and we need to disable it. */ + command_byte |= KBD_MODE_DISABLE_KBD; + } + break; + case KBD_CCMD_KBD_ENABLE: + if (command_byte & KBD_MODE_DISABLE_KBD) { + /* Keyboard was disabled and we need to enable it. */ + command_byte &= ~KBD_MODE_DISABLE_KBD; + } + break; + case KBD_CCMD_WRITE_AUX_OBUF: + case KBD_CCMD_WRITE_MOUSE: + case KBD_CCMD_WRITE_MODE: + /* Handled in x8042_write_output() */ + break; + default: + printk(KERN_ERR "x8042: unrecognized command 0x%X\n", val); + break; + } + + status_reg &= ~KBD_STAT_IBF; +} + +EXPORT_SYMBOL(x8042_write_command); diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_keyb/xps2.c linuxppc-2.4/drivers/char/xilinx_keyb/xps2.c --- linux-2.4.22/drivers/char/xilinx_keyb/xps2.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_keyb/xps2.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,503 @@ +/* $Id: xps2.c,v 1.2 2002/06/26 23:19:28 carsten Exp $ */ +/***************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +*****************************************************************************/ +/****************************************************************************/ +/** +* +* @file xps2.c +* +* This file contains the required functions for the PS/2 driver. +* Refer to the header file xps2.h for more detailed information. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   06/18/02 First release
+* 
+* +*****************************************************************************/ + +/***************************** Include Files ********************************/ + +#include "xstatus.h" +#include "xparameters.h" +#include "xps2.h" +#include "xps2_i.h" +#include "xps2_l.h" +#include "xio.h" + +/************************** Constant Definitions ****************************/ + +/**************************** Type Definitions ******************************/ + +/***************** Macros (Inline Functions) Definitions ********************/ + +/************************** Variable Definitions ****************************/ + +/************************** Function Prototypes *****************************/ + +static void XPs2_StubHandler(void *CallBackRef, u32 Event, + unsigned int ByteCount); + +/****************************************************************************/ +/** +* +* Initializes a specific PS/2 instance such that it is ready to be used. +* The default operating mode of the driver is polled mode. +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* @param DeviceId is the unique id of the device controlled by this +* XPs2 instance. Passing in a device id associates the generic +* XPs2 instance to a specific device, as chosen by the caller +* or application developer. +* +* @return +* +* - XST_SUCCESS if initialization was successful +* - XST_DEVICE_NOT_FOUND if the device ID could not be found in the +* configuration table +* +* @note +* +* None. +* +*****************************************************************************/ +XStatus +XPs2_Initialize(XPs2 * InstancePtr, u16 DeviceId) +{ + XPs2_Config *Ps2ConfigPtr; + + /* + * Assert validates the input arguments + */ + XASSERT_NONVOID(InstancePtr != NULL); + + /* + * Lookup the device configuration in the temporary CROM table. Use this + * configuration info down below when initializing this component. + */ + Ps2ConfigPtr = XPs2_LookupConfig(DeviceId); + + if (Ps2ConfigPtr == (XPs2_Config *) NULL) { + return XST_DEVICE_NOT_FOUND; + } + + /* + * Setup the data that is from the configuration information + */ + InstancePtr->BaseAddress = Ps2ConfigPtr->BaseAddress; + + /* + * Initialize the instance data to some default values and setup a default + * handler + */ + InstancePtr->Handler = XPs2_StubHandler; + + InstancePtr->SendBuffer.NextBytePtr = NULL; + InstancePtr->SendBuffer.RemainingBytes = 0; + InstancePtr->SendBuffer.RequestedBytes = 0; + + InstancePtr->ReceiveBuffer.NextBytePtr = NULL; + InstancePtr->ReceiveBuffer.RemainingBytes = 0; + InstancePtr->ReceiveBuffer.RequestedBytes = 0; + + /* + * Reset the PS/2 Hardware + */ + XPs2_mReset(InstancePtr->BaseAddress); + + /* + * Disable all PS/2 interrupts + */ + XPs2_mDisableIntr(InstancePtr->BaseAddress, XPS2_INT_ALL); + + /* + * Indicate the instance is now ready to use, initialized without error + */ + InstancePtr->IsReady = XCOMPONENT_IS_READY; + + return XST_SUCCESS; +} + +/****************************************************************************/ +/** +* +* This functions sends the specified buffer of data to the PS/2 port in either +* polled or interrupt driven modes. This function is non-blocking such that it +* will return before the data has been sent thorugh PS/2. If the port is busy +* sending data, it will return and indicate zero bytes were sent. +* +* In a polled mode, this function will only send 1 byte which is as much data +* as the transmitter can buffer. The application may need to call it +* repeatedly to send a buffer. +* +* In interrupt mode, this function will start sending the specified buffer and +* then the interrupt handler of the driver will continue sending data until the +* buffer has been sent. A callback function, as specified by the application, +* will be called to indicate the completion of sending the buffer. +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* @param BufferPtr is pointer to a buffer of data to be sent. +* @param NumBytes contains the number of bytes to be sent. A value of zero +* will stop a previous send operation that is in progress in interrupt +* mode. Any data that was already put into the transmit FIFO will be +* sent. +* +* @return +* +* The number of bytes actually sent. +* +* @note +* +* The number of bytes is not asserted so that this function may be called with +* a value of zero to stop an operation that is already in progress. +*

+* This function modifies shared data such that there may be a need for mutual +* exclusion in a multithreaded environment +* +*****************************************************************************/ +unsigned int +XPs2_Send(XPs2 * InstancePtr, u8 * BufferPtr, unsigned int NumBytes) +{ + unsigned int BytesSent; + + /* + * Assert validates the input arguments + */ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(BufferPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Enter a critical region by disabling the PS/2 transmit interrupts to + * allow this call to stop a previous operation that may be interrupt + * driven, only stop the transmit interrupt since this critical region is + * not really exited in the normal manner + */ + XPs2_mDisableIntr(InstancePtr->BaseAddress, XPS2_INT_TX_ALL); + + /* + * Setup the specified buffer to be sent by setting the instance + * variables so it can be sent with polled or interrupt mode + */ + InstancePtr->SendBuffer.RequestedBytes = NumBytes; + InstancePtr->SendBuffer.RemainingBytes = NumBytes; + InstancePtr->SendBuffer.NextBytePtr = BufferPtr; + + /* + * Send the buffer and return the number of bytes sent + */ + BytesSent = XPs2_SendBuffer(InstancePtr); + + /* + * The critical region is not exited in this function because of the way + * the transmit interrupts work. The other function called enables the + * transmit interrupt such that this function can't restore a value to the + * interrupt enable register and does not need to exit the critical region + */ + return BytesSent; +} + +/****************************************************************************/ +/** +* +* This function will attempt to receive a specified number of bytes of data +* from PS/2 and store it into the specified buffer. This function is +* designed for either polled or interrupt driven modes. It is non-blocking +* such that it will return if no data has already received by the PS/2 port. +* +* In a polled mode, this function will only receive 1 byte which is as much +* data as the receiver can buffer. The application may need to call it +* repeatedly to receive a buffer. Polled mode is the default mode of +* operation for the driver. +* +* In interrupt mode, this function will start receiving and then the interrupt +* handler of the driver will continue receiving data until the buffer has been +* received. A callback function, as specified by the application, will be called +* to indicate the completion of receiving the buffer or when any receive errors +* or timeouts occur. Interrupt mode must be enabled. +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* @param BufferPtr is pointer to buffer for data to be received into +* @param NumBytes is the number of bytes to be received. A value of zero will +* stop a previous receive operation that is in progress in interrupt mode. +* +* @return +* +* The number of bytes received. +* +* @note +* +* The number of bytes is not asserted so that this function may be called with +* a value of zero to stop an operation that is already in progress. +* +*****************************************************************************/ +unsigned int +XPs2_Recv(XPs2 * InstancePtr, u8 * BufferPtr, unsigned int NumBytes) +{ + unsigned int ReceivedCount; + + /* + * Assert validates the input arguments + */ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(BufferPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Setup the specified buffer to be sent by setting the instance + * variables so it can be sent with polled or interrupt mode + */ + InstancePtr->ReceiveBuffer.RequestedBytes = NumBytes; + InstancePtr->ReceiveBuffer.RemainingBytes = NumBytes; + InstancePtr->ReceiveBuffer.NextBytePtr = BufferPtr; + + /* + * Receive the data from PS/2 and return the number of bytes + * received + */ + ReceivedCount = XPs2_ReceiveBuffer(InstancePtr); + + return ReceivedCount; +} + +/****************************************************************************/ +/** +* +* This function sends a buffer that has been previously specified by setting +* up the instance variables of the instance. This function is designed to be +* an internal function for the XPs2 component such that it may be called +* from a shell function that sets up the buffer or from an interrupt handler. +* +* This function sends the specified buffer of data to the PS/2 port in either +* polled or interrupt driven modes. This function is non-blocking such that +* it will return before the data has been sent. +* +* In a polled mode, this function will only send 1 byte which is as much data +* transmitter can buffer. The application may need to call it repeatedly to +* send a buffer. +* +* In interrupt mode, this function will start sending the specified buffer and +* then the interrupt handler of the driver will continue until the buffer +* has been sent. A callback function, as specified by the application, will +* be called to indicate the completion of sending the buffer. +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* +* @return +* +* NumBytes is the number of bytes actually sent +* +* @note +* +* None. +* +*****************************************************************************/ +unsigned int +XPs2_SendBuffer(XPs2 * InstancePtr) +{ + unsigned int SentCount = 0; + + /* + * If the transmitter is empty send one byte of data + */ + if (!XPs2_mIsTransmitFull(InstancePtr->BaseAddress)) { + XPs2_SendByte(InstancePtr->BaseAddress, + InstancePtr->SendBuffer.NextBytePtr[SentCount]); + + SentCount = 1; + } + /* + * Update the buffer to reflect the bytes that were sent + * from it + */ + InstancePtr->SendBuffer.NextBytePtr += SentCount; + InstancePtr->SendBuffer.RemainingBytes -= SentCount; + + /* + * If interrupts are enabled as indicated by the receive interrupt, then + * enable the transmit interrupt + */ + if (XPs2_mIsIntrEnabled((InstancePtr->BaseAddress), XPS2_INT_RX_FULL)) { + XPs2_mEnableIntr(InstancePtr->BaseAddress, XPS2_INT_TX_ALL | + XPS2_INT_WDT_TOUT); + } + + return SentCount; +} + +/****************************************************************************/ +/** +* +* This function receives a buffer that has been previously specified by setting +* up the instance variables of the instance. This function is designed to be +* an internal function for the XPs2 component such that it may be called +* from a shell function that sets up the buffer or from an interrupt handler. +* +* This function will attempt to receive a specified number of bytes of data +* from PS/2 and store it into the specified buffer. This function is +* designed for either polled or interrupt driven modes. It is non-blocking +* such that it will return if there is no data has already received. +* +* In a polled mode, this function will only receive 1 byte which is as much +* data as the receiver can buffer. The application may need to call it +* repeatedly to receive a buffer. Polled mode is the default mode of operation +* for the driver. +* +* In interrupt mode, this function will start receiving and then the interrupt +* handler of the driver will continue until the buffer has been received. A +* callback function, as specified by the application, will be called to indicate +* the completion of receiving the buffer or when any receive errors or timeouts +* occur. Interrupt mode must be enabled using the SetOptions function. +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* +* @return +* +* The number of bytes received. +* +* @note +* +* None. +* +*****************************************************************************/ +unsigned int +XPs2_ReceiveBuffer(XPs2 * InstancePtr) +{ + unsigned int ReceivedCount = 0; + + /* + * Loop until there is no more date buffered by the PS/2 receiver or the + * specified number of bytes has been received + */ + while (ReceivedCount < InstancePtr->ReceiveBuffer.RemainingBytes) { + /* + * If there is data ready to be read , then put the next byte + * read into the specified buffer + */ + if (!XPs2_mIsReceiveEmpty(InstancePtr->BaseAddress)) { + InstancePtr->ReceiveBuffer. + NextBytePtr[ReceivedCount++] = + XPs2_RecvByte(InstancePtr->BaseAddress); + } + + /* + * There is no more data buffered, so exit such that this function does + * not block waiting for data + */ + else { + break; + } + } + + /* + * Update the receive buffer to reflect the number of bytes that was + * received + */ + InstancePtr->ReceiveBuffer.NextBytePtr += ReceivedCount; + InstancePtr->ReceiveBuffer.RemainingBytes -= ReceivedCount; + + return ReceivedCount; +} + +/****************************************************************************/ +/** +* +* Looks up the device configuration based on the unique device ID. A table +* contains the configuration info for each device in the system. +* +* @param DeviceId contains the ID of the device to look up the configuration +* for. +* +* @return +* +* A pointer to the configuration found or NULL if the specified device ID was +* not found. +* +* @note +* +* None. +* +******************************************************************************/ +XPs2_Config * +XPs2_LookupConfig(u16 DeviceId) +{ + XPs2_Config *CfgPtr = NULL; + + int i; + + for (i = 0; i < XPAR_XPS2_NUM_INSTANCES; i++) { + if (XPs2_ConfigTable[i].DeviceId == DeviceId) { + CfgPtr = &XPs2_ConfigTable[i]; + } + } + + return CfgPtr; +} + +/****************************************************************************/ +/** +* +* This function is a stub handler that is the default handler such that if the +* application has not set the handler when interrupts are enabled, this +* function will be called. The function interface has to match the interface +* specified for a handler even though none of the arguments are used. +* +* @param CallBackRef is unused by this function. +* @param Event is unused by this function. +* @param ByteCount is unused by this function. +* +* @return +* +* None. +* +* @note +* +* None. +* +*****************************************************************************/ +static void +XPs2_StubHandler(void *CallBackRef, u32 Event, unsigned int ByteCount) +{ + /* + * Assert alway occurs since this is a stub and should never be called + */ + XASSERT_VOID(FALSE); +} diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_keyb/xps2.h linuxppc-2.4/drivers/char/xilinx_keyb/xps2.h --- linux-2.4.22/drivers/char/xilinx_keyb/xps2.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_keyb/xps2.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,227 @@ +/* $Id: xps2.h,v 1.5 2002/06/26 20:49:18 carsten Exp $ */ +/***************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +*****************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xps2.h +* +* This driver supports the following features: +* +* - Polled mode +* - Interrupt driven mode +* +* Interrupts +* +* The device does not have any way to disable the receiver such that the +* receiver may contain unwanted data. The IP is reset driver is initialized, +* +* The driver defaults to no interrupts at initialization such that interrupts +* must be enabled if desired. An interrupt is generated for any of the following +* conditions. +* +* - Data in the receiver +* - Any receive status error detected +* - Data byte transmitted +* - Any transmit status error detected +* +* The application can control which interrupts are enabled using the SetOptions +* function. +* +* In order to use interrupts, it is necessary for the user to connect the +* driver interrupt handler, XPs2_InterruptHandler(), to the interrupt system of +* the application. This function does not save and restore the processor +* context such that the user must provide it. A handler must be set for the +* driver such that the handler is called when interrupt events occur. The +* handler is called from interrupt context and is designed to allow application +* specific processing to be performed. +* +* The functions, XPs2_Send() and Ps2_Recv(), are provided in the driver to +* allow data to be sent and received. They are designed to be used in polled +* or interrupt modes. +* +* @note +* +* None. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   06/18/02 First release
+* 
+* +******************************************************************************/ + +#ifndef XPS2_H /* prevent circular inclusions */ +#define XPS2_H /* by using protection macros */ + +/***************************** Include Files ********************************/ + +#include "xbasic_types.h" +#include "xstatus.h" +#include "xps2_l.h" + +/************************** Constant Definitions ****************************/ + +/* + * These constants specify the handler events that are passed to + * a handler from the driver. These constants are not bit masks suuch that + * only one will be passed at a time to the handler + */ +#define XPS2_EVENT_RECV_DATA 1 +#define XPS2_EVENT_RECV_ERROR 2 +#define XPS2_EVENT_RECV_OVF 3 +#define XPS2_EVENT_SENT_DATA 4 +#define XPS2_EVENT_SENT_NOACK 5 +#define XPS2_EVENT_TIMEOUT 6 + +/* + * These constants specify the errors that may be retrieved from the driver + * using the XPs2_GetLastErrors function. All of them are bit masks, except + * no error, such that multiple errors may be specified. + */ +#define XPS2_ERROR_NONE 0x00 +#define XPS2_ERROR_WDT_TOUT_MASK 0x01 +#define XPS2_ERROR_TX_NOACK_MASK 0x02 +#define XPS2_ERROR_RX_OVF_MASK 0x08 +#define XPS2_ERROR_RX_ERR_MASK 0x10 + +/**************************** Type Definitions ******************************/ + +/* + * This typedef contains configuration information for the device + */ +typedef struct { + u16 DeviceId; /* Unique ID of device */ + u32 BaseAddress; /* Base address of device */ +} XPs2_Config; + +/* + * The following data type is used to manage the buffers that are handled + * when sending and receiving data in the interrupt mode + */ +typedef struct { + u8 *NextBytePtr; + unsigned int RequestedBytes; + unsigned int RemainingBytes; +} XPs2Buffer; + +/* + * This data type defines a handler which the application must define + * when using interrupt mode. The handler will be called from the driver in an + * interrupt context to handle application specific processing. + * + * @param CallBackRef is a callback reference passed in by the upper layer + * when setting the handler, and is passed back to the upper layer when + * the handler is called. + * @param Event contains one of the event constants indicating why the handler + * is being called. + * @param EventData contains the number of bytes sent or received at the time +* of the call. +*/ +typedef void (*XPs2_Handler) (void *CallBackRef, u32 Event, + unsigned int EventData); +/* + * PS/2 statistics + */ +typedef struct { + u16 TransmitInterrupts; + u16 ReceiveInterrupts; + u16 CharactersTransmitted; + u16 CharactersReceived; + u16 ReceiveErrors; + u16 ReceiveOverflowErrors; + u16 TransmitErrors; +} XPs2Stats; + +/* + * The PS/2 driver instance data. The user is required to allocate a + * variable of this type for every PS/2 device in the system. + * If the last byte of a message was received then call the application + * handler, this code should not use an else from the previous check of + * the number of bytes to receive because the call to receive the buffer + * updates the bytes to receive + * A pointer to a variable of this type is then passed to the driver API + * functions + */ +typedef struct { + XPs2Stats Stats; /* Component Statistics */ + u32 BaseAddress; /* Base address of device (IPIF) */ + u32 IsReady; /* Device is initialized and ready */ + u8 LastErrors; /* the accumulated errors */ + + XPs2Buffer SendBuffer; + XPs2Buffer ReceiveBuffer; + + XPs2_Handler Handler; + void *CallBackRef; /* Callback reference for control handler */ +} XPs2; + +/***************** Macros (Inline Functions) Definitions ********************/ + +/************************** Function Prototypes *****************************/ + +/* + * required functions is xps2.c + */ +XStatus XPs2_Initialize(XPs2 * InstancePtr, u16 DeviceId); + +unsigned int XPs2_Send(XPs2 * InstancePtr, u8 * BufferPtr, + unsigned int NumBytes); +unsigned int XPs2_Recv(XPs2 * InstancePtr, u8 * BufferPtr, + unsigned int NumBytes); +XPs2_Config *XPs2_LookupConfig(u16 DeviceId); + +/* + * options functions in xps2_options.c + */ +u8 XPs2_GetLastErrors(XPs2 * InstancePtr); +u32 XPs2_IsSending(XPs2 * InstancePtr); + +/* + * interrupt functions in xps2_intr.c + */ +void XPs2_SetHandler(XPs2 * InstancePtr, XPs2_Handler FuncPtr, + void *CallBackRef); +void XPs2_InterruptHandler(XPs2 * InstancePtr); +void XPs2_EnableInterrupt(XPs2 * InstancePtr); +void XPs2_DisableInterrupt(XPs2 * InstancePtr); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_keyb/xps2_g.c linuxppc-2.4/drivers/char/xilinx_keyb/xps2_g.c --- linux-2.4.22/drivers/char/xilinx_keyb/xps2_g.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_keyb/xps2_g.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,84 @@ +/* $Id: xps2_g.c,v 1.3 2002/07/17 20:58:58 ryserp Exp $ */ +/***************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +*****************************************************************************/ +/****************************************************************************/ +/** +* +* @file xps2_g.c +* +* This file contains a configuration table that specifies the configuration of +* PS/2 devices in the system. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   06/18/02 First release
+* 
+* +******************************************************************************/ +/***************************** Include Files ********************************/ + +#include "xps2.h" +#include "xparameters.h" + +/************************** Constant Definitions ****************************/ + +/**************************** Type Definitions ******************************/ + +/***************** Macros (Inline Functions) Definitions ********************/ + +/************************** Variable Definitions ****************************/ + +/* + * The configuration table for PS/2 devices in the table. Each + * device should have an entry in this table. + */ +XPs2_Config XPs2_ConfigTable[] = { + { + XPAR_PS2_0_DEVICE_ID, + XPAR_PS2_0_BASEADDR} + , + + { + XPAR_PS2_1_DEVICE_ID, + XPAR_PS2_1_BASEADDR} +}; + +/************************** Function Prototypes *****************************/ diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_keyb/xps2_i.h linuxppc-2.4/drivers/char/xilinx_keyb/xps2_i.h --- linux-2.4.22/drivers/char/xilinx_keyb/xps2_i.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_keyb/xps2_i.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,107 @@ +/****************************************************************************/ +/* $Id: xps2_i.h,v 1.2 2002/06/24 23:31:39 carsten Exp $ */ +/***************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +*****************************************************************************/ +/****************************************************************************/ +/** +* +* @file xps2_i.h +* +* This header file contains internal identifiers, which are those shared +* between the files of the driver. It is intended for internal use only. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   06/18/02 First release
+* 
+* +******************************************************************************/ +#ifndef XPS2_I_H /* prevent circular inclusions */ +#define XPS2_I_H /* by using protection macros */ + +/***************************** Include Files ********************************/ + +#include "xps2.h" + +/************************** Constant Definitions ****************************/ + +/**************************** Type Definitions ******************************/ + +/***************** Macros (Inline Functions) Definitions ********************/ + +/**************************************************************************** +* +* This macro clears the statistics of the component instance. The purpose of +* this macro is to allow common processing between the modules of the +* component with less overhead than a function in the required module. +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* Signature: void XPs2_mClearStats(XPs2 *InstancePtr) +* +*****************************************************************************/ +#define XPs2_mClearStats(InstancePtr) \ +{ \ + InstancePtr->Stats.TransmitInterrupts = 0UL; \ + InstancePtr->Stats.ReceiveInterrupts = 0UL; \ + InstancePtr->Stats.CharactersTransmitted = 0UL; \ + InstancePtr->Stats.CharactersReceived = 0UL; \ + InstancePtr->Stats.ReceiveErrors = 0UL; \ + InstancePtr->Stats.ReceiveOverflowErrors = 0UL; \ + InstancePtr->Stats.TransmitErrors = 0UL; \ +} + +/************************** Variable Definitions ****************************/ + +extern XPs2_Config XPs2_ConfigTable[]; + +/************************** Function Prototypes *****************************/ + +unsigned int XPs2_SendBuffer(XPs2 * InstancePtr); +unsigned int XPs2_ReceiveBuffer(XPs2 * InstancePtr); + +#endif diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_keyb/xps2_intr.c linuxppc-2.4/drivers/char/xilinx_keyb/xps2_intr.c --- linux-2.4.22/drivers/char/xilinx_keyb/xps2_intr.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_keyb/xps2_intr.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,494 @@ +/* $Id: xps2_intr.c,v 1.4 2002/06/26 23:19:28 carsten Exp $ */ +/***************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +*****************************************************************************/ +/****************************************************************************/ +/** +* +* @file xps2_intr.c +* +* This file contains the functions that are related to interrupt processing +* for the PS/2 driver. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   06/18/02 First release
+* 
+* +*****************************************************************************/ +/***************************** Include Files ********************************/ + +#include "xps2.h" +#include "xps2_i.h" +#include "xio.h" + +/************************** Constant Definitions ****************************/ + +/**************************** Type Definitions ******************************/ + +/***************** Macros (Inline Functions) Definitions ********************/ + +/************************** Variable Definitions ****************************/ + +typedef void (*Handler) (XPs2 * InstancePtr); + +/************************** Function Prototypes *****************************/ + +static void ReceiveDataHandler(XPs2 * InstancePtr); +static void ReceiveErrorHandler(XPs2 * InstancePtr); +static void ReceiveOverflowHandler(XPs2 * InstancePtr); +static void SendDataHandler(XPs2 * InstancePtr); +static void SendErrorHandler(XPs2 * InstancePtr); +static void TimeoutHandler(XPs2 * InstancePtr); + +/****************************************************************************/ +/** +* +* This function sets the handler that will be called when an event (interrupt) +* occurs in the driver. The purpose of the handler is to allow application +* specific processing to be performed. +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* @param FuncPtr is the pointer to the callback function. +* @param CallBackRef is the upper layer callback reference passed back when +* the callback function is invoked. +* +* @return +* +* None. +* +* @notes +* +* There is no assert on the CallBackRef since the driver doesn't know what it +* is (nor should it) +* +*****************************************************************************/ +void +XPs2_SetHandler(XPs2 * InstancePtr, XPs2_Handler FuncPtr, void *CallBackRef) +{ + /* + * Assert validates the input arguments + * CallBackRef not checked, no way to know what is valid + */ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(FuncPtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + InstancePtr->Handler = FuncPtr; + InstancePtr->CallBackRef = CallBackRef; +} + +/****************************************************************************/ +/** +* +* This function is the interrupt handler for the PS/2 driver. +* It must be connected to an interrupt system by the user such that it is +* called when an interrupt for any PS/2 port occurs. This function does +* not save or restore the processor context such that the user must +* ensure this occurs. +* +* @param InstancePtr contains a pointer to the instance of the PS/2 port +* that the interrupt is for. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XPs2_InterruptHandler(XPs2 * InstancePtr) +{ + u8 IntrStatus; + + XASSERT_VOID(InstancePtr != NULL); + + /* + * Read the interrupt status register to determine which + * interrupt is active + */ + IntrStatus = XPs2_mGetIntrStatus(InstancePtr->BaseAddress); + + if (IntrStatus & XPS2_INT_WDT_TOUT) { + TimeoutHandler(InstancePtr); + } + + if (IntrStatus & XPS2_INT_RX_ERR) { + ReceiveErrorHandler(InstancePtr); + } + + if (IntrStatus & XPS2_INT_RX_OVF) { + ReceiveOverflowHandler(InstancePtr); + } + + if (IntrStatus & XPS2_INT_TX_NOACK) { + SendErrorHandler(InstancePtr); + } + + if (IntrStatus & XPS2_INT_RX_FULL) { + ReceiveDataHandler(InstancePtr); + } + + if (IntrStatus & XPS2_INT_TX_ACK) { + SendDataHandler(InstancePtr); + } +} + +/****************************************************************************/ +/** +* +* This function enables the PS/2 interrupts. +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +*****************************************************************************/ +void +XPs2_EnableInterrupt(XPs2 * InstancePtr) +{ + /* + * ASSERT the arguments + */ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Enable all receiver interrupts (RX_FULL, RX_ERR, RX_OVF) + * transmitter interrupts are enabled when sending data. + */ + XPs2_mEnableIntr(InstancePtr->BaseAddress, XPS2_INT_RX_ALL); +} + +/****************************************************************************/ +/**void XPs2_DisableInterrupt +* +* This function disables the PS/2 interrupts. +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +*****************************************************************************/ +void +XPs2_DisableInterrupt(XPs2 * InstancePtr) +{ + /* + * ASSERT the arguments + */ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Disable all interrupts. + */ + XPs2_mDisableIntr(InstancePtr->BaseAddress, XPS2_INT_ALL); +} + +/****************************************************************************/ +/** +* +* This function handles the interrupt when data is received. +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +*****************************************************************************/ +static void +ReceiveDataHandler(XPs2 * InstancePtr) +{ + XPs2_mClearIntr(InstancePtr->BaseAddress, XPS2_INT_RX_FULL); + + /* + * If there are bytes still to be received in the specified buffer + * go ahead and receive them + */ + if (InstancePtr->ReceiveBuffer.RemainingBytes != 0) { + XPs2_ReceiveBuffer(InstancePtr); + } + + /* + * If the last byte of a message was received then call the application + * handler, this code should not use an else from the previous check of + * the number of bytes to receive because the call to receive the buffer + * updates the bytes to receive + */ + if (InstancePtr->ReceiveBuffer.RemainingBytes == 0) { + InstancePtr->Handler(InstancePtr->CallBackRef, + XPS2_EVENT_RECV_DATA, + InstancePtr->ReceiveBuffer.RequestedBytes - + InstancePtr->ReceiveBuffer.RemainingBytes); + } + + /* + * Update the receive stats to reflect the receive interrupt + */ + InstancePtr->Stats.ReceiveInterrupts++; +} + +/****************************************************************************/ +/** +* +* This function handles the receive error interrupt. +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +*****************************************************************************/ +static void +ReceiveErrorHandler(XPs2 * InstancePtr) +{ + XPs2_mClearIntr(InstancePtr->BaseAddress, XPS2_INT_RX_ERR); + + /* + * Call the application handler with an error code + */ + InstancePtr->Handler(InstancePtr->CallBackRef, XPS2_EVENT_RECV_ERROR, + InstancePtr->ReceiveBuffer.RequestedBytes - + InstancePtr->ReceiveBuffer.RemainingBytes); + + /* + * Update the LastError variable + */ + InstancePtr->LastErrors |= XPS2_ERROR_RX_ERR_MASK; + + /* + * Update the receive stats to reflect the receive error interrupt + */ + InstancePtr->Stats.ReceiveErrors++; +} + +/****************************************************************************/ +/** +* +* This function handles the receive overflow interrupt. +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +*****************************************************************************/ +static void +ReceiveOverflowHandler(XPs2 * InstancePtr) +{ + XPs2_mClearIntr(InstancePtr->BaseAddress, XPS2_INT_RX_OVF); + + /* + * Call the application handler with an error code + */ + InstancePtr->Handler(InstancePtr->CallBackRef, XPS2_EVENT_RECV_OVF, + InstancePtr->ReceiveBuffer.RequestedBytes - + InstancePtr->ReceiveBuffer.RemainingBytes); + + /* + * Update the LastError variable + */ + InstancePtr->LastErrors |= XPS2_ERROR_RX_OVF_MASK; + + /* + * Update the receive stats to reflect the receive interrupt + */ + InstancePtr->Stats.ReceiveOverflowErrors++; +} + +/****************************************************************************/ +/** +* +* This function handles the interrupt when data has been sent, the transmit +* transmitter holding register is empty. +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +*****************************************************************************/ +static void +SendDataHandler(XPs2 * InstancePtr) +{ + XPs2_mClearIntr(InstancePtr->BaseAddress, XPS2_INT_TX_ACK); + + /* + * If there are no bytes to be sent from the specified buffer then disable + * the transmit interrupt + */ + if (InstancePtr->SendBuffer.RemainingBytes == 0) { + XPs2_mDisableIntr(InstancePtr->BaseAddress, XPS2_INT_TX_ALL); + + /* + * Call the application handler to indicate the data has been sent + */ + InstancePtr->Handler(InstancePtr->CallBackRef, + XPS2_EVENT_SENT_DATA, + InstancePtr->SendBuffer.RequestedBytes - + InstancePtr->SendBuffer.RemainingBytes); + } + + /* + * Otherwise there is still more data to send in the specified buffer + * so go ahead and send it + */ + else { + XPs2_SendBuffer(InstancePtr); + } + + /* + * Update the transmit stats to reflect the transmit interrupt + */ + InstancePtr->Stats.TransmitInterrupts++; +} + +/****************************************************************************/ +/** +* +* This function handles the interrupt when a transmit is not acknowledged +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +*****************************************************************************/ +static void +SendErrorHandler(XPs2 * InstancePtr) +{ + XPs2_mClearIntr(InstancePtr->BaseAddress, XPS2_INT_TX_NOACK); + + /* + * Call the application handler + */ + InstancePtr->Handler(InstancePtr->CallBackRef, XPS2_EVENT_SENT_NOACK, + InstancePtr->SendBuffer.RequestedBytes - + InstancePtr->SendBuffer.RemainingBytes); + + /* + * Update the LastError variable + */ + InstancePtr->LastErrors |= XPS2_ERROR_TX_NOACK_MASK; + + /* + * Update the transmit stats to reflect the transmit interrupt + */ + InstancePtr->Stats.TransmitErrors++; +} + +/****************************************************************************/ +/** +* +* This function handles the interrupt when timeout occurrs +* +* @param InstancePtr is a pointer to the XPs2 instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +*****************************************************************************/ +static void +TimeoutHandler(XPs2 * InstancePtr) +{ + XPs2_mClearIntr(InstancePtr->BaseAddress, XPS2_INT_WDT_TOUT); + + /* + * Call the application handler + */ + InstancePtr->Handler(InstancePtr->CallBackRef, XPS2_EVENT_TIMEOUT, + InstancePtr->SendBuffer.RequestedBytes - + InstancePtr->SendBuffer.RemainingBytes); + + /* + * Update the LastError variable + */ + InstancePtr->LastErrors |= XPS2_ERROR_WDT_TOUT_MASK; + + /* + * Update the transmit stats to reflect the transmit interrupt + */ + InstancePtr->Stats.TransmitErrors++; +} diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_keyb/xps2_l.c linuxppc-2.4/drivers/char/xilinx_keyb/xps2_l.c --- linux-2.4.22/drivers/char/xilinx_keyb/xps2_l.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_keyb/xps2_l.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,116 @@ +/* $Id: xps2_l.c,v 1.1 2002/06/18 22:53:30 carsten Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xps2_l.c +* +* This file contains low-level driver functions that can be used to access the +* device. The user should refer to the hardware device specification for more +* details of the device operation. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   06/18/02 First release
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xps2_l.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/****************************************************************************/ +/** +* +* This function sends a data byte to PS/2. This function operates in the +* polling mode and blocks until the data has been put into the transmit +* holding register. +* +* @param BaseAddress contains the base address of the PS/2 port. +* @param Data contains the data byte to be sent. +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +void +XPs2_SendByte(u32 BaseAddress, u8 Data) +{ + while (XPs2_mIsTransmitFull(BaseAddress)) { + } + + XIo_Out8(BaseAddress + XPS2_TX_REG_OFFSET, Data); +} + +/****************************************************************************/ +/** +* +* This function receives a byte from PS/2. It operates in the polling mode +* and blocks until a byte of data is received. +* +* @param BaseAddress contains the base address of the PS/2 port. +* +* @return The data byte received by PS/2. +* +* @note None. +* +*****************************************************************************/ +u8 +XPs2_RecvByte(u32 BaseAddress) +{ + while (XPs2_mIsReceiveEmpty(BaseAddress)) { + } + + return (u8) XIo_In8(BaseAddress + XPS2_RX_REG_OFFSET); +} diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_keyb/xps2_l.h linuxppc-2.4/drivers/char/xilinx_keyb/xps2_l.h --- linux-2.4.22/drivers/char/xilinx_keyb/xps2_l.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_keyb/xps2_l.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,262 @@ +/* $Id: xps2_l.h,v 1.5 2002/07/23 04:17:07 ryserp Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xps2_l.h +* +* This header file contains identifiers and low-level driver functions (or +* macros) that can be used to access the device. The user should refer to the +* hardware device specification for more details of the device operation. +* High-level driver functions are defined in xps2.h. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   06/18/02 First release
+* 
+* +******************************************************************************/ + +#ifndef XPS2_L_H /* prevent circular inclusions */ +#define XPS2_L_H /* by using protection macros */ + +/***************************** Include Files ********************************/ + +#include "xbasic_types.h" +#include "xio.h" + +/************************** Constant Definitions ****************************/ + +/* PS/2 register offsets */ +#define XPS2_RESET_OFFSET 0 /* reset register, write only */ +#define XPS2_STATUS_OFFSET 4 /* status register, read only */ +#define XPS2_RX_REG_OFFSET 8 /* receive register, read only */ +#define XPS2_TX_REG_OFFSET 12 /* transmit register, write only */ +#define XPS2_INTSTA_REG_OFFSET 16 /* int status register, read only */ +#define XPS2_INTCLR_REG_OFFSET 20 /* int clear register, write only */ +#define XPS2_INTMSET_REG_OFFSET 24 /* mask set register, read/write */ +#define XPS2_INTMCLR_REG_OFFSET 28 /* mask clear register, write only */ + +/* reset register bit positions */ +#define XPS2_CLEAR_RESET 0x00 +#define XPS2_RESET 0x01 + +/* status register bit positions */ +#define XPS2_ST_RX_FULL 0x01 +#define XPS2_ST_TX_FULL 0x02 + +/* interrupt register bit positions */ +/* used for the INTSTA, INTCLR, INTMSET, INTMCLR register */ +#define XPS2_INT_WDT_TOUT 0x01 +#define XPS2_INT_TX_NOACK 0x02 +#define XPS2_INT_TX_ACK 0x04 +#define XPS2_INT_TX_ALL 0x06 +#define XPS2_INT_RX_OVF 0x08 +#define XPS2_INT_RX_ERR 0x10 +#define XPS2_INT_RX_FULL 0x20 +#define XPS2_INT_RX_ALL 0x38 +#define XPS2_INT_ALL 0x3f + +/**************************** Type Definitions ******************************/ + +/***************** Macros (Inline Functions) Definitions ********************/ + +/***************************************************************************** +* +* Low-level driver macros. The list below provides signatures to help the +* user use the macros. +* +* void XPs2_mReset(u32 BaseAddress) +* u8 XPs2_mGetStatus(u32 BaseAddress) +* +* u8 XPs2_mGetIntrStatus(u32 BaseAddress) +* void XPs2_mClearIntr(u32 BaseAddress, u8 ClearMask) +* u32 XPs2_mIsIntrEnabled(u32 BaseAddress, u8 EnabledMask) +* void XPs2_mEnableIntr(u32 BaseAddress, u8 EnableMask) +* void XPs2_mDisableIntr(u32 BaseAddress, u8 DisableMask) +* +* u32 XPs2_mIsReceiveEmpty(u32 BaseAddress) +* u32 XPs2_mIsTransmitFull(u32 BaseAddress) +* +*****************************************************************************/ + +/****************************************************************************/ +/** +* Reset the PS/2 port. +* +* @param BaseAddress contains the base address of the device. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +#define XPs2_mReset(BaseAddress) \ + XIo_Out8(((BaseAddress) + XPS2_RESET_OFFSET), XPS2_RESET); \ + XIo_Out8(((BaseAddress) + XPS2_RESET_OFFSET), XPS2_CLEAR_RESET) + +/****************************************************************************/ +/** +* Read the PS/2 status register. +* +* @param BaseAddress contains the base address of the device. +* +* @return The value read from the register. +* +* @note None. +* +******************************************************************************/ +#define XPs2_mGetStatus(BaseAddress) \ + (XIo_In8((BaseAddress) + XPS2_STATUS_OFFSET)) + +/****************************************************************************/ +/** +* Read the interrupt status register. +* +* @param BaseAddress contains the base address of the device. +* +* @return The value read from the register. +* +* @note None. +* +******************************************************************************/ +#define XPs2_mGetIntrStatus(BaseAddress) \ + (XIo_In8((BaseAddress) + XPS2_INTSTA_REG_OFFSET)) + +/****************************************************************************/ +/** +* Clear pending interrupts. +* +* @param BaseAddress contains the base address of the device. +* Bitmask for interrupts to be cleared. A "1" clears the interrupt. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +#define XPs2_mClearIntr(BaseAddress, ClearMask) \ + XIo_Out8((BaseAddress) + XPS2_INTCLR_REG_OFFSET, (ClearMask)) + +/****************************************************************************/ +/** +* Check for enabled interrupts. +* +* @param BaseAddress contains the base address of the device. +* Bitmask for interrupts to be checked. +* +* @return TRUE if the interrupt is enabled, FALSE otherwise. +* +* @note None. +* +******************************************************************************/ +#define XPs2_mIsIntrEnabled(BaseAddress, EnabledMask) \ + (XIo_In8((BaseAddress) + XPS2_INTMSET_REG_OFFSET) & (EnabledMask)) + +/****************************************************************************/ +/** +* Enable Interrupts. +* +* @param BaseAddress contains the base address of the device. +* Bitmask for interrupts to be enabled. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +#define XPs2_mEnableIntr(BaseAddress, EnableMask) \ + XIo_Out8((BaseAddress) + XPS2_INTMSET_REG_OFFSET, (EnableMask)) + +/****************************************************************************/ +/** +* Disable Interrupts. +* +* @param BaseAddress contains the base address of the device. +* Bitmask for interrupts to be disabled. +* +* @return None. +* +* @note None. +* +******************************************************************************/ +#define XPs2_mDisableIntr(BaseAddress, DisableMask) \ + XIo_Out8((BaseAddress) + XPS2_INTMCLR_REG_OFFSET, (DisableMask)) + +/****************************************************************************/ +/** +* Determine if there is receive data in the receiver. +* +* @param BaseAddress contains the base address of the device. +* +* @return TRUE if there is receive data, FALSE otherwise. +* +* @note None. +* +******************************************************************************/ +#define XPs2_mIsReceiveEmpty(BaseAddress) \ + (!(XPs2_mGetStatus(BaseAddress) & XPS2_ST_RX_FULL)) + +/****************************************************************************/ +/** +* Determine if a byte of data can be sent with the transmitter. +* +* @param BaseAddress contains the base address of the device. +* +* @return TRUE if a byte can be sent, FALSE otherwise. +* +* @note None. +* +******************************************************************************/ +#define XPs2_mIsTransmitFull(BaseAddress) \ + (XPs2_mGetStatus(BaseAddress) & XPS2_ST_TX_FULL) + +/************************** Variable Definitions ****************************/ + +/************************** Function Prototypes *****************************/ + +void XPs2_SendByte(u32 BaseAddress, u8 Data); +u8 XPs2_RecvByte(u32 BaseAddress); + +/****************************************************************************/ + +#endif diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_ts/Makefile linuxppc-2.4/drivers/char/xilinx_ts/Makefile --- linux-2.4.22/drivers/char/xilinx_ts/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_ts/Makefile 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,21 @@ +# +# Makefile for the Xilinx touchscreen driver +# + +EXTRA_CFLAGS += -I$(TOPDIR)/arch/ppc/platforms/xilinx_ocp + +list-multi := xilinx_ts.o + +# The Linux adapter for the Xilinx driver code. +xilinx_ts-objs += adapter.o + +# The Xilinx OS independent code. +xilinx_ts-objs += xtouchscreen.o xtouchscreen_g.o \ + xtouchscreen_intr.o xtouchscreen_l.o + +obj-$(CONFIG_XILINX_TS) := xilinx_ts.o + +xilinx_ts.o: $(xilinx_ts-objs) + $(LD) -r -o $@ $(xilinx_ts-objs) + +include $(TOPDIR)/Rules.make diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_ts/adapter.c linuxppc-2.4/drivers/char/xilinx_ts/adapter.c --- linux-2.4.22/drivers/char/xilinx_ts/adapter.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_ts/adapter.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,601 @@ +/* + * adapter.c + * + * Xilinx Adapter component to interface touchscreen component to Linux + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +/* + * Parts of this code were written using drivers/misc/ucb1x00-ts.c as a + * reference. ucb1x00-ts.c is: + * Copyright (C) 2001 Russell King, All Rights Reserved. + */ + +/* + * This driver is a bit unusual in that it is composed of two logical + * parts where one part is the OS independent code and the other part is + * the OS dependent code. Xilinx provides their drivers split in this + * fashion. This file represents the Linux OS dependent part known as + * the Linux adapter. The other files in this directory are the OS + * independent files as provided by Xilinx with no changes made to them. + * The names exported by those files begin with XTouchscreen_. All + * functions in this file that are called by Linux have names that begin + * with xts_. Any other functions are static helper functions. + */ + +/* + * CURRENT STATE OF AFFAIRS: + * The underlying driver and hardware have some quirks that need to be + * ironed out. I've seen good X and Y values with zero Z's. That is + * why this driver synthesizes Z as either zero or 255 and uses + * XTouchscreen_GetPosition_2D instead of XTouchscreen_GetPosition_3D. + * There seem to be linearity problems and troubles with dead zones + * around the edges of the screen. I believe that this could be a + * result of the issues outlined in the Burr-Brown (now Texas + * Instruments) "Touch Screen Controller Tips" application note for the + * ADS7846 touch screen controller which is used by the Xilinx ML300. + * The Xilinx touchscreen controls the ADS7846, but it doesn't give + * software enough access to the ADS7846 to implement the strategies + * outlined in the aforementioned application note. Xilinx is going to + * look into improving this. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "xtouchscreen.h" +#include "xtouchscreen_i.h" + +MODULE_AUTHOR("MontaVista Software, Inc. "); +MODULE_DESCRIPTION("Xilinx touchscreen driver"); +MODULE_LICENSE("GPL"); + +/* Dynamically allocate a major number. */ +static int xts_major = 0; +#define XTS_NAME "xilinx_ts" + +/* + * The definition of the following structure is copied from ucb1x00-ts.c so the + * touchscreen will "just work" with tslib, etc. + */ +/* + * This structure is nonsense - millisecs is not very useful + * since the field size is too small. Also, we SHOULD NOT + * be exposing jiffies to user space directly. + */ +struct ts_event { + u16 pressure; + u16 x; + u16 y; + u16 pad; + struct timeval stamp; +}; + +#define NR_EVENTS 16 /* Must be a power of two */ + +/* Our private per interface data. */ +struct xts_dev { + struct xts_dev *next_dev; /* The next device in dev_list */ + int index; /* Which interface is this */ + u32 save_BaseAddress; /* Saved physical address */ + unsigned int irq; /* device IRQ number */ + devfs_handle_t devfs_handle; + struct semaphore sem; + int use_count; /* How many things have us open */ + wait_queue_head_t read_wait; + struct fasync_struct *fasync; + struct ts_event events[NR_EVENTS]; + u8 evt_head; + u8 evt_tail; + /* + * The underlying OS independent code needs space as well. A + * pointer to the following XTouchscreen structure will be + * passed to any XTouchscreen_ function that requires it. + * However, we treat the data as an opaque object in this file + * (meaning that we never reference any of the fields inside of + * the structure). + */ + XTouchscreen Touchscreen; + /* + * The following bit fields are used to keep track of what + * all has been done to initialize the xts_dev to make + * error handling out of probe() easier. + */ + unsigned int remapped:1; /* Has ioremap() been called? */ + unsigned int pen_is_down:1; + unsigned int pen_was_down:1; /* Used to determine pen-up */ +}; + +/* + * List of devices we're handling and a lock to give us atomic access in + * anticipation of someday being able to hot add/remove. If this is done, the + * code that shoots the list in xts_thread will need to have protection added. + */ +static struct xts_dev *dev_list = NULL; +static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED; + +static devfs_handle_t devfs_dir = NULL; +struct task_struct *xts_task = NULL; /* xts_thread task pointer */ +static struct completion task_sync; /* xts_thread start/stop syncing */ +static char task_shutdown = 0; /* Set to non-zero when task should quit. */ +static wait_queue_head_t irq_wait; /* xts_thread waiting for interrupt */ + +#define event_pending(dev) ((volatile u8)(dev)->evt_head != (dev)->evt_tail) +#define event_get(dev) ((dev)->events + (dev)->evt_tail) +#define event_pull(dev) ((dev)->evt_tail = ((dev)->evt_tail + 1) & (NR_EVENTS - 1)) +#define event_clear(dev) ((dev)->evt_head = (dev)->evt_tail = 0) + +/* + * Add a new touch event as long as either x, y or z was different + * from the last touch event. + */ +static inline void +event_add(struct xts_dev *dev, u16 pressure, u16 x, u16 y) +{ + int next_head, prior_head; + + next_head = (dev->evt_head + 1) & (NR_EVENTS - 1); + prior_head = (dev->evt_head - 1) & (NR_EVENTS - 1); + if (next_head != dev->evt_tail + && (dev->events[prior_head].pressure != pressure + || dev->events[prior_head].x != x + || dev->events[prior_head].y != y)) + { + dev->events[dev->evt_head].pressure = pressure; + dev->events[dev->evt_head].x = x; + dev->events[dev->evt_head].y = y; + do_gettimeofday(&dev->events[dev->evt_head].stamp); + dev->evt_head = next_head; + + if (dev->fasync) + kill_fasync(&dev->fasync, SIGIO, POLL_IN); + wake_up_interruptible(&dev->read_wait); + } +} + +/* SAATODO: This function will be moved into the Xilinx code. */ +/*****************************************************************************/ +/** +* +* Lookup the device configuration based on the touchscreen instance. The table +* XTouchscreen_ConfigTable contains the configuration info for each device in +* the system. +* +* @param Instance is the index of the interface being looked up. +* +* @return +* +* A pointer to the configuration table entry corresponding to the given +* device ID, or NULL if no match is found. +* +* @note +* +* None. +* +******************************************************************************/ +XTouchscreen_Config * +XTouchscreen_GetConfig(int Instance) +{ + if (Instance < 0 || Instance >= XPAR_XTOUCHSCREEN_NUM_INSTANCES) { + return NULL; + } + + return &XTouchscreen_ConfigTable[Instance]; +} + +/* + * This routine is registered with the OS as the function to call when + * the touchscreen interrupts. It in turn, calls the Xilinx OS + * independent interrupt function. The Xilinx OS independent interrupt + * function will in turn call any callbacks that we have registered for + * various conditions. + */ +static void +xts_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct xts_dev *dev = dev_id; + XTouchscreen_InterruptHandler(&dev->Touchscreen); +} + +static void +Handler(void *CallbackRef, u32 Event, unsigned int EventData) +{ + struct xts_dev *dev = (struct xts_dev *) CallbackRef; + + switch (Event) { + case XTOUCHSCREEN_EVENT_PEN_DOWN: + dev->pen_is_down = 1; + wake_up(&irq_wait); /* Schedule our task */ + break; + case XTOUCHSCREEN_EVENT_PEN_UP: + dev->pen_is_down = 0; + break; + default: + printk(KERN_ERR "%s #%d: Unknown event %d.\n", XTS_NAME, + dev->index, Event); + break; + } +} + +/* + * This task waits until at least one touchscreen is touched. It then loops + * digitizing and generating events until no touchscreens are being touched. + */ +static int +xts_thread(void *arg) +{ + int any_pens_down; + struct xts_dev *dev; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + xts_task = tsk; + + daemonize(); + reparent_to_init(); + strcpy(xts_task->comm, XTS_NAME); + xts_task->tty = NULL; + + /* only want to receive SIGKILL */ + spin_lock_irq(&xts_task->sigmask_lock); + siginitsetinv(&xts_task->blocked, sigmask(SIGKILL)); + recalc_sigpending(xts_task); + spin_unlock_irq(&xts_task->sigmask_lock); + + complete(&task_sync); + + add_wait_queue(&irq_wait, &wait); + any_pens_down = 0; + for (;;) { + /* + * Block waiting for interrupt or if any pens are down, either + * an interrupt or timeout to sample again. + */ + set_current_state(TASK_INTERRUPTIBLE); + if (any_pens_down) + schedule_timeout(HZ / 100); + while (signal_pending(tsk)) { + siginfo_t info; + + /* Only honor the signal if we're cleaning up */ + if (task_shutdown) + goto exit; + /* + * Someone else sent us a kill (probably the + * shutdown scripts "Sending all processes the + * KILL signal"). Just dequeue it and ignore + * it. + */ + spin_lock_irq(¤t->sigmask_lock); + (void)dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + } + schedule(); + + any_pens_down = 0; + for (dev = dev_list; dev; dev = dev->next_dev) { + if (dev->pen_is_down) { + u32 x, y; + XTouchscreen_GetPosition_2D(&dev->Touchscreen, + &x, &y); + event_add(dev, 255, (u16) x, (u16) y); + dev->pen_was_down = 1; + any_pens_down = 1; + } else if (dev->pen_was_down) { + event_add(dev, 0, 0, 0); + dev->pen_was_down = 0; + } + } + } + +exit: + remove_wait_queue(&irq_wait, &wait); + + xts_task = NULL; + complete_and_exit(&task_sync, 0); +} + +static ssize_t +xts_read(struct file *filp, char *buffer, size_t count, loff_t * ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct xts_dev *dev = filp->private_data; + char *ptr = buffer; + int err = 0; + + add_wait_queue(&dev->read_wait, &wait); + while (count >= sizeof (struct ts_event)) { + err = -ERESTARTSYS; + if (signal_pending(current)) + break; + + if (event_pending(dev)) { + struct ts_event *evt = event_get(dev); + + err = copy_to_user(ptr, evt, sizeof (struct ts_event)); + event_pull(dev); + + if (err) + break; + + ptr += sizeof (struct ts_event); + count -= sizeof (struct ts_event); + continue; + } + + set_current_state(TASK_INTERRUPTIBLE); + err = -EAGAIN; + if (filp->f_flags & O_NONBLOCK) + break; + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->read_wait, &wait); + + return ptr == buffer ? err : ptr - buffer; +} +static unsigned int +xts_poll(struct file *filp, poll_table * wait) +{ + struct xts_dev *dev = filp->private_data; + int ret = 0; + + poll_wait(filp, &dev->read_wait, wait); + if (event_pending(dev)) + ret = POLLIN | POLLRDNORM; + + return ret; +} +static int +xts_open(struct inode *inode, struct file *filp) +{ + struct xts_dev *dev = filp->private_data; + + /* If dev is NULL, devfs must not be being used; find the right dev. */ + if (!dev) { + dev = dev_list; + while (dev && dev->index != MINOR(inode->i_rdev)) + dev = dev->next_dev; + if (!dev) + return -ENODEV; + else + filp->private_data = dev; + } + + if (down_interruptible(&dev->sem)) + return -EINTR; + + if (dev->use_count++ == 0) { + /* This was the first opener; we need to get the IRQ. */ + int retval; + retval = request_irq(dev->irq, xts_interrupt, 0, XTS_NAME, dev); + if (retval) { + printk(KERN_ERR + "%s #%d: Could not allocate interrupt %d.\n", + XTS_NAME, dev->index, dev->irq); + dev->use_count--; + up(&dev->sem); + return retval; + } + } + + up(&dev->sem); + return 0; +} +static int +xts_release(struct inode *inode, struct file *filp) +{ + struct xts_dev *dev = filp->private_data; + + if (down_interruptible(&dev->sem)) + return -EINTR; + + if (--dev->use_count == 0) { + /* This was the last closer; get rid of the IRQ. */ + disable_irq(dev->irq); + free_irq(dev->irq, dev); + } + + up(&dev->sem); + return 0; +} +static int +xts_fasync(int fd, struct file *filp, int on) +{ + struct xts_dev *dev = filp->private_data; + + return fasync_helper(fd, filp, on, &dev->fasync); +} + +static void +remove_head_dev(void) +{ + struct xts_dev *dev; + XTouchscreen_Config *cfg; + + /* Pull the head off of dev_list. */ + spin_lock(&dev_lock); + dev = dev_list; + dev_list = dev->next_dev; + spin_unlock(&dev_lock); + + if (dev->devfs_handle) + devfs_unregister(dev->devfs_handle); + + if (dev->remapped) { + cfg = XTouchscreen_GetConfig(dev->index); + iounmap((void *) cfg->BaseAddress); + cfg->BaseAddress = dev->save_BaseAddress; + }; + + kfree(dev); +} + +static struct file_operations fops = { + owner:THIS_MODULE, + read:xts_read, + poll:xts_poll, + open:xts_open, + release:xts_release, + fasync:xts_fasync, +}; + +static void /* __exit removed because xts_init calls this */ +xts_cleanup(void) +{ + while (dev_list) + remove_head_dev(); + + if (xts_task) { + task_shutdown = 1; + send_sig(SIGKILL, xts_task, 1); + wait_for_completion(&task_sync); + } + if (devfs_dir) { + devfs_unregister(devfs_dir); + devfs_dir = NULL; + } + if (devfs_unregister_chrdev(xts_major, XTS_NAME) != 0) + printk(KERN_ERR "%s: unable to release major %d.\n", + XTS_NAME, xts_major); +} + +static int __init +probe(int index) +{ + static const unsigned long remap_size + = XPAR_TOUCHSCREEN_0_HIGHADDR - XPAR_TOUCHSCREEN_0_BASEADDR + 1; + struct xts_dev *dev; + XTouchscreen_Config *cfg; + unsigned int irq; + char name[20]; + + switch (index) { +#if defined(XPAR_INTC_0_TOUCHSCREEN_0_VEC_ID) + case 0: + irq = 31 - XPAR_INTC_0_TOUCHSCREEN_0_VEC_ID; + break; +#if defined(XPAR_INTC_0_TOUCHSCREEN_1_VEC_ID) + case 1: + irq = 31 - XPAR_INTC_0_TOUCHSCREEN_1_VEC_ID; + break; +#if defined(XPAR_INTC_0_TOUCHSCREEN_2_VEC_ID) + case 2: + irq = 31 - XPAR_INTC_0_TOUCHSCREEN_2_VEC_ID; + break; +#if defined(XPAR_INTC_0_TOUCHSCREEN_3_VEC_ID) +#error Edit this file to add more devices. +#endif /* 3 */ +#endif /* 2 */ +#endif /* 1 */ +#endif /* 0 */ + default: + return -ENODEV; + } + + /* Find the config for our device. */ + cfg = XTouchscreen_GetConfig(index); + if (!cfg) + return -ENODEV; + + /* Allocate the dev and zero it out. */ + dev = (struct xts_dev *) kmalloc(sizeof (struct xts_dev), GFP_KERNEL); + if (!dev) { + printk(KERN_ERR "%s #%d: Could not allocate device.\n", + XTS_NAME, index); + return -ENOMEM; + } + memset(dev, 0, sizeof (struct xts_dev)); + dev->index = index; + + /* Make it the head of dev_list. */ + spin_lock(&dev_lock); + dev->next_dev = dev_list; + dev_list = dev; + spin_unlock(&dev_lock); + + /* Change the addresses to be virtual; save the old ones to restore. */ + dev->save_BaseAddress = cfg->BaseAddress; + cfg->BaseAddress = (u32) ioremap(dev->save_BaseAddress, remap_size); + dev->remapped = 1; + + /* Tell the Xilinx code to bring this touchscreen interface up. */ + if (XTouchscreen_Initialize(&dev->Touchscreen, + cfg->DeviceId) != XST_SUCCESS) { + printk(KERN_ERR "%s #%d: Could not initialize device.\n", + XTS_NAME, dev->index); + remove_head_dev(); + return -ENODEV; + } + + XTouchscreen_SetHandler(&dev->Touchscreen, Handler, (void *) dev); + + /* We'll do the request_irq in open. */ + dev->irq = irq; + + init_MUTEX(&dev->sem); + init_waitqueue_head(&dev->read_wait); + sprintf(name, "xilinx%u", index); + dev->devfs_handle = devfs_register(devfs_dir, name, DEVFS_FL_DEFAULT, + xts_major, index, + S_IFCHR | S_IRUGO | S_IWUGO, &fops, + dev); + + printk(KERN_INFO "%s #%d at 0x%08X mapped to 0x%08X, irq=%d\n", + XTS_NAME, dev->index, + dev->save_BaseAddress, cfg->BaseAddress, dev->irq); + + return 0; +} + +static int __init +xts_init(void) +{ + int index = 0; + int retval; + + if ((retval = devfs_register_chrdev(xts_major, XTS_NAME, &fops)) < 0) { + printk(KERN_ERR "%s: unable to register major %d.\n", XTS_NAME, + xts_major); + return retval; + } + if (xts_major == 0) + xts_major = retval; + + devfs_dir = devfs_mk_dir(NULL, "touchscreen", NULL); + + /* Try to initialize all the touchscreens. */ + while (probe(index++) == 0) ; + + if (index > 1) { + /* We found at least one. */ + init_completion(&task_sync); + init_waitqueue_head(&irq_wait); + if ((retval = kernel_thread(xts_thread, 0, 0)) >= 0) { + wait_for_completion(&task_sync); + return 0; + } + } else + retval = -ENODEV; + + /* None found or error starting thread, clean up and get out of here. */ + xts_cleanup(); + return retval; +} + +EXPORT_NO_SYMBOLS; + +module_init(xts_init); +module_exit(xts_cleanup); diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen.c linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen.c --- linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,290 @@ +/* $Id: xtouchscreen.c,v 1.4 2002/10/25 23:18:10 carsten Exp $ */ +/***************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +*****************************************************************************/ +/****************************************************************************/ +/** +* +* @file xtouchscreen.c +* +* This file contains the required functions for the touchscreen driver. +* Refer to the header file xtouchscreen.h for more detailed information. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   08/15/02 First release
+* 
+* +*****************************************************************************/ + +/***************************** Include Files ********************************/ + +#include +#include "xstatus.h" +#include "xparameters.h" +#include "xtouchscreen.h" +#include "xtouchscreen_i.h" +#include "xtouchscreen_l.h" +#include "xio.h" + +/************************** Constant Definitions ****************************/ + +/**************************** Type Definitions ******************************/ + +/***************** Macros (Inline Functions) Definitions ********************/ + +/************************** Variable Definitions ****************************/ + +/************************** Function Prototypes *****************************/ + +static void XTouchscreen_StubHandler(void *CallBackRef, u32 Event, + unsigned int ByteCount); + +/****************************************************************************/ +/** +* +* Initializes a specific touchscreen instance such that it is ready to be used. +* The default operating mode of the driver is polled mode. +* +* @param InstancePtr is a pointer to the XTouchscreen instance to be +* worked on. +* @param DeviceId is the unique id of the device controlled by this +* XTouchscreen instance. Passing in a device id associates the +* generic XTouchscreen instance to a specific device, as chosen +* by the caller or application developer. +* +* @return +* +* - XST_SUCCESS if initialization was successful +* - XST_DEVICE_NOT_FOUND if the device ID could not be found in the +* configuration table +* +* @note +* +* None. +* +*****************************************************************************/ +XStatus +XTouchscreen_Initialize(XTouchscreen * InstancePtr, u16 DeviceId) +{ + XTouchscreen_Config *TouchscreenConfigPtr; + + /* + * Assert validates the input arguments + */ + XASSERT_NONVOID(InstancePtr != NULL); + + /* + * Lookup the device configuration in the temporary CROM table. Use this + * configuration info down below when initializing this component. + */ + TouchscreenConfigPtr = XTouchscreen_LookupConfig(DeviceId); + + if (TouchscreenConfigPtr == (XTouchscreen_Config *) NULL) { + return XST_DEVICE_NOT_FOUND; + } + + /* + * Setup the data that is from the configuration information + */ + InstancePtr->BaseAddress = TouchscreenConfigPtr->BaseAddress; + + /* + * Initialize the instance data to some default values and setup a default + * handler + */ + InstancePtr->Handler = XTouchscreen_StubHandler; + + InstancePtr->CurrentState = XTOUCHSCREEN_STATE_PEN_UP; + + /* + * Enable Touchscreen + */ + XTouchscreen_mWriteCtrlReg(InstancePtr->BaseAddress, 0xd3); + udelay(100); + XTouchscreen_mWriteCtrlReg(InstancePtr->BaseAddress, 0xd0); + udelay(100); + + /* + * Clear all interrupts + */ + XTouchscreen_mClearIntr(InstancePtr->BaseAddress, + XTOUCHSCREEN_INT_PEN_DOWN | + XTOUCHSCREEN_INT_PEN_UP); + + /* + * Indicate the instance is now ready to use, initialized without error + */ + InstancePtr->IsReady = XCOMPONENT_IS_READY; + + return XST_SUCCESS; +} + +/****************************************************************************/ +/** +* +* Looks up the device configuration based on the unique device ID. A table +* contains the configuration info for each device in the system. +* +* @param DeviceId contains the ID of the device to look up the configuration +* for. +* +* @return +* +* A pointer to the configuration found or NULL if the specified device ID was +* not found. +* +* @note +* +* None. +* +******************************************************************************/ +XTouchscreen_Config * +XTouchscreen_LookupConfig(u16 DeviceId) +{ + XTouchscreen_Config *CfgPtr = NULL; + + int i; + + for (i = 0; i < XPAR_XTOUCHSCREEN_NUM_INSTANCES; i++) { + if (XTouchscreen_ConfigTable[i].DeviceId == DeviceId) { + CfgPtr = &XTouchscreen_ConfigTable[i]; + } + } + + return CfgPtr; +} + +/****************************************************************************/ +/** +* +* This function reads 2D (X & Y) coordinates from the touchscreen. +* +* @param InstancePtr is a pointer to the XTouchscreen instance to be +* worked on. +* @param *x pointer to store the x coordinate. +* *y pointer to store the y coordinate. +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +void +XTouchscreen_GetPosition_2D(XTouchscreen * InstancePtr, u32 * x, u32 * y) +{ + *x = XTouchscreen_GetValue(InstancePtr->BaseAddress, + XTOUCHSCREEN_CTRL_CHSEL_X); + *y = XTouchscreen_GetValue(InstancePtr->BaseAddress, + XTOUCHSCREEN_CTRL_CHSEL_Y); +} + +/****************************************************************************/ +/** +* +* This function reads 3D (X, Y & Z) coordinates from the touchscreen. x and +* y are the actual positions, the pressure has to be calculated from z1 and +* z2 +* +* @param InstancePtr is a pointer to the XTouchscreen instance to be +* worked on. +* @param *x pointer to store the x coordinate. +* *y pointer to store the y coordinate. +* *z pointer to store the z coordinate (pressure) +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +void +XTouchscreen_GetPosition_3D(XTouchscreen * InstancePtr, u32 * x, + u32 * y, u32 * z) +{ + u32 z1; + u32 z2; + + *x = XTouchscreen_GetValue(InstancePtr->BaseAddress, + XTOUCHSCREEN_CTRL_CHSEL_X); + *y = XTouchscreen_GetValue(InstancePtr->BaseAddress, + XTOUCHSCREEN_CTRL_CHSEL_Y); + + z1 = XTouchscreen_GetValue(InstancePtr->BaseAddress, + XTOUCHSCREEN_CTRL_CHSEL_Z1); + z2 = XTouchscreen_GetValue(InstancePtr->BaseAddress, + XTOUCHSCREEN_CTRL_CHSEL_Z2); + + if (z1 == 0) { + *z = XTOUCHSCREEN_SAMPLE_ERROR; + return; + } else if (z1 > z2) { + *z = XTOUCHSCREEN_SAMPLE_ERROR; + return; + } else { + *z = ((*x) * (z2 / z1 - 1)); + } +} + +/****************************************************************************/ +/** +* +* This function is a stub handler that is the default handler such that if the +* application has not set the handler when interrupts are enabled, this +* function will be called. The function interface has to match the interface +* specified for a handler even though none of the arguments are used. +* +* @param CallBackRef is unused by this function. +* @param Event is unused by this function. +* @param ByteCount is unused by this function. +* +* @return +* +* None. +* +* @note +* +* None. +* +*****************************************************************************/ +static void +XTouchscreen_StubHandler(void *CallBackRef, u32 Event, unsigned int ByteCount) +{ + XASSERT_VOID(FALSE); +} diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen.h linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen.h --- linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,157 @@ +/* $Id: xtouchscreen.h,v 1.4 2002/10/25 23:18:10 carsten Exp $ */ +/***************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +*****************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xtouchscreen.h +* +* This driver supports the following features: +* +* - Polled mode +* - Interrupt driven mode +* +* Interrupts +* +* In order to use interrupts, it is necessary for the user to connect the +* driver interrupt handler, XTouchscreen_InterruptHandler(), to the interrupt +* system of the application. This function does not save and restore the +* processor context such that the user must provide it. A handler must be set +* for the driver such that the handler is called when interrupt events occur. +* The handler is called from interrupt context and is designed to allow +* application specific processing to be performed. +* +* @note +* +* None. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   08/15/02 First release
+* 
+* +******************************************************************************/ + +#ifndef XTOUCHSCREEN_H /* prevent circular inclusions */ +#define XTOUCHSCREEN_H /* by using protection macros */ + +/***************************** Include Files ********************************/ + +#include "xbasic_types.h" +#include "xstatus.h" +#include "xtouchscreen_l.h" + +/************************** Constant Definitions ****************************/ + +/* + * These constants specify the handler events that are passed to + * a handler from the driver. These constants are not bit masks suuch that + * only one will be passed at a time to the handler + */ +#define XTOUCHSCREEN_EVENT_PEN_UP 1 +#define XTOUCHSCREEN_EVENT_PEN_DOWN 2 + +/* + * These constants are used to specify the current pen state. + * They are only used internally. + */ +#define XTOUCHSCREEN_STATE_PEN_UP 1 +#define XTOUCHSCREEN_STATE_PEN_DOWN 2 + +/**************************** Type Definitions ******************************/ + +/* + * This typedef contains configuration information for the device + */ +typedef struct { + u16 DeviceId; + u32 BaseAddress; +} XTouchscreen_Config; + +/* + * This data type defines a handler which the application must define + * when using interrupt mode. The handler will be called from the driver in an + * interrupt context to handle application specific processing. + * + * @param CallBackRef is a callback reference passed in by the upper layer + * when setting the handler, and is passed back to the upper layer when + * the handler is called. + * @param Event contains one of the event constants indicating why the handler + * is being called. + * @param EventData contains the number of bytes sent or received at the time +* of the call. +*/ +typedef void (*XTouchscreen_Handler) (void *CallBackRef, u32 Event, + unsigned int EventData); +/* + * The Touchscreen driver instance data. The user is required to allocate a + * variable of this type for every touchscreen device in the system. + * A pointer to a variable of this type is then passed to the driver API + * functions + */ +typedef struct { + u32 BaseAddress; + u32 IsReady; + u32 CurrentState; + XTouchscreen_Handler Handler; + void *CallBackRef; +} XTouchscreen; + +/***************** Macros (Inline Functions) Definitions ********************/ + +/************************** Function Prototypes *****************************/ + +/* + * required functions is xtouchscreen.c + */ +XStatus XTouchscreen_Initialize(XTouchscreen * InstancePtr, u16 DeviceId); +XTouchscreen_Config *XTouchscreen_LookupConfig(u16 DeviceId); +void XTouchscreen_GetPosition_2D(XTouchscreen * InstancePtr, u32 * x, u32 * y); +void XTouchscreen_GetPosition_3D(XTouchscreen * InstancePtr, u32 * x, + u32 * y, u32 * z); +/* + * interrupt functions in xtouchscreen_intr.c + */ +void XTouchscreen_SetHandler(XTouchscreen * InstancePtr, + XTouchscreen_Handler FuncPtr, void *CallBackRef); +void XTouchscreen_InterruptHandler(XTouchscreen * InstancePtr); + +#endif diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen_g.c linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen_g.c --- linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen_g.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen_g.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,80 @@ +/* $Id: xtouchscreen_g.c,v 1.1 2002/08/15 22:17:01 carsten Exp $ */ +/***************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +*****************************************************************************/ +/****************************************************************************/ +/** +* +* @file xtouchscreen_g.c +* +* This file contains a configuration table that specifies the configuration of +* touchscreen devices in the system. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   08/15/02 First release
+* 
+* +******************************************************************************/ + +/***************************** Include Files ********************************/ + +#include "xtouchscreen.h" +#include "xparameters.h" + +/************************** Constant Definitions ****************************/ + +/**************************** Type Definitions ******************************/ + +/***************** Macros (Inline Functions) Definitions ********************/ + +/************************** Variable Definitions ****************************/ + +/* + * The configuration table for touchscreen devices in the system. Each + * device should have an entry in this table. + */ +XTouchscreen_Config XTouchscreen_ConfigTable[] = { + { + XPAR_TOUCHSCREEN_0_DEVICE_ID, + XPAR_TOUCHSCREEN_0_BASEADDR} +}; + +/************************** Function Prototypes *****************************/ diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen_i.h linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen_i.h --- linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen_i.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen_i.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,76 @@ +/****************************************************************************/ +/* $Id: xtouchscreen_i.h,v 1.1 2002/08/15 22:17:01 carsten Exp $ */ +/***************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +*****************************************************************************/ +/****************************************************************************/ +/** +* +* @file xtouchscreen_i.h +* +* This header file contains internal identifiers, which are those shared +* between the files of the driver. It is intended for internal use only. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   08/15/02 First release
+* 
+* +******************************************************************************/ +#ifndef XTOUCHSCREEN_I_H /* prevent circular inclusions */ +#define XTOUCHSCREEN_I_H /* by using protection macros */ + +/***************************** Include Files ********************************/ + +#include "xtouchscreen.h" + +/************************** Constant Definitions ****************************/ + +/**************************** Type Definitions ******************************/ + +/***************** Macros (Inline Functions) Definitions ********************/ + +/************************** Variable Definitions ****************************/ + +extern XTouchscreen_Config XTouchscreen_ConfigTable[]; + +/************************** Function Prototypes *****************************/ + +#endif diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen_intr.c linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen_intr.c --- linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen_intr.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen_intr.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,187 @@ +/* $Id: xtouchscreen_intr.c,v 1.1 2002/08/15 22:17:01 carsten Exp $ */ +/***************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +*****************************************************************************/ +/****************************************************************************/ +/** +* +* @file xtouchscreen_intr.c +* +* This file contains the functions that are related to interrupt processing +* for the touchscreen driver. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   08/15/02 First release
+* 
+* +*****************************************************************************/ + +/***************************** Include Files ********************************/ + +#include "xio.h" +#include "xtouchscreen.h" +#include "xtouchscreen_l.h" + +/************************** Constant Definitions ****************************/ + +/**************************** Type Definitions ******************************/ + +/***************** Macros (Inline Functions) Definitions ********************/ + +/************************** Variable Definitions ****************************/ + +typedef void (*Handler) (XTouchscreen * InstancePtr); + +/************************** Function Prototypes *****************************/ + +/****************************************************************************/ +/** +* +* This function sets the handler that will be called when an event (interrupt) +* occurs in the driver. The purpose of the handler is to allow application +* specific processing to be performed. +* +* @param InstancePtr is a pointer to the XTouchscreen instance to be +* worked on. +* @param FuncPtr is the pointer to the callback function. +* @param CallBackRef is the upper layer callback reference passed back when +* the callback function is invoked. +* +* @return +* +* None. +* +* @notes +* +* There is no assert on the CallBackRef since the driver doesn't know what it +* is (nor should it) +* +*****************************************************************************/ +void +XTouchscreen_SetHandler(XTouchscreen * InstancePtr, + XTouchscreen_Handler FuncPtr, void *CallBackRef) +{ + /* + * Assert validates the input arguments + * CallBackRef not checked, no way to know what is valid + */ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(FuncPtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + InstancePtr->Handler = FuncPtr; + InstancePtr->CallBackRef = CallBackRef; +} + +/****************************************************************************/ +/** +* +* This function is the interrupt handler for the Touchscreen driver. +* It must be connected to an interrupt system by the user such that it is +* called when an interrupt for any PS/2 port occurs. This function does +* not save or restore the processor context such that the user must +* ensure this occurs. +* +* @param InstancePtr contains a pointer to the Touchscreen instance. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XTouchscreen_InterruptHandler(XTouchscreen * InstancePtr) +{ + u8 IntrStatus; + // u32 x,y,z; + + XASSERT_VOID(InstancePtr != NULL); + + /* + * Read the interrupt status register to determine which + * interrupt is active + */ + IntrStatus = XTouchscreen_mGetIntrStatus(InstancePtr->BaseAddress); + + if (IntrStatus & XTOUCHSCREEN_INT_PEN_DOWN) { + /* + * Check if the pen is alreay down. This is done for debouncing + */ + if (!(InstancePtr->CurrentState == XTOUCHSCREEN_STATE_PEN_DOWN)) { + /* + * Call the application handler + */ + InstancePtr->Handler(InstancePtr->CallBackRef, + XTOUCHSCREEN_EVENT_PEN_DOWN, 0); + } + /* + * Save the current state + */ + InstancePtr->CurrentState = XTOUCHSCREEN_STATE_PEN_DOWN; + + XTouchscreen_mClearIntr(InstancePtr->BaseAddress, + XTOUCHSCREEN_INT_PEN_DOWN); + } + + if (IntrStatus & XTOUCHSCREEN_INT_PEN_UP) { + /* + * Check if the pen is alreay up. This is done for debouncing + */ + if (!(InstancePtr->CurrentState == XTOUCHSCREEN_STATE_PEN_UP)) { + /* + * Call the application handler + */ + InstancePtr->Handler(InstancePtr->CallBackRef, + XTOUCHSCREEN_EVENT_PEN_UP, 0); + } + /* + * Save the current pen state + */ + InstancePtr->CurrentState = XTOUCHSCREEN_STATE_PEN_UP; + + XTouchscreen_mClearIntr(InstancePtr->BaseAddress, + XTOUCHSCREEN_INT_PEN_UP); + } +} diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen_l.c linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen_l.c --- linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen_l.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen_l.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,103 @@ +/* $Id: xtouchscreen_l.c,v 1.3 2002/10/24 00:01:12 carsten Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +*****************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xtouchscreen_l.c +* +* This file contains low-level driver functions that can be used to access the +* device. The user should refer to the hardware device specification for more +* details of the device operation. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   08/08/02 First release
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include +#include "xtouchscreen_l.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/****************************************************************************/ +/** +* +* This function reads a digitized value from the touchscreen controller. +* +* @param BaseAddress contains the base address of the touchscreen +* controller. +* @param Channel addresses the channel to be read. +* +* @return Digitized value of the addressed channel. +* +* @note None. +* +*****************************************************************************/ +u32 +XTouchscreen_GetValue(u32 BaseAddress, u8 Channel) +{ + u32 ReadVal; + + XTouchscreen_mWriteCtrlReg(BaseAddress, Channel); + udelay(100); + ReadVal = XTouchscreen_mReadCtrlReg(BaseAddress); + XTouchscreen_mWriteCtrlReg(BaseAddress, 0xd0); + + /* + * Shift right 16bits, only the upper halfword is valid + */ + ReadVal >>= 16; + return ReadVal; +} diff -urN -X bkexcl linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen_l.h linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen_l.h --- linux-2.4.22/drivers/char/xilinx_ts/xtouchscreen_l.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/char/xilinx_ts/xtouchscreen_l.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,169 @@ +/* $Id: xtouchscreen_l.h,v 1.3 2002/10/24 00:01:12 carsten Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR +* OBTAINING ANY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx products are not intended for use in life support appliances, +* devices, or systems. Use in such applications is expressly prohibited. +* +* +* (c) Copyright 2002 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +*****************************************************************************/ +/****************************************************************************/ +/** +* +* @file xtouchscreen_l.h +* +* This header file contains identifiers and low-level driver functions (or +* macros) that can be used to access the device. The user should refer to the +* hardware device specification for more details of the device operation. +* High-level driver functions are defined in xtouchscreen.h. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a ch   08/08/02 First release
+* 
+* +*****************************************************************************/ + +#ifndef XTOUCHSCREEN_L_H /* prevent circular inclusions */ +#define XTOUCHSCREEN_L_H /* by using protection macros */ + +/***************************** Include Files ********************************/ + +#include "xbasic_types.h" +#include "xio.h" + +/************************** Constant Definitions ****************************/ + +/* Register offsets */ +#define XTOUCHSCREEN_CTRL_REG_OFFSET 0 +#define XTOUCHSCREEN_INTR_REG_OFFSET 4 + +/* control register channel selectors */ +#define XTOUCHSCREEN_CTRL_CHSEL_X 0xd3 +#define XTOUCHSCREEN_CTRL_CHSEL_Y 0x93 +#define XTOUCHSCREEN_CTRL_CHSEL_Z1 0xb3 +#define XTOUCHSCREEN_CTRL_CHSEL_Z2 0xc3 + +/* interrupt register positions */ +#define XTOUCHSCREEN_INT_PEN_UP 0x01 +#define XTOUCHSCREEN_INT_PEN_DOWN 0x02 + +/* error reading touchscreen */ +#define XTOUCHSCREEN_SAMPLE_ERROR 0xffffffff + +/**************************** Type Definitions ******************************/ + +/***************** Macros (Inline Functions) Definitions ********************/ + +/***************************************************************************** +* +* Low-level driver macros. The list below provides signatures to help the +* user use the macros. +* +* u32 XTouchscreen_mReadCtrlReg(u32 BaseAddress) +* void XTouchscreen_mWriteCtrlReg(u32 BaseAddress, u8 Value) +* +* u32 XTouchscreen_mGetIntrStatus(u32 BaseAddress) +* void XTouchscreen_mClearIntr(u32 BaseAddress, u32 ClearMask) +* +*****************************************************************************/ + +/****************************************************************************/ +/** +* Read the control register. +* +* @param BaseAddress contains the base address of the device. +* +* @return The value read from the register. +* +* @note None. +* +*****************************************************************************/ +#define XTouchscreen_mReadCtrlReg(BaseAddress) \ + XIo_In32((BaseAddress) + XTOUCHSCREEN_CTRL_REG_OFFSET) + +/****************************************************************************/ +/** +* Write to the control register +* +* @param BaseAddress contains the base address of the device. + Value to be written +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +#define XTouchscreen_mWriteCtrlReg(BaseAddress, Value) \ + XIo_Out8((BaseAddress) + XTOUCHSCREEN_CTRL_REG_OFFSET, Value) + +/****************************************************************************/ +/** +* Read the interrupt status register. +* +* @param BaseAddress contains the base address of the device. +* +* @return The value read from the register. +* +* @note None. +* +*****************************************************************************/ +#define XTouchscreen_mGetIntrStatus(BaseAddress) \ + XIo_In32((BaseAddress) + XTOUCHSCREEN_INTR_REG_OFFSET) + +/****************************************************************************/ +/** +* Clear pending interrupts. +* +* @param BaseAddress contains the base address of the device. +* Bitmask for interrupts to be cleared. A "1" clears the interrupt. +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +#define XTouchscreen_mClearIntr(BaseAddress, ClearMask) \ + XIo_Out32((BaseAddress) + XTOUCHSCREEN_INTR_REG_OFFSET, ClearMask) + +/************************** Variable Definitions ****************************/ + +/************************** Function Prototypes *****************************/ + +u32 XTouchscreen_GetValue(u32 BaseAddress, u8 Channel); + +/****************************************************************************/ + +#endif diff -urN -X bkexcl linux-2.4.22/drivers/i2c/Config.in linuxppc-2.4/drivers/i2c/Config.in --- linux-2.4.22/drivers/i2c/Config.in 2003-08-24 09:04:15.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/Config.in 2003-08-26 11:57:12.000000000 +1000 @@ -38,11 +38,12 @@ dep_tristate ' Embedded Planet RPX Lite/Classic suppoort' CONFIG_I2C_RPXLITE $CONFIG_I2C_ALGO8XX fi fi - if [ "$CONFIG_405" = "y" ]; then - dep_tristate 'PPC 405 I2C Algorithm' CONFIG_I2C_PPC405_ALGO $CONFIG_I2C - if [ "$CONFIG_I2C_PPC405_ALGO" != "n" ]; then - dep_tristate ' PPC 405 I2C Adapter' CONFIG_I2C_PPC405_ADAP $CONFIG_I2C_PPC405_ALGO - fi + if [ "$CONFIG_IBM_OCP" = "y" ]; then + dep_tristate 'IBM IIC I2C interface' CONFIG_I2C_IBM_IIC $CONFIG_I2C + fi + + if [ "$CONFIG_XILINX_OCP" = "y" ]; then + dep_tristate 'Xilinx on-chip I2C' CONFIG_I2C_XILINX $CONFIG_I2C fi if [ "$CONFIG_ALL_PPC" = "y" ] ; then diff -urN -X bkexcl linux-2.4.22/drivers/i2c/Makefile linuxppc-2.4/drivers/i2c/Makefile --- linux-2.4.22/drivers/i2c/Makefile 2003-08-24 09:04:15.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/Makefile 2003-08-26 11:57:12.000000000 +1000 @@ -5,7 +5,9 @@ O_TARGET := i2c.o export-objs := i2c-core.o i2c-algo-bit.o i2c-algo-pcf.o \ - i2c-algo-ite.o i2c-proc.o i2c-algo-sibyte.o + i2c-algo-ite.o i2c-ibm_iic.o i2c-proc.o i2c-algo-sibyte.o + +mod-subdirs := xilinx_iic obj-$(CONFIG_I2C) += i2c-core.o obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o @@ -23,6 +25,9 @@ obj-$(CONFIG_I2C_KEYWEST) += i2c-keywest.o obj-$(CONFIG_I2C_ALGO_SIBYTE) += i2c-algo-sibyte.o i2c-sibyte.o obj-$(CONFIG_I2C_MAX1617) += i2c-max1617.o +obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o +subdir-$(CONFIG_I2C_XILINX) += xilinx_iic +obj-$(CONFIG_I2C_XILINX) += xilinx_iic/xilinx_iic.o # This is needed for automatic patch generation: sensors code starts here # This is needed for automatic patch generation: sensors code ends here diff -urN -X bkexcl linux-2.4.22/drivers/i2c/i2c-ibm_iic.c linuxppc-2.4/drivers/i2c/i2c-ibm_iic.c --- linux-2.4.22/drivers/i2c/i2c-ibm_iic.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/i2c-ibm_iic.c 2003-07-25 02:00:01.000000000 +1000 @@ -0,0 +1,733 @@ +/* + * drivers/i2c/i2c-ibm_iic.c + * + * Support for the IIC peripheral on IBM PPC 4xx + * + * Copyright (c) 2003 Zultys Technologies. + * Eugene Surovegin or + * + * Based on original work by + * Ian DaSilva + * Armin Kuster + * Matt Porter + * + * Copyright 2000-2003 MontaVista Software Inc. + * + * Original driver version was highly leveraged from i2c-elektor.c + * + * Copyright 1995-97 Simon G. Vogl + * 1998-99 Hans Berglund + * + * With some changes from Kyösti Mälkki + * and even Frodo Looijaard + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-ibm_iic.h" + +#define DRIVER_VERSION "2.0" + +MODULE_DESCRIPTION("IBM IIC driver v" DRIVER_VERSION); +MODULE_LICENSE("GPL"); + +static int iic_scan = 0; +MODULE_PARM(iic_scan, "i"); +MODULE_PARM_DESC(iic_scan, "Scan for active chips on the bus"); + +static int iic_force_poll = 0; +MODULE_PARM(iic_force_poll, "i"); +MODULE_PARM_DESC(iic_force_poll, "Force polling mode"); + +static int iic_force_fast = 0; +MODULE_PARM(iic_force_fast, "i"); +MODULE_PARM_DESC(iic_fast_poll, "Force fast mode (400 kHz)"); + +#define DBG_LEVEL 0 + +#if DBG_LEVEL > 0 +# define DBG(x...) printk(KERN_DEBUG "ibm-iic" ##x) +#else +# define DBG(x...) ((void)0) +#endif +#if DBG_LEVEL > 1 +# define DBG2(x...) DBG( ##x ) +#else +# define DBG2(x...) ((void)0) +#endif +#if DBG_LEVEL > 2 +static void dump_iic_regs(const char* header, struct ibm_iic_private* dev) +{ + volatile struct iic_regs *iic = dev->vaddr; + printk(KERN_DEBUG "ibm-iic%d: %s\n", dev->idx, header); + printk(KERN_DEBUG " cntl = 0x%02x, mdcntl = 0x%02x\n" + KERN_DEBUG " sts = 0x%02x, extsts = 0x%02x\n" + KERN_DEBUG " clkdiv = 0x%02x, xfrcnt = 0x%02x\n" + KERN_DEBUG " xtcntlss = 0x%02x, directcntl = 0x%02x\n", + in_8(&iic->cntl), in_8(&iic->mdcntl), in_8(&iic->sts), + in_8(&iic->extsts), in_8(&iic->clkdiv), in_8(&iic->xfrcnt), + in_8(&iic->xtcntlss), in_8(&iic->directcntl)); +} +# define DUMP_REGS(h,dev) dump_iic_regs((h),(dev)) +#else +# define DUMP_REGS(h,dev) ((void)0) +#endif + +/* Enable/disable interrupt generation */ +static inline void iic_interrupt_mode(struct ibm_iic_private* dev, int enable) +{ + out_8(&dev->vaddr->intmsk, enable ? INTRMSK_EIMTC : 0); +} + +/* + * Initialize IIC interface. + */ +static void iic_dev_init(struct ibm_iic_private* dev) +{ + volatile struct iic_regs *iic = dev->vaddr; + + DBG("%d: init\n", dev->idx); + + /* Clear master address */ + out_8(&iic->lmadr, 0); + out_8(&iic->hmadr, 0); + + /* Clear slave address */ + out_8(&iic->lsadr, 0); + out_8(&iic->hsadr, 0); + + /* Clear status & extended status */ + out_8(&iic->sts, STS_SCMP | STS_IRQA); + out_8(&iic->extsts, EXTSTS_IRQP | EXTSTS_IRQD | EXTSTS_LA + | EXTSTS_ICT | EXTSTS_XFRA); + + /* Set clock divider */ + out_8(&iic->clkdiv, dev->clckdiv); + + /* Clear transfer count */ + out_8(&iic->xfrcnt, 0); + + /* Clear extended control and status */ + out_8(&iic->xtcntlss, XTCNTLSS_SRC | XTCNTLSS_SRS | XTCNTLSS_SWC + | XTCNTLSS_SWS); + + /* Clear control register */ + out_8(&iic->cntl, 0); + + /* Enable interrupts if possible */ + iic_interrupt_mode(dev, dev->irq >= 0); + + /* Set mode control */ + out_8(&iic->mdcntl, MDCNTL_FMDB | MDCNTL_EINT | MDCNTL_EUBS + | (dev->fast_mode ? MDCNTL_FSM : 0)); + + DUMP_REGS("iic_init", dev); +} + +/* + * Reset IIC interface + */ +static void iic_dev_reset(struct ibm_iic_private* dev) +{ + volatile struct iic_regs *iic = dev->vaddr; + int i; + u8 dc; + + DBG("%d: soft reset\n", dev->idx); + DUMP_REGS("reset", dev); + + /* Place chip in the reset state */ + out_8(&iic->xtcntlss, XTCNTLSS_SRST); + + /* Check if bus is free */ + dc = in_8(&iic->directcntl); + if (!DIRCTNL_FREE(dc)){ + DBG("%d: trying to regain bus control\n", dev->idx); + + /* Try to set bus free state */ + out_8(&iic->directcntl, DIRCNTL_SDAC | DIRCNTL_SCC); + + /* Wait until we regain bus control */ + for (i = 0; i < 100; ++i){ + dc = in_8(&iic->directcntl); + if (DIRCTNL_FREE(dc)) + break; + + /* Toggle SCL line */ + dc ^= DIRCNTL_SCC; + out_8(&iic->directcntl, dc); + udelay(10); + dc ^= DIRCNTL_SCC; + out_8(&iic->directcntl, dc); + + /* be nice */ + if (current->need_resched) + schedule(); + } + } + + /* Remove reset */ + out_8(&iic->xtcntlss, 0); + + /* Reinitialize interface */ + iic_dev_init(dev); +} + +/* + * IIC interrupt handler + */ +static void iic_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + struct ibm_iic_private* dev = (struct ibm_iic_private*)dev_id; + volatile struct iic_regs* iic = dev->vaddr; + + DBG2("%d: irq handler, STS = 0x%02x, EXTSTS = 0x%02x\n", + dev->idx, in_8(&iic->sts), in_8(&iic->extsts)); + + /* Acknowledge IRQ and wakeup iic_wait_for_tc */ + out_8(&iic->sts, STS_IRQA | STS_SCMP); + wake_up_interruptible(&dev->wq); +} + +/* + * Get master transfer result and clear errors if any. + * Returns the number of actually transferred bytes or error (<0) + */ +static int iic_xfer_result(struct ibm_iic_private* dev) +{ + volatile struct iic_regs *iic = dev->vaddr; + + if (unlikely(in_8(&iic->sts) & STS_ERR)){ + DBG("%d: xfer error, EXTSTS = 0x%02x\n", dev->idx, + in_8(&iic->extsts)); + + /* Clear errors and possible pending IRQs */ + out_8(&iic->extsts, EXTSTS_IRQP | EXTSTS_IRQD | + EXTSTS_LA | EXTSTS_ICT | EXTSTS_XFRA); + + /* Flush master data buffer */ + out_8(&iic->mdcntl, in_8(&iic->mdcntl) | MDCNTL_FMDB); + + /* Is bus free? + * If error happened during combined xfer + * IIC interface is usually stuck in some strange + * state, the only way out - soft reset. + */ + if ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){ + DBG("%d: bus is stuck, resetting\n", dev->idx); + iic_dev_reset(dev); + } + return -EREMOTEIO; + } + else + return in_8(&iic->xfrcnt) & XFRCNT_MTC_MASK; +} + +/* + * Try to abort active transfer. + */ +static void iic_abort_xfer(struct ibm_iic_private* dev) +{ + volatile struct iic_regs *iic = dev->vaddr; + unsigned long x; + + DBG("%d: iic_abort_xfer\n", dev->idx); + + out_8(&iic->cntl, CNTL_HMT); + + /* + * Wait for the abort command to complete. + * It's not worth to be optimized, just poll (timeout >= 1 tick) + */ + x = jiffies + 2; + while ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){ + if (time_after(jiffies, x)){ + DBG("%d: abort timeout, resetting...\n", dev->idx); + iic_dev_reset(dev); + return; + } + schedule(); + } + + /* Just to clear errors */ + iic_xfer_result(dev); +} + +/* + * Wait for master transfer to complete. + * It puts current process to sleep until we get interrupt or timeout expires. + * Returns the number of transferred bytes or error (<0) + */ +static int iic_wait_for_tc(struct ibm_iic_private* dev){ + + volatile struct iic_regs *iic = dev->vaddr; + int ret = 0; + + if (dev->irq >= 0){ + /* Interrupt mode */ + wait_queue_t wait; + init_waitqueue_entry(&wait, current); + + add_wait_queue(&dev->wq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + if (in_8(&iic->sts) & STS_PT) + schedule_timeout(dev->adap.timeout * HZ); + set_current_state(TASK_RUNNING); + remove_wait_queue(&dev->wq, &wait); + + if (unlikely(signal_pending(current))){ + DBG("%d: wait interrupted\n", dev->idx); + ret = -ERESTARTSYS; + } else if (unlikely(in_8(&iic->sts) & STS_PT)){ + DBG("%d: wait timeout\n", dev->idx); + ret = -ETIMEDOUT; + } + } + else { + /* Polling mode */ + unsigned long x = jiffies + dev->adap.timeout * HZ; + + while (in_8(&iic->sts) & STS_PT){ + if (unlikely(time_after(jiffies, x))){ + DBG("%d: poll timeout\n", dev->idx); + ret = -ETIMEDOUT; + break; + } + + if (unlikely(signal_pending(current))){ + DBG("%d: poll interrupted\n", dev->idx); + ret = -ERESTARTSYS; + break; + } + schedule(); + } + } + + if (unlikely(ret < 0)) + iic_abort_xfer(dev); + else + ret = iic_xfer_result(dev); + + DBG2("%d: iic_wait_for_tc -> %d\n", dev->idx, ret); + + return ret; +} + +/* + * Low level master transfer routine + */ +static int iic_xfer_bytes(struct ibm_iic_private* dev, struct i2c_msg* pm, + int combined_xfer) +{ + volatile struct iic_regs *iic = dev->vaddr; + char* buf = pm->buf; + int i, j, loops, ret = 0; + int len = pm->len; + + u8 cntl = (in_8(&iic->cntl) & CNTL_AMD) | CNTL_PT; + if (pm->flags & I2C_M_RD) + cntl |= CNTL_RW; + + loops = (len + 3) / 4; + for (i = 0; i < loops; ++i, len -= 4){ + int count = len > 4 ? 4 : len; + u8 cmd = cntl | ((count - 1) << CNTL_TCT_SHIFT); + + if (!(cntl & CNTL_RW)) + for (j = 0; j < count; ++j) + out_8((volatile u8*)&iic->mdbuf, *buf++); + + if (i < loops - 1) + cmd |= CNTL_CHT; + else if (combined_xfer) + cmd |= CNTL_RPST; + + DBG2("%d: xfer_bytes, %d, CNTL = 0x%02x\n", dev->idx, count, cmd); + + /* Start transfer */ + out_8(&iic->cntl, cmd); + + /* Wait for completion */ + ret = iic_wait_for_tc(dev); + + if (unlikely(ret < 0)) + break; + else if (unlikely(ret != count)){ + DBG("%d: xfer_bytes, requested %d, transfered %d\n", + dev->idx, count, ret); + + /* If it's not a last part of xfer, abort it */ + if (combined_xfer || (i < loops - 1)) + iic_abort_xfer(dev); + + ret = -EREMOTEIO; + break; + } + + if (cntl & CNTL_RW) + for (j = 0; j < count; ++j) + *buf++ = in_8((volatile u8*)&iic->mdbuf); + } + + return ret > 0 ? 0 : ret; +} + +/* + * Set target slave address for master transfer + */ +static inline void iic_address(struct ibm_iic_private* dev, struct i2c_msg* msg) +{ + volatile struct iic_regs *iic = dev->vaddr; + u16 addr = msg->addr; + + DBG2("%d: iic_address, 0x%03x (%d-bit)\n", dev->idx, + addr, msg->flags & I2C_M_TEN ? 10 : 7); + + if (msg->flags & I2C_M_TEN){ + out_8(&iic->cntl, CNTL_AMD); + out_8(&iic->lmadr, addr); + out_8(&iic->hmadr, 0xf0 | ((addr >> 7) & 0x06)); + } + else { + out_8(&iic->cntl, 0); + out_8(&iic->lmadr, addr << 1); + } +} + +static inline int iic_invalid_address(const struct i2c_msg* p) +{ + return (p->addr > 0x3ff) || (!(p->flags & I2C_M_TEN) && (p->addr > 0x7f)); +} + +static inline int iic_address_neq(const struct i2c_msg* p1, + const struct i2c_msg* p2) +{ + return (p1->addr != p2->addr) + || ((p1->flags & I2C_M_TEN) != (p2->flags & I2C_M_TEN)); +} + +/* + * Generic master transfer entrypoint. + * Returns the number of processed messages or error (<0) + */ +static int iic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct ibm_iic_private* dev = (struct ibm_iic_private*)(adap->data); + volatile struct iic_regs *iic = dev->vaddr; + int i, ret = 0; + + DBG2("%d: iic_xfer, %d msg(s)\n", dev->idx, num); + + if (!num) + return 0; + + /* Check the sanity of the passed messages. + * Uhh, generic i2c layer is more suitable place for such code... + */ + if (unlikely(iic_invalid_address(&msgs[0]))){ + DBG("%d: invalid address 0x%03x (%d-bit)\n", dev->idx, + msgs[0].addr, msgs[0].flags & I2C_M_TEN ? 10 : 7); + return -EINVAL; + } + for (i = 0; i < num; ++i){ + if (unlikely(msgs[i].len <= 0)){ + DBG("%d: invalid len %d in msg[%d]\n", dev->idx, + msgs[i].len, i); + return -EINVAL; + } + if (unlikely(iic_address_neq(&msgs[0], &msgs[i]))){ + DBG("%d: invalid addr in msg[%d]\n", dev->idx, i); + return -EINVAL; + } + } + + /* Check bus state */ + if (unlikely((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE)){ + DBG("%d: iic_xfer, bus is not free\n", dev->idx); + + /* Usually it means something serious has happend. + * We *cannot* have unfinished previous transfer + * so it doesn't make any sense to try to stop it. + * Probably we were not able to recover from the + * previous error. + * The only *reasonable* thing I can think of here + * is soft reset. --ebs + */ + iic_dev_reset(dev); + + if ((in_8(&iic->extsts) & EXTSTS_BCS_MASK) != EXTSTS_BCS_FREE){ + DBG("%d: iic_xfer, bus is still not free\n", dev->idx); + return -EREMOTEIO; + } + } + else { + /* Flush master data buffer (just in case) */ + out_8(&iic->mdcntl, in_8(&iic->mdcntl) | MDCNTL_FMDB); + } + + /* Load slave address */ + iic_address(dev, &msgs[0]); + + /* Do real transfer */ + for (i = 0; i < num && !ret; ++i) + ret = iic_xfer_bytes(dev, &msgs[i], i < num - 1); + + return ret < 0 ? ret : num; +} + +static u32 iic_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; +} + +static struct i2c_algorithm iic_algo = { + .name = "IBM IIC algorithm", + .id = I2C_ALGO_OCP, + .master_xfer = iic_xfer, + .smbus_xfer = NULL, + .slave_send = NULL, + .slave_recv = NULL, + .algo_control = NULL, + .functionality = iic_func +}; + +/* + * Scan bus for valid 7-bit addresses (ie things that ACK on 1 byte read) + * We only scan range [0x08 - 0x77], all other addresses are reserved anyway + */ +static void __init iic_scan_bus(struct ibm_iic_private* dev) +{ + int found = 0; + char dummy; + struct i2c_msg msg = { + .buf = &dummy, + .len = sizeof(dummy), + .flags = I2C_M_RD + }; + + printk(KERN_INFO "ibm-iic%d: scanning bus...\n" KERN_INFO, dev->idx); + + for (msg.addr = 8; msg.addr < 0x78; ++msg.addr) + if (iic_xfer(&dev->adap, &msg, 1) == 1){ + ++found; + printk(" 0x%02x", msg.addr); + } + + printk("%sibm-iic%d: %d device(s) detected\n", + found ? "\n" KERN_INFO : "", dev->idx, found); +} + +/* + * Module reference counting + */ +static void iic_inc_use(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} + +static void iic_dec_use(struct i2c_adapter *adap) +{ + MOD_DEC_USE_COUNT; +} + +/* + * Calculates IICx_CLCKDIV value for a specific OPB clock frequency + */ +static inline u8 iic_clckdiv(unsigned int opb) +{ + /* Compatibility kludge, should go away after all cards + * are fixed to fill correct value for opbfreq. + * Previous driver version used hardcoded divider value 4, + * it corresponds to OPB frequency from the range (40, 50] MHz + */ + if (!opb){ + printk(KERN_WARNING "ibm-iic: using compatibility value for OPB freq," + " fix your board specific setup\n"); + opb = 50000000; + } + + /* Convert to MHz */ + opb /= 1000000; + + if (opb < 20 || opb > 150){ + printk(KERN_CRIT "ibm-iic: invalid OPB clock frequency %u MHz\n", + opb); + opb = opb < 20 ? 20 : 150; + } + return (u8)((opb + 9) / 10 - 1); +} + +/* + * Register single IIC interface + */ +static int iic_probe(struct ocp_device *ocp){ + + struct ibm_iic_private* dev; + struct i2c_adapter* adap; + int ret; + bd_t* bd = (bd_t*)__res; + + if (!(dev = kmalloc(sizeof(*dev), GFP_KERNEL))){ + printk(KERN_CRIT "ibm-iic: failed to allocate device data\n"); + return -ENOMEM; + } + + memset(dev, 0, sizeof(*dev)); + dev->idx = ocp->def->index; + ocp_set_drvdata(ocp, dev); + + if (!(dev->vaddr = ioremap(ocp->def->paddr, sizeof(struct iic_regs)))){ + printk(KERN_CRIT "ibm-iic%d: failed to ioremap device registers\n", + dev->idx); + ret = -ENXIO; + goto fail2; + } + + init_waitqueue_head(&dev->wq); + + dev->irq = iic_force_poll ? -1 : ocp->def->irq; + if (dev->irq >= 0){ + /* Disable interrupts until we finish intialization, + assumes level-sensitive IRQ setup... + */ + iic_interrupt_mode(dev, 0); + if (request_irq(dev->irq, iic_handler, 0, "IBM IIC", dev)){ + printk(KERN_ERR "ibm-iic%d: request_irq %d failed\n", + dev->idx, dev->irq); + /* Fallback to the polling mode */ + dev->irq = -1; + } + } + + if (dev->irq < 0) + printk(KERN_WARNING "ibm-iic%d: using polling mode\n", + dev->idx); + + /* Board specific settings */ + BUG_ON(dev->idx >= sizeof(bd->bi_iic_fast) / sizeof(bd->bi_iic_fast[0])); + dev->fast_mode = iic_force_fast ? 1 : bd->bi_iic_fast[dev->idx]; + + /* clckdiv is the same for *all* IIC interfaces, + * but I'd rather make a copy than introduce another global. --ebs + */ + dev->clckdiv = iic_clckdiv(bd->bi_opb_busfreq); + DBG("%d: clckdiv = %d\n", dev->idx, dev->clckdiv); + + /* Initialize IIC interface */ + iic_dev_init(dev); + + /* Register it with i2c layer */ + adap = &dev->adap; + strcpy(adap->name, "IBM IIC"); + adap->data = dev; + adap->id = I2C_HW_OCP | iic_algo.id; + adap->algo = &iic_algo; + adap->inc_use = iic_inc_use; + adap->dec_use = iic_dec_use; + adap->client_register = NULL; + adap->client_unregister = NULL; + adap->timeout = 1; + adap->retries = 1; + + if ((ret = i2c_add_adapter(adap)) != 0){ + printk(KERN_CRIT "ibm-iic%d: failed to register i2c adapter\n", + dev->idx); + goto fail; + } + + printk(KERN_INFO "ibm-iic%d: using %s mode\n", dev->idx, + dev->fast_mode ? "fast (400 kHz)" : "standard (100 kHz)"); + + /* Scan bus if requested by user */ + if (iic_scan) + iic_scan_bus(dev); + + return 0; + +fail: + if (dev->irq >= 0){ + iic_interrupt_mode(dev, 0); + free_irq(dev->irq, dev); + } + + iounmap((void*)dev->vaddr); +fail2: + ocp_set_drvdata(ocp, 0); + kfree(dev); + return ret; +} + +/* + * Cleanup initialized IIC interface + */ +static void iic_remove(struct ocp_device *ocp) +{ + struct ibm_iic_private* dev = (struct ibm_iic_private*)ocp_get_drvdata(ocp); + BUG_ON(dev == NULL); + if (i2c_del_adapter(&dev->adap)){ + printk(KERN_CRIT "ibm-iic%d: failed to delete i2c adapter :(\n", + dev->idx); + /* That's *very* bad, just shutdown IRQ ... */ + if (dev->irq >= 0){ + iic_interrupt_mode(dev, 0); + free_irq(dev->irq, dev); + dev->irq = -1; + } + } else { + if (dev->irq >= 0){ + iic_interrupt_mode(dev, 0); + free_irq(dev->irq, dev); + } + iounmap((void*)dev->vaddr); + kfree(dev); + } +} + +static struct ocp_device_id ibm_iic_ids[] = +{ + { .vendor = OCP_VENDOR_IBM, .function = OCP_FUNC_IIC }, + { .vendor = OCP_VENDOR_INVALID } +}; + +static struct ocp_driver ibm_iic_driver = +{ + .name = "iic", + .id_table = ibm_iic_ids, + + .probe = iic_probe, + .remove = iic_remove, +}; + +static int __init iic_init(void) +{ + printk(KERN_INFO "IBM IIC driver v" DRIVER_VERSION "\n"); + if (ocp_register_driver(&ibm_iic_driver) == 0){ + /* No devices were bound to this driver */ + ocp_unregister_driver(&ibm_iic_driver); + return -ENODEV; + } + return 0; +} + +static void __exit iic_exit(void) +{ + ocp_unregister_driver(&ibm_iic_driver); +} + +module_init(iic_init); +module_exit(iic_exit); diff -urN -X bkexcl linux-2.4.22/drivers/i2c/i2c-ibm_iic.h linuxppc-2.4/drivers/i2c/i2c-ibm_iic.h --- linux-2.4.22/drivers/i2c/i2c-ibm_iic.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/i2c-ibm_iic.h 2003-05-26 09:40:53.000000000 +1000 @@ -0,0 +1,99 @@ +/* + * drivers/i2c/i2c-ibm_iic.h + * + * Support for the IIC peripheral on IBM PPC 4xx + * + * Copyright (c) 2003 Zultys Technologies. + * Eugene Surovegin or + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#ifndef __I2C_IBM_IIC_H_ +#define __I2C_IBM_IIC_H_ + +#include +#include + +struct ibm_iic_private { + struct i2c_adapter adap; + volatile struct iic_regs *vaddr; + wait_queue_head_t wq; + int idx; + int irq; + int fast_mode; + u8 clckdiv; +}; + +/* IICx_CNTL register */ +#define CNTL_HMT 0x80 +#define CNTL_AMD 0x40 +#define CNTL_TCT_MASK 0x30 +#define CNTL_TCT_SHIFT 4 +#define CNTL_RPST 0x08 +#define CNTL_CHT 0x04 +#define CNTL_RW 0x02 +#define CNTL_PT 0x01 + +/* IICx_MDCNTL register */ +#define MDCNTL_FSDB 0x80 +#define MDCNTL_FMDB 0x40 +#define MDCNTL_EGC 0x20 +#define MDCNTL_FSM 0x10 +#define MDCNTL_ESM 0x08 +#define MDCNTL_EINT 0x04 +#define MDCNTL_EUBS 0x02 +#define MDCNTL_HSCL 0x01 + +/* IICx_STS register */ +#define STS_SSS 0x80 +#define STS_SLPR 0x40 +#define STS_MDBS 0x20 +#define STS_MDBF 0x10 +#define STS_SCMP 0x08 +#define STS_ERR 0x04 +#define STS_IRQA 0x02 +#define STS_PT 0x01 + +/* IICx_EXTSTS register */ +#define EXTSTS_IRQP 0x80 +#define EXTSTS_BCS_MASK 0x70 +#define EXTSTS_BCS_FREE 0x40 +#define EXTSTS_IRQD 0x08 +#define EXTSTS_LA 0x04 +#define EXTSTS_ICT 0x02 +#define EXTSTS_XFRA 0x01 + +/* IICx_INTRMSK register */ +#define INTRMSK_EIRC 0x80 +#define INTRMSK_EIRS 0x40 +#define INTRMSK_EIWC 0x20 +#define INTRMSK_EIWS 0x10 +#define INTRMSK_EIHE 0x08 +#define INTRMSK_EIIC 0x04 +#define INTRMSK_EITA 0x02 +#define INTRMSK_EIMTC 0x01 + +/* IICx_XFRCNT register */ +#define XFRCNT_MTC_MASK 0x07 + +/* IICx_XTCNTLSS register */ +#define XTCNTLSS_SRC 0x80 +#define XTCNTLSS_SRS 0x40 +#define XTCNTLSS_SWC 0x20 +#define XTCNTLSS_SWS 0x10 +#define XTCNTLSS_SRST 0x01 + +/* IICx_DIRECTCNTL register */ +#define DIRCNTL_SDAC 0x08 +#define DIRCNTL_SCC 0x04 +#define DIRCNTL_MSDA 0x02 +#define DIRCNTL_MSC 0x01 + +/* Check if we really control the I2C bus and bus is free */ +#define DIRCTNL_FREE(v) (((v) & 0x0f) == 0x0f) + +#endif /* __I2C_IBM_IIC_H_ */ diff -urN -X bkexcl linux-2.4.22/drivers/i2c/xilinx_iic/Makefile linuxppc-2.4/drivers/i2c/xilinx_iic/Makefile --- linux-2.4.22/drivers/i2c/xilinx_iic/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/xilinx_iic/Makefile 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,21 @@ +# +# Makefile for the Xilinx I2C driver +# + +EXTRA_CFLAGS += -I$(TOPDIR)/arch/ppc/platforms/xilinx_ocp + +list-multi := xilinx_iic.o + +# The Linux adapter for the Xilinx driver code. +xilinx_iic-objs += i2c-algo-xilinx.o + +# The Xilinx OS independent code. +xilinx_iic-objs += xiic.o xiic_g.o xiic_intr.o \ + xiic_master.o xiic_options.o + +obj-$(CONFIG_I2C_XILINX) := xilinx_iic.o + +xilinx_iic.o: $(xilinx_iic-objs) + $(LD) -r -o $@ $(xilinx_iic-objs) + +include $(TOPDIR)/Rules.make diff -urN -X bkexcl linux-2.4.22/drivers/i2c/xilinx_iic/i2c-algo-xilinx.c linuxppc-2.4/drivers/i2c/xilinx_iic/i2c-algo-xilinx.c --- linux-2.4.22/drivers/i2c/xilinx_iic/i2c-algo-xilinx.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/xilinx_iic/i2c-algo-xilinx.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,536 @@ +/* + * i2c-algo-xilinx.c + * + * Xilinx IIC Adapter component to interface IIC component to Linux + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +/* + * I2C drivers are split into two pieces: the adapter and the algorithm. + * The adapter is responsible for actually manipulating the hardware and + * the algorithm is the layer above that that handles the higher level + * tasks such as transmitting or receiving a buffer. The best example + * (in my opinion) of this is the bit banging algorithm has a number of + * different adapters that can plug in under it to actually wiggle the + * SDA and SCL. + * + * The interesting part is that the drivers Xilinx provides with their + * IP are also split into two pieces where one part is the OS + * independent code and the other part is the OS dependent code. All of + * the other sources in this directory are the OS independent files as + * provided by Xilinx with no changes made to them. + * + * As it turns out, this maps quite well into the I2C driver philosophy. + * This file is the I2C algorithm that communicates with the Xilinx OS + * independent function that will serve as our I2C adapter. The + * unfortunate part is that the term "adapter" is overloaded in our + * context. Xilinx refers to the OS dependent part of a driver as an + * adapter. So from an I2C driver perspective, this file is not an + * adapter; that role is filled by the Xilinx OS independent files. + * From a Xilinx perspective, this file is an adapter; it adapts their + * OS independent code to Linux. + * + * Another thing to consider is that the Xilinx OS dependent code knows + * nothing about Linux I2C adapters, so even though this file is billed + * as the I2C algorithm, it takes care of the i2c_adapter structure. + * + * Fortunately, naming conventions will give you a clue as to what comes + * from where. Functions beginning with XIic_ are provided by the + * Xilinx OS independent files. Functions beginning with i2c_ are + * provided by the I2C Linux core. All functions in this file that are + * called by Linux have names that begin with xiic_. The functions in + * this file that have Handler in their name are registered as callbacks + * with the underlying Xilinx OS independent layer. Any other functions + * are static helper functions. + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include "xiic.h" +#include "xiic_i.h" + +MODULE_AUTHOR("MontaVista Software, Inc. "); +MODULE_DESCRIPTION("Xilinx IIC driver"); +MODULE_LICENSE("GPL"); +MODULE_PARM(scan, "i"); +MODULE_PARM_DESC(scan, "Scan for active chips on the bus"); +static int scan = 0; /* have a look at what's hanging 'round */ + +/* SAATODO: actually use these? */ +#define XIIC_TIMEOUT 100 +#define XIIC_RETRY 3 + +/* Our private per device data. */ +struct xiic_dev { + struct i2c_adapter adap; /* The Linux I2C core data */ + struct xiic_dev *next_dev; /* The next device in dev_list */ + struct completion complete; /* for waiting for interrupts */ + int index; /* Which interface is this */ + u32 save_BaseAddress; /* Saved physical base address */ + unsigned int irq; /* device IRQ number */ + /* + * The underlying OS independent code needs space as well. A + * pointer to the following XIic structure will be passed to + * any XIic_ function that requires it. However, we treat the + * data as an opaque object in this file (meaning that we never + * reference any of the fields inside of the structure). + */ + XIic Iic; + XStatus interrupt_status; + /* + * The following bit fields are used to keep track of what + * all has been done to initialize the xiic_dev to make + * error handling out of probe() easier. + */ + unsigned int reqirq:1; /* Has request_irq() been called? */ + unsigned int remapped:1; /* Has ioremap() been called? */ + unsigned int started:1; /* Has XIic_Start() been called? */ + unsigned int added:1; /* Has i2c_add_adapter() been called? */ +}; + +/* List of devices we're handling and a lock to give us atomic access. */ +static struct xiic_dev *dev_list = NULL; +static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED; + +/* SAATODO: This function will be moved into the Xilinx code. */ +/*****************************************************************************/ +/** +* +* Lookup the device configuration based on the iic instance. The table +* XIic_ConfigTable contains the configuration info for each device in the system. +* +* @param Instance is the index of the interface being looked up. +* +* @return +* +* A pointer to the configuration table entry corresponding to the given +* device ID, or NULL if no match is found. +* +* @note +* +* None. +* +******************************************************************************/ +XIic_Config *XIic_GetConfig(int Instance) +{ + if (Instance < 0 || Instance >= XPAR_XIIC_NUM_INSTANCES) + { + return NULL; + } + + return &XIic_ConfigTable[Instance]; +} + +static int +xiic_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + struct xiic_dev *dev = (struct xiic_dev *) i2c_adap; + struct i2c_msg *pmsg; + u32 options; + int i, retries; + XStatus Status; + + for (i = 0; i < num; i++) { + pmsg = &msgs[i]; + + if (!pmsg->len) /* If length is zero */ + continue; /* on to the next request. */ + + options = 0; + if (pmsg->flags & I2C_M_TEN) + options |= XII_SEND_10_BIT_OPTION; + if (i != num-1) + options |= XII_REPEATED_START_OPTION; + XIic_SetOptions(&dev->Iic, options); + + if (XIic_SetAddress(&dev->Iic, XII_ADDR_TO_SEND_TYPE, + pmsg->addr) != XST_SUCCESS) { + printk(KERN_WARNING + "%s #%d: Could not set address to 0x%2x.\n", + dev->adap.name, dev->index, pmsg->addr); + return -EIO; + } + dev->interrupt_status = ~(XStatus) 0; + /* + * The Xilinx layer does not handle bus busy conditions yet + * so this code retries a request up to 16 times if it + * receives a bus busy condition. If and when the underlying + * code is enhanced, the retry code can be removed. + */ + retries = 16; + if (pmsg->flags & I2C_M_RD) { + while ((Status = XIic_MasterRecv(&dev->Iic, + pmsg->buf, pmsg->len)) + == XST_IIC_BUS_BUSY && retries--) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/10); + } + } else { + while ((Status = XIic_MasterSend(&dev->Iic, + pmsg->buf, pmsg->len)) + == XST_IIC_BUS_BUSY && retries--) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/10); + } + } + if (Status != XST_SUCCESS) { + printk(KERN_WARNING + "%s #%d: Unexpected error %d.\n", + dev->adap.name, dev->index, Status); + return -EIO; + } + wait_for_completion(&dev->complete); + if (dev->interrupt_status != XST_SUCCESS) { + printk(KERN_WARNING + "%s #%d: Could not talk to device 0x%2x (%d).\n", + dev->adap.name, dev->index, pmsg->addr, + dev->interrupt_status); + return -EIO; + } + } + return num; +} + +static int +xiic_algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static u32 +xiic_bit_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_PROTOCOL_MANGLING; +} + +static struct i2c_algorithm xiic_algo = { + "Xilinx IIC algorithm", /* name */ + /* + * SAATODO: Get a real ID (perhaps I2C_ALGO_XILINX) after + * initial release. Will need to email lm78@stimpy.netroedge.com + * per http://www2.lm-sensors.nu/~lm78/support.html + */ + I2C_ALGO_EXP, /* id */ + xiic_xfer, /* master_xfer */ + NULL, /* smbus_xfer */ + NULL, /* slave_send */ + NULL, /* slave_recv */ + xiic_algo_control, /* algo_control */ + xiic_bit_func, /* functionality */ +}; + +/* + * This routine is registered with the OS as the function to call when + * the IIC interrupts. It in turn, calls the Xilinx OS independent + * interrupt function. The Xilinx OS independent interrupt function + * will in turn call any callbacks that we have registered for various + * conditions. + */ +static void +xiic_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct xiic_dev *dev = dev_id; + XIic_InterruptHandler(&dev->Iic); +} + +static void +RecvHandler(void *CallbackRef, int ByteCount) +{ + struct xiic_dev *dev = (struct xiic_dev *) CallbackRef; + + if (ByteCount == 0) { + dev->interrupt_status = XST_SUCCESS; + complete(&dev->complete); + } +} +static void +SendHandler(void *CallbackRef, int ByteCount) +{ + struct xiic_dev *dev = (struct xiic_dev *) CallbackRef; + + if (ByteCount == 0) { + dev->interrupt_status = XST_SUCCESS; + complete(&dev->complete); + } +} +static void +StatusHandler(void *CallbackRef, XStatus Status) +{ + struct xiic_dev *dev = (struct xiic_dev *) CallbackRef; + + dev->interrupt_status = Status; + complete(&dev->complete); +} + +static void +xiic_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} +static void +xiic_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +static void +remove_head_dev(void) +{ + struct xiic_dev *dev; + XIic_Config *cfg; + + /* Pull the head off of dev_list. */ + spin_lock(&dev_lock); + dev = dev_list; + dev_list = dev->next_dev; + spin_unlock(&dev_lock); + + /* + * If we've told the core I2C code about this dev, tell + * the core I2C code to forget the dev. + */ + if (dev->added) { + /* + * If an error is returned, there's not a whole lot we can + * do. An error has already been printed out so we'll + * just keep trundling along. + */ + (void) i2c_del_adapter(&dev->adap); + } + + /* Tell the Xilinx code to take this IIC interface down. */ + if (dev->started) { + while (XIic_Stop(&dev->Iic) != XST_SUCCESS) { + /* The bus was busy. Retry. */ + printk(KERN_WARNING + "%s #%d: Could not stop device. Will retry.\n", + dev->adap.name, dev->index); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 2); + } + } + + /* + * Now that the Xilinx code isn't using the IRQ or registers, + * unmap the registers and free the IRQ. + */ + if (dev->remapped) { + cfg = XIic_GetConfig(dev->index); + iounmap((void *) cfg->BaseAddress); + cfg->BaseAddress = dev->save_BaseAddress; + }; + if (dev->reqirq) { + disable_irq(dev->irq); + free_irq(dev->irq, dev); + } + + kfree(dev); +} + +static int __init +probe(int index) +{ + static const unsigned long remap_size + = XPAR_IIC_0_HIGHADDR - XPAR_IIC_0_BASEADDR + 1; + struct xiic_dev *dev; + XIic_Config *cfg; + unsigned int irq; + int retval; + + switch (index) { +#if defined(XPAR_INTC_0_IIC_0_VEC_ID) + case 0: + irq = 31 - XPAR_INTC_0_IIC_0_VEC_ID; + break; +#if defined(XPAR_INTC_0_IIC_1_VEC_ID) + case 1: + irq = 31 - XPAR_INTC_0_IIC_1_VEC_ID; + break; +#if defined(XPAR_INTC_0_IIC_2_VEC_ID) + case 2: + irq = 31 - XPAR_INTC_0_IIC_2_VEC_ID; + break; +#if defined(XPAR_INTC_0_IIC_3_VEC_ID) +#error Edit this file to add more devices. +#endif /* 3 */ +#endif /* 2 */ +#endif /* 1 */ +#endif /* 0 */ + default: + return -ENODEV; + } + + /* Find the config for our device. */ + cfg = XIic_GetConfig(index); + if (!cfg) + return -ENODEV; + + /* Allocate the dev and zero it out. */ + dev = (struct xiic_dev *) kmalloc(sizeof (struct xiic_dev), GFP_KERNEL); + if (!dev) { + printk(KERN_ERR "%s #%d: Could not allocate device.\n", + "Xilinx IIC adapter", index); + return -ENOMEM; + } + memset(dev, 0, sizeof (struct xiic_dev)); + strcpy(dev->adap.name, "Xilinx IIC adapter"); + dev->index = index; + + /* Make it the head of dev_list. */ + spin_lock(&dev_lock); + dev->next_dev = dev_list; + dev_list = dev; + spin_unlock(&dev_lock); + + init_completion(&dev->complete); + + /* Change the addresses to be virtual; save the old ones to restore. */ + dev->save_BaseAddress = cfg->BaseAddress; + cfg->BaseAddress = (u32) ioremap(dev->save_BaseAddress, remap_size); + dev->remapped = 1; + + /* Tell the Xilinx code to bring this IIC interface up. */ + if (XIic_Initialize(&dev->Iic, cfg->DeviceId) != XST_SUCCESS) { + printk(KERN_ERR "%s #%d: Could not initialize device.\n", + dev->adap.name, dev->index); + remove_head_dev(); + return -ENODEV; + } + XIic_SetRecvHandler(&dev->Iic, (void *) dev, RecvHandler); + XIic_SetSendHandler(&dev->Iic, (void *) dev, SendHandler); + XIic_SetStatusHandler(&dev->Iic, (void *) dev, StatusHandler); + + /* Grab the IRQ */ + dev->irq = irq; + retval = request_irq(dev->irq, xiic_interrupt, 0, dev->adap.name, dev); + if (retval) { + printk(KERN_ERR "%s #%d: Could not allocate interrupt %d.\n", + dev->adap.name, dev->index, dev->irq); + remove_head_dev(); + return retval; + } + dev->reqirq = 1; + + if (XIic_Start(&dev->Iic) != XST_SUCCESS) { + printk(KERN_ERR "%s #%d: Could not start device.\n", + dev->adap.name, dev->index); + remove_head_dev(); + return -ENODEV; + } + dev->started = 1; + + /* Now tell the core I2C code about our new device. */ + /* + * SAATODO: Get a real ID (perhaps I2C_HW_XILINX) after + * initial release. Will need to email lm78@stimpy.netroedge.com + * per http://www2.lm-sensors.nu/~lm78/support.html + */ + dev->adap.id = xiic_algo.id | I2C_DRIVERID_EXP0; + dev->adap.algo = &xiic_algo; + dev->adap.algo_data = NULL; + dev->adap.inc_use = xiic_inc_use; + dev->adap.dec_use = xiic_dec_use; + dev->adap.timeout = XIIC_TIMEOUT; + dev->adap.retries = XIIC_RETRY; + retval = i2c_add_adapter(&dev->adap); + if (retval) { + printk(KERN_ERR "%s #%d: Could not add I2C adapter.\n", + dev->adap.name, dev->index); + remove_head_dev(); + return retval; + } + dev->added = 1; + + printk(KERN_INFO "%s #%d at 0x%08X mapped to 0x%08X, irq=%d\n", + dev->adap.name, dev->index, + dev->save_BaseAddress, cfg->BaseAddress, dev->irq); + + /* scan bus */ + if (scan) { + int i, anyfound; + + printk(KERN_INFO "%s #%d Bus scan:", + dev->adap.name, dev->index); + /* + * The Philips I2C-bus specification defines special addresses: + * 0x00 General Call + * 0x01 CBUS Address + * 0x02 Reserved for different bus format + * 0x03 Reserved for future purposes + * 0x04 to 0x07 Hs-mode master code + * 0x78 to 0x7B 10-bit slave addressing + * 0x7C to 0x7F Reserved for future purposes + * + * We don't bother scanning any of these addresses. + */ + anyfound = 0; + for (i = 0x08; i < 0x78; i++) { + u8 data; + XStatus Status; + + if (XIic_SetAddress(&dev->Iic, XII_ADDR_TO_SEND_TYPE, + i) != XST_SUCCESS) { + printk(KERN_WARNING + "%s #%d: Could not set address to %2x.\n", + dev->adap.name, dev->index, i); + continue; + } + dev->interrupt_status = ~(XStatus) 0; + Status = XIic_MasterRecv(&dev->Iic, &data, 1); + if (Status != XST_SUCCESS) { + printk(KERN_WARNING + "%s #%d: Unexpected error %d.\n", + dev->adap.name, dev->index, Status); + continue; + } + wait_for_completion(&dev->complete); + if (dev->interrupt_status == XST_SUCCESS) { + printk(" 0x%02x", i); + anyfound = 1; + } + } + printk(anyfound ? " responded.\n" : " Nothing responded.\n"); + } + + return 0; +} +static int __init +xiic_init(void) +{ + int index = 0; + + while (probe(index++) == 0) ; + /* If we found at least one, report success. */ + return (index > 1) ? 0 : -ENODEV; +} + +static void __exit +xiic_cleanup(void) +{ + while (dev_list) + remove_head_dev(); +} + +EXPORT_NO_SYMBOLS; + +module_init(xiic_init); +module_exit(xiic_cleanup); diff -urN -X bkexcl linux-2.4.22/drivers/i2c/xilinx_iic/xiic.c linuxppc-2.4/drivers/i2c/xilinx_iic/xiic.c --- linux-2.4.22/drivers/i2c/xilinx_iic/xiic.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/xilinx_iic/xiic.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,703 @@ +/* $Id: xiic.c,v 1.4 2003/05/14 13:43:30 robertm Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xiic.c +* +* Contains required functions for the XIic component. See xiic.h for more +* information on the driver. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- --- ------- -----------------------------------------------
+* 1.01a rfp  10/19/01 release
+* 1.01c ecm  12/05/02 new rev
+* 1.01c rmm  05/14/03 Fixed diab compiler warnings relating to asserts.
+* 
+* +****************************************************************************/ + +/***************************** Include Files *******************************/ + +#include "xiic.h" +#include "xiic_i.h" +#include "xio.h" +#include "xparameters.h" + +/************************** Constant Definitions ***************************/ + +/**************************** Type Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *******************/ + +/************************** Function Prototypes ****************************/ + +static void XIic_StubStatusHandler(void *CallBackRef, XStatus ErrorCode); + +static void XIic_StubHandler(void *CallBackRef, int ByteCount); + +/************************** Variable Definitions **************************/ + +/*****************************************************************************/ +/** +* +* Initializes a specific XIic instance. The initialization entails: +* +* - Check the device has an entry in the configuration table. +* - Initialize the driver to allow access to the device registers and +* initialize other subcomponents necessary for the operation of the device. +* - Default options to: +* - 7-bit slave addressing +* - Send messages as a slave device +* - Repeated start off +* - General call recognition disabled +* - Clear messageing and error statistics +* +* The XIic_Start() function must be called after this function before the device +* is ready to send and receive data on the IIC bus. +* +* Before XIic_Start() is called, the interrupt control must connect the ISR +* routine to the interrupt handler. This is done by the user, and not +* XIic_Start() to allow the user to use an interrupt controller of their choice. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* @param DeviceId is the unique id of the device controlled by this XIic +* instance. Passing in a device id associates the generic XIic +* instance to a specific device, as chosen by the caller or +* application developer. +* +* @return +* +* - XST_SUCCESS when successful +* - XST_DEVICE_IS_STARTED indicates the device is started (i.e. interrupts +* enabled and messaging is possible). Must stop before re-initialization +* is allowed. +* +* @note +* +* None. +* +****************************************************************************/ +XStatus +XIic_Initialize(XIic * InstancePtr, u16 DeviceId) +{ + XIic_Config *IicConfigPtr; /* Pointer to configuration data */ + + /* + * Asserts test the validity of selected input arguments. + */ + XASSERT_NONVOID(InstancePtr != NULL); + + InstancePtr->IsReady = 0; + + /* + * If the device is started, disallow the initialize and return a Status + * indicating it is started. This allows the user to stop the device + * and reinitialize, but prevents a user from inadvertently initializing + */ + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + return XST_DEVICE_IS_STARTED; + } + + /* + * Lookup the device configuration in the temporary CROM table. Use this + * configuration info down below when initializing this component. + */ + IicConfigPtr = XIic_LookupConfig(DeviceId); + if (IicConfigPtr == NULL) { + return XST_DEVICE_NOT_FOUND; + } + /* + * Set default values, including setting the callback + * handlers to stubs so the system will not crash should the + * application not assign its own callbacks. + */ + InstancePtr->IsStarted = 0; + InstancePtr->BaseAddress = IicConfigPtr->BaseAddress; + InstancePtr->RecvHandler = XIic_StubHandler; + InstancePtr->SendHandler = XIic_StubHandler; + InstancePtr->StatusHandler = XIic_StubStatusHandler; + InstancePtr->Has10BitAddr = IicConfigPtr->Has10BitAddr; + InstancePtr->IsReady = XCOMPONENT_IS_READY; + InstancePtr->Options = 0; + InstancePtr->BNBOnly = FALSE; + + /* + * Reset the device so it's in the reset state, this must be after the + * IPIF is initialized since it resets thru the IPIF and clear the stats + */ + XIic_Reset(InstancePtr); + + XIIC_CLEAR_STATS(InstancePtr); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* This function starts the IIC device and driver by enabling the proper +* interrupts such that data may be sent and received on the IIC bus. +* This function must be called before the functions to send and receive data. +* +* Before XIic_Start() is called, the interrupt control must connect the ISR +* routine to the interrupt handler. This is done by the user, and not +* XIic_Start() to allow the user to use an interrupt controller of their choice. +* +* Start enables: +* - IIC device +* - Interrupts: +* - Addressed as slave to allow messages from another master +* - Arbitration Lost to detect Tx arbitration errors +* - Global IIC interrupt within the IPIF interface +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* +* @return +* +* XST_SUCCESS always +* +* @note +* +* The device interrupt is connected to the interrupt controller, but no +* "messaging" interrupts are enabled. Addressed as Slave is enabled to +* reception of messages when this devices address is written to the bus. +* The correct messaging interrupts are enabled when sending or receiving +* via the IicSend() and IicRecv() functions. No action is required +* by the user to control any IIC interrupts as the driver completely +* manages all 8 interrupts. Start and Stop control the ability +* to use the device. Stopping the device completely stops all device +* interrupts from the processor. +* +****************************************************************************/ +XStatus +XIic_Start(XIic * InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Mask off all interrupts, each is enabled when needed. + */ + XIIF_V123B_WRITE_IIER(InstancePtr->BaseAddress, 0); + + /* + * Clear all interrupts by reading and rewriting exact value back. + * Only those bits set will get written as 1 (writing 1 clears intr) + */ + XIic_mClearIntr(InstancePtr->BaseAddress, 0xFFFFFFFF); + + /* + * Enable the device + */ + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, + XIIC_CR_ENABLE_DEVICE_MASK); + /* + * Set Rx FIFO Occupancy depth to throttle at first byte(after reset = 0) + */ + XIo_Out8(InstancePtr->BaseAddress + XIIC_RFD_REG_OFFSET, 0); + + /* + * Clear and enable the interrupts needed + */ + XIic_mClearEnableIntr(InstancePtr->BaseAddress, + XIIC_INTR_AAS_MASK | XIIC_INTR_ARB_LOST_MASK); + + InstancePtr->IsStarted = XCOMPONENT_IS_STARTED; + + /* Enable all interrupts by the global enable in the IPIF */ + + XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* This function stops the IIC device and driver such that data is no longer +* sent or received on the IIC bus. This function stops the device by +* disabling interrupts. This function only disables interrupts within the +* device such that the caller is responsible for disconnecting the interrupt +* handler of the device from the interrupt source and disabling interrupts +* at other levels. +* +* Due to bus throttling that could hold the bus between messages when using +* repeated start option, stop will not occur when the device is actively +* sending or receiving data from the IIC bus or the bus is being throttled +* by this device, but instead return XST_IIC_BUS_BUSY. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* +* @return +* +* - XST_SUCCESS indicates all IIC interrupts are disabled. No messages can +* be received or transmitted until XIic_Start() is called. +* - XST_IIC_BUS_BUSY indicates this device is currently engaged in message +* traffic and cannot be stopped. +* +* @note +* +* None. +* +****************************************************************************/ +XStatus +XIic_Stop(XIic * InstancePtr) +{ + u8 Status; + u8 CntlReg; + + XASSERT_NONVOID(InstancePtr != NULL); + + /* + * Disable all interrupts globally using the IPIF + */ + XIIF_V123B_GINTR_DISABLE(InstancePtr->BaseAddress); + + CntlReg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); + Status = XIo_In8(InstancePtr->BaseAddress + XIIC_SR_REG_OFFSET); + + if ((CntlReg & XIIC_CR_MSMS_MASK) + || (Status & XIIC_SR_ADDR_AS_SLAVE_MASK)) { + /* when this device is using the bus + * - re-enable interrupts to finish current messaging + * - return bus busy + */ + XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); + + return XST_IIC_BUS_BUSY; + } + + InstancePtr->IsStarted = 0; + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Resets the IIC device. Reset must only be called after the driver has been +* initialized. The configuration after this reset is as follows: +* - Repeated start is disabled +* - General call is disabled +* +* The upper layer software is responsible for initializing and re-configuring +* (if necessary) and restarting the IIC device after the reset. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +* @internal +* +* The reset is accomplished by setting the IPIF reset register. This takes +* care of resetting all IPIF hardware blocks, including the IIC device. +* +****************************************************************************/ +void +XIic_Reset(XIic * InstancePtr) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + XIIF_V123B_RESET(InstancePtr->BaseAddress); +} + +/*****************************************************************************/ +/** +* +* This function sets the bus addresses. The addresses include the device +* address that the device responds to as a slave, or the slave address +* to communicate with on the bus. The IIC device hardware is built to +* allow either 7 or 10 bit slave addressing only at build time rather +* than at run time. When this device is a master, slave addressing can +* be selected at run time to match addressing modes for other bus devices. +* +* Addresses are represented as hex values with no adjustment for the data +* direction bit as the software manages address bit placement. +* Example: For a 7 address written to the device of 1010 011X where X is +* the transfer direction (send/recv), the address parameter for this function +* needs to be 01010011 or 0x53 where the correct bit alllignment will be +* handled for 7 as well as 10 bit devices. This is especially important as +* the bit placement is not handled the same depending on which options are +* used such as repeated start. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* @param AddressType indicates which address is being modified; the address +* which this device responds to on the IIC bus as a slave, or the +* slave address to communicate with when this device is a master. One +* of the following values must be contained in this argument. +*
+*   XII_ADDRESS_TO_SEND         Slave being addressed by a this master
+*   XII_ADDRESS_TO_RESPOND      Address to respond to as a slave device
+* 
+* @param Address contains the address to be set; 7 bit or 10 bit address. +* A ten bit address must be within the range: 0 - 1023 and a 7 bit +* address must be within the range 0 - 127. +* +* @return +* +* XST_SUCCESS is returned if the address was successfully set, otherwise one +* of the following errors is returned. +* - XST_IIC_NO_10_BIT_ADDRESSING indicates only 7 bit addressing supported. +* - XST_INVALID_PARAM indicates an invalid parameter was specified. +* +* @note +* +* Upper bits of 10-bit address is written only when current device is built +* as a ten bit device. +* +****************************************************************************/ +XStatus +XIic_SetAddress(XIic * InstancePtr, int AddressType, int Address) +{ + u8 SendAddr; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(Address < 1023); + + /* Set address to respond to for this device into address registers */ + + if (AddressType == XII_ADDR_TO_RESPOND_TYPE) { + SendAddr = (u8) ((Address & 0x007F) << 1); /* Addr in upper 7 bits */ + XIo_Out8(InstancePtr->BaseAddress + XIIC_ADR_REG_OFFSET, + SendAddr); + + if (InstancePtr->Has10BitAddr == TRUE) { + /* Write upper 3 bits of addr to DTR only when 10 bit option + * included in design i.e. register exists + */ + SendAddr = (u8) ((Address & 0x0380) >> 7); + XIo_Out8(InstancePtr->BaseAddress + XIIC_TBA_REG_OFFSET, + SendAddr); + } + + return XST_SUCCESS; + } + + /* Store address of slave device being read from */ + + if (AddressType == XII_ADDR_TO_SEND_TYPE) { + InstancePtr->AddrOfSlave = Address; + return XST_SUCCESS; + } + + return XST_INVALID_PARAM; +} + +/*****************************************************************************/ +/** +* +* This function gets the addresses for the IIC device driver. The addresses +* include the device address that the device responds to as a slave, or the +* slave address to communicate with on the bus. The address returned has the +* same format whether 7 or 10 bits. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* @param AddressType indicates which address, the address which this +* responds to on the IIC bus as a slave, or the slave address to +* communicate with when this device is a master. One of the following +* values must be contained in this argument. +*
+*   XII_ADDRESS_TO_SEND_TYPE         slave being addressed as a master
+*   XII_ADDRESS_TO_RESPOND_TYPE      slave address to respond to as a slave
+* 
+* If neither of the two valid arguments are used, the function returns +* the address of the slave device +* +* @return +* +* The address retrieved. +* +* @note +* +* None. +* +****************************************************************************/ +u16 +XIic_GetAddress(XIic * InstancePtr, int AddressType) +{ + u8 LowAddr; + u16 HighAddr = 0; + + XASSERT_NONVOID(InstancePtr != NULL); + + /* return this devices address */ + + if (AddressType == XII_ADDR_TO_RESPOND_TYPE) { + + LowAddr = + XIo_In8(InstancePtr->BaseAddress + XIIC_ADR_REG_OFFSET); + + if (InstancePtr->Has10BitAddr == TRUE) { + HighAddr = (u16) XIo_In8(InstancePtr->BaseAddress + + XIIC_TBA_REG_OFFSET); + } + return ((HighAddr << 8) & (u16) LowAddr); + } + + /* Otherwise return address of slave device on the IIC bus */ + + return InstancePtr->AddrOfSlave; +} + +/*****************************************************************************/ +/** +* +* A function to determine if the device is currently addressed as a slave +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* +* @return +* +* TRUE if the device is addressed as slave, and FALSE otherwise. +* +* @note +* +* None. +* +****************************************************************************/ +u32 +XIic_IsSlave(XIic * InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + + if ((XIo_In8(InstancePtr->BaseAddress + XIIC_SR_REG_OFFSET) & + XIIC_SR_ADDR_AS_SLAVE_MASK) == 0) { + return FALSE; + } + return TRUE; +} + +/*****************************************************************************/ +/** +* +* Sets the receive callback function, the receive handler, which the driver +* calls when it finishes receiving data. The number of bytes used to signal +* when the receive is complete is the number of bytes set in the XIic_Recv +* function. +* +* The handler executes in an interrupt context such that it must minimize +* the amount of processing performed such as transferring data to a thread +* context. +* +* The number of bytes received is passed to the handler as an argument. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* @param CallBackRef is the upper layer callback reference passed back when +* the callback function is invoked. +* @param FuncPtr is the pointer to the callback function. +* +* @return +* +* None. +* +* @note +* +* The handler is called within interrupt context ... +* +****************************************************************************/ +void +XIic_SetRecvHandler(XIic * InstancePtr, void *CallBackRef, XIic_Handler FuncPtr) +{ + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(FuncPtr != NULL); + + InstancePtr->RecvHandler = FuncPtr; + InstancePtr->RecvCallBackRef = CallBackRef; +} + +/*****************************************************************************/ +/** +* +* Sets the send callback function, the send handler, which the driver calls when +* it receives confirmation of sent data. The handler executes in an interrupt +* context such that it must minimize the amount of processing performed such +* as transferring data to a thread context. +* +* @param InstancePtr the pointer to the XIic instance to be worked on. +* @param CallBackRef the upper layer callback reference passed back when +* the callback function is invoked. +* @param FuncPtr the pointer to the callback function. +* +* @return +* +* None. +* +* @note +* +* The handler is called within interrupt context ... +* +****************************************************************************/ +void +XIic_SetSendHandler(XIic * InstancePtr, void *CallBackRef, XIic_Handler FuncPtr) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + XASSERT_VOID(FuncPtr != NULL); + + InstancePtr->SendHandler = FuncPtr; + InstancePtr->SendCallBackRef = CallBackRef; +} + +/*****************************************************************************/ +/** +* +* Sets the status callback function, the status handler, which the driver calls +* when it encounters conditions which are not data related. The handler +* executes in an interrupt context such that it must minimize the amount of +* processing performed such as transferring data to a thread context. The +* status events that can be returned are described in xiic.h. +* +* @param InstancePtr points to the XIic instance to be worked on. +* @param CallBackRef is the upper layer callback reference passed back when +* the callback function is invoked. +* @param FuncPtr is the pointer to the callback function. +* +* @return +* +* None. +* +* @note +* +* The handler is called within interrupt context ... +* +****************************************************************************/ +void +XIic_SetStatusHandler(XIic * InstancePtr, void *CallBackRef, + XIic_StatusHandler FuncPtr) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + XASSERT_VOID(FuncPtr != NULL); + + InstancePtr->StatusHandler = FuncPtr; + InstancePtr->StatusCallBackRef = CallBackRef; +} + +/*****************************************************************************/ +/** +* +* Looks up the device configuration based on the unique device ID. The table +* IicConfigTable contains the configuration info for each device in the system. +* +* @param DeviceId is the unique device ID to look for +* +* @return +* +* A pointer to the configuration data of the device, or NULL if no match is +* found. +* +* @note +* +* None. +* +******************************************************************************/ +XIic_Config * +XIic_LookupConfig(u16 DeviceId) +{ + XIic_Config *CfgPtr = NULL; + int i; + + for (i = 0; i < XPAR_XIIC_NUM_INSTANCES; i++) { + if (XIic_ConfigTable[i].DeviceId == DeviceId) { + CfgPtr = &XIic_ConfigTable[i]; + break; + } + } + + return CfgPtr; +} + +/***************************************************************************** +* +* This is a stub for the send and recv callbacks. The stub is here in case the +* upper layers forget to set the handlers. +* +* @param CallBackRef is a pointer to the upper layer callback reference +* @param ByteCount is the number of bytes sent or received +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +XIic_StubHandler(void *CallBackRef, int ByteCount) +{ + XASSERT_VOID_ALWAYS(); +} + +/***************************************************************************** +* +* This is a stub for the asynchronous error callback. The stub is here in case +* the upper layers forget to set the handler. +* +* @param CallBackRef is a pointer to the upper layer callback reference +* @param ErrorCode is the Xilinx error code, indicating the cause of the error +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +XIic_StubStatusHandler(void *CallBackRef, XStatus ErrorCode) +{ + XASSERT_VOID_ALWAYS(); +} diff -urN -X bkexcl linux-2.4.22/drivers/i2c/xilinx_iic/xiic.h linuxppc-2.4/drivers/i2c/xilinx_iic/xiic.h --- linux-2.4.22/drivers/i2c/xilinx_iic/xiic.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/xilinx_iic/xiic.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,424 @@ +/* $Id: xiic.h,v 1.3 2003/01/13 21:47:59 meinelte Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xiic.h +* +* XIic is the driver for an IIC master or slave device. + +* In order to reduce the memory requirements of the driver it is partitioned +* such that there are optional parts of the driver. Slave, master, and +* multimaster features are optional such that these files are not required. +* In order to use the slave and multimaster features of the driver, the user +* must call functions (XIic_SlaveInclude and XIic_MultiMasterInclude) +* to dynamically include the code . These functions may be called at any time. +* +* Bus Throttling +* +* The IIC hardware provides bus throttling which allows either the device, as +* either a master or a slave, to stop the clock on the IIC bus. This feature +* allows the software to perform the appropriate processing for each interrupt +* without an unreasonable response restriction. With this design, it is +* important for the user to understand the implications of bus throttling. +* +* Repeated Start +* +* An application can send multiple messages, as a master, to a slave device +* and re-acquire the IIC bus each time a message is sent. The repeated start +* option allows the application to send multiple messages without re-acquiring +* the IIC bus for each message. This feature also could cause the application +* to lock up, or monopolize the IIC bus, should repeated start option be +* enabled and sequences of messages never end (periodic data collection). +* Also when repeated start is not disable before the last master message is +* sent or received, will leave the bus captive to the master, but unused. +* +* Addressing +* +* The IIC hardware is parameterized such that it can be built for 7 or 10 +* bit addresses. The driver provides the ability to control which address +* size is sent in messages as a master to a slave device. The address size +* which the hardware responds to as a slave is parameterized as 7 or 10 bits +* but fixed by the hardware build. +* +* Addresses are represented as hex values with no adjustment for the data +* direction bit as the software manages address bit placement. This is +* especially important as the bit placement is not handled the same depending +* on which options are used such as repeated start and 7 vs 10 bit addessing. +* +* Data Rates +* +* The IIC hardware is parameterized such that it can be built to support +* data rates from DC to 400KBit. The frequency of the interrupts which +* occur is proportional to the data rate. +* +* Polled Mode Operation +* +* This driver does not provide a polled mode of operation primarily because +* polled mode which is non-blocking is difficult with the amount of +* interaction with the hardware that is necessary. +* +* Interrupts +* +* The device has many interrupts which allow IIC data transactions as well +* as bus status processing to occur. +* +* The interrupts are divided into two types, data and status. Data interrupts +* indicate data has been received or transmitted while the status interrupts +* indicate the status of the IIC bus. Some of the interrupts, such as Not +* Addressed As Slave and Bus Not Busy, are only used when these specific +* events must be recognized as opposed to being enabled at all times. +* +* Many of the interrupts are not a single event in that they are continuously +* present such that they must be disabled after recognition or when undesired. +* Some of these interrupts, which are data related, may be acknowledged by the +* software by reading or writing data to the appropriate register, or must +* be disabled. The following interrupts can be continuous rather than single +* events. +* - Data Transmit Register Empty/Transmit FIFO Empty +* - Data Receive Register Full/Receive FIFO +* - Transmit FIFO Half Empty +* - Bus Not Busy +* - Addressed As Slave +* - Not Addressed As Slave +* +* The following interrupts are not passed directly to the application thru the +* status callback. These are only used internally for the driver processing +* and may result in the receive and send handlers being called to indicate +* completion of an operation. The following interrupts are data related +* rather than status. +* - Data Transmit Register Empty/Transmit FIFO Empty +* - Data Receive Register Full/Receive FIFO +* - Transmit FIFO Half Empty +* - Slave Transmit Complete +* +* Interrupt To Event Mapping +* +* The following table provides a mapping of the interrupts to the events which +* are passed to the status handler and the intended role (master or slave) for +* the event. Some interrupts can cause multiple events which are combined +* together into a single status event such as XII_MASTER_WRITE_EVENT and +* XII_GENERAL_CALL_EVENT +*
+* Interrupt                         Event(s)                     Role
+*
+* Arbitration Lost Interrupt        XII_ARB_LOST_EVENT            Master
+* Transmit Error                    XII_SLAVE_NO_ACK_EVENT        Master
+* IIC Bus Not Busy                  XII_BUS_NOT_BUSY_EVENT        Master
+* Addressed As Slave                XII_MASTER_READ_EVENT,        Slave
+*                                   XII_MASTER_WRITE_EVENT,       Slave
+*                                   XII_GENERAL_CALL_EVENT        Slave
+* 
+* Not Addressed As Slave Interrupt +* +* The Not Addressed As Slave interrupt is not passed directly to the +* application thru the status callback. It is used to determine the end of +* a message being received by a slave when there was no stop condition +* (repeated start). It will cause the receive handler to be called to +* indicate completion of the operation. +* +* RTOS Independence +* +* This driver is intended to be RTOS and processor independent. It works +* with physical addresses only. Any needs for dynamic memory management, +* threads or thread mutual exclusion, virtual memory, or cache control must +* be satisfied by the layer above this driver. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.01a rfp  10/19/01 release
+* 1.01c ecm  12/05/02 new rev
+* 
+* +******************************************************************************/ + +#ifndef XIIC_H /* prevent circular inclusions */ +#define XIIC_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xstatus.h" +#include "xipif_v1_23_b.h" +#include "xiic_l.h" + +/************************** Constant Definitions *****************************/ + +/** @name Configuration options + * + * The following options may be specified or retrieved for the device and + * enable/disable additional features of the IIC bus. Each of the options + * are bit fields such that more than one may be specified. + * @{ + */ +/** + *
+ * XII_GENERAL_CALL_OPTION      The general call option allows an IIC slave to
+ *                              recognized the general call address. The status
+ *                              handler is called as usual indicating the device
+ *                              has been addressed as a slave with a general
+ *                              call. It is the application's responsibility to
+ *                              perform any special processing for the general
+ *                              call.
+ *
+ * XII_REPEATED_START_OPTION    The repeated start option allows multiple
+ *                              messages to be sent/received on the IIC bus
+ *                              without rearbitrating for the bus.  The messages
+ *                              are sent as a series of messages such that the
+ *                              option must be enabled before the 1st message of
+ *                              the series, to prevent an stop condition from
+ *                              being generated on the bus, and disabled before
+ *                              the last message of the series, to allow the
+ *                              stop condition to be generated.
+ *
+ * XII_SEND_10_BIT_OPTION       The send 10 bit option allows 10 bit addresses
+ *                              to be sent on the bus when the device is a
+ *                              master. The device can be configured to respond
+ *                              as to 7 bit addresses even though it may be
+ *                              communicating with other devices that support 10
+ *                              bit addresses.  When this option is not enabled,
+ *                              only 7 bit addresses are sent on the bus.
+ *
+ * 
+ */ +#define XII_GENERAL_CALL_OPTION 0x00000001 +#define XII_REPEATED_START_OPTION 0x00000002 +#define XII_SEND_10_BIT_OPTION 0x00000004 + +/*@}*/ + +/** @name Status events + * + * The following status events occur during IIC bus processing and are passed + * to the status callback. Each event is only valid during the appropriate + * processing of the IIC bus. Each of these events are bit fields such that + * more than one may be specified. + * @{ + */ +/** + *
+ *   XII_BUS_NOT_BUSY_EVENT      bus transitioned to not busy
+ *   XII_ARB_LOST_EVENT          arbitration was lost
+ *   XII_SLAVE_NO_ACK_EVENT      slave did not acknowledge data (had error)
+ *   XII_MASTER_READ_EVENT       master reading from slave
+ *   XII_MASTER_WRITE_EVENT      master writing to slave
+ *   XII_GENERAL_CALL_EVENT      general call to all slaves
+ * 
+ */ +#define XII_BUS_NOT_BUSY_EVENT 0x00000001 +#define XII_ARB_LOST_EVENT 0x00000002 +#define XII_SLAVE_NO_ACK_EVENT 0x00000004 +#define XII_MASTER_READ_EVENT 0x00000008 +#define XII_MASTER_WRITE_EVENT 0x00000010 +#define XII_GENERAL_CALL_EVENT 0x00000020 +/*@}*/ + +/* The following address types are used when setting and getting the addresses + * of the driver. These are mutually exclusive such that only one or the other + * may be specified. + */ +/** bus address of slave device */ +#define XII_ADDR_TO_SEND_TYPE 1 +/** this device's bus address when slave */ +#define XII_ADDR_TO_RESPOND_TYPE 2 + +/**************************** Type Definitions *******************************/ + +/** + * This typedef contains configuration information for the device. + */ +typedef struct { + u16 DeviceId; /**< Unique ID of device */ + u32 BaseAddress;/**< Device base address */ + u32 Has10BitAddr; + /**< does device have 10 bit address decoding */ +} XIic_Config; + +/** + * This callback function data type is defined to handle the asynchronous + * processing of sent and received data of the IIC driver. The application + * using this driver is expected to define a handler of this type to support + * interrupt driven mode. The handlers are called in an interrupt context such + * that minimal processing should be performed. The handler data type is + * utilized for both send and receive handlers. + * + * @param CallBackRef is a callback reference passed in by the upper layer when + * setting the callback functions, and passed back to the upper layer + * when the callback is invoked. Its type is unimportant to the driver + * component, so it is a void pointer. + * + * @param ByteCount indicates the number of bytes remaining to be sent or + * received. A value of zero indicates that the requested number of + * bytes were sent or received. + */ +typedef void (*XIic_Handler) (void *CallBackRef, int ByteCount); + +/** + * This callback function data type is defined to handle the asynchronous + * processing of status events of the IIC driver. The application using + * this driver is expected to define a handler of this type to support + * interrupt driven mode. The handler is called in an interrupt context such + * that minimal processing should be performed. + * + * @param CallBackRef is a callback reference passed in by the upper layer when + * setting the callback functions, and passed back to the upper layer + * when the callback is invoked. Its type is unimportant to the driver + * component, so it is a void pointer. + * + * @param StatusEvent indicates one or more status events that occurred. See + * the definition of the status events above. + */ +typedef void (*XIic_StatusHandler) (void *CallBackRef, XStatus StatusEvent); + +/** + * XIic statistics + */ +typedef struct { + u8 ArbitrationLost;/**< Number of times arbitration was lost */ + u8 RepeatedStarts; /**< Number of repeated starts */ + u8 BusBusy; /**< Number of times bus busy status returned */ + u8 RecvBytes; /**< Number of bytes received */ + u8 RecvInterrupts; /**< Number of receive interrupts */ + u8 SendBytes; /**< Number of transmit bytes received */ + u8 SendInterrupts; /**< Number of transmit interrupts */ + u8 TxErrors; /**< Number of transmit errors (no ack) */ + u8 IicInterrupts; /**< Number of IIC (device) interrupts */ +} XIicStats; + +/** + * The XIic driver instance data. The user is required to allocate a + * variable of this type for every IIC device in the system. A pointer + * to a variable of this type is then passed to the driver API functions. + */ +typedef struct { + XIicStats Stats; /* Statistics */ + u32 BaseAddress; /* Device base address */ + u32 Has10BitAddr; /* TRUE when 10 bit addressing in design */ + u32 IsReady; /* Device is initialized and ready */ + u32 IsStarted; /* Device has been started */ + int AddrOfSlave; /* Slave addr writing to */ + + u32 Options; /* current operating options */ + u8 *SendBufferPtr; /* Buffer to send (state) */ + u8 *RecvBufferPtr; /* Buffer to receive (state) */ + u8 TxAddrMode; /* State of Tx Address transmission */ + int SendByteCount; /* Number of data bytes in buffer (state) */ + int RecvByteCount; /* Number of empty bytes in buffer (state) */ + + u32 BNBOnly; /* TRUE when BNB interrupt needs to */ + /* call callback */ + + XIic_StatusHandler StatusHandler; + void *StatusCallBackRef; /* Callback reference for status handler */ + XIic_Handler RecvHandler; + void *RecvCallBackRef; /* Callback reference for recv handler */ + XIic_Handler SendHandler; + void *SendCallBackRef; /* Callback reference for send handler */ + +} XIic; + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/* + * Required functions in xiic.c + */ +XStatus XIic_Initialize(XIic * InstancePtr, u16 DeviceId); + +XStatus XIic_Start(XIic * InstancePtr); +XStatus XIic_Stop(XIic * InstancePtr); + +void XIic_Reset(XIic * InstancePtr); + +XStatus XIic_SetAddress(XIic * InstancePtr, int AddressType, int Address); +u16 XIic_GetAddress(XIic * InstancePtr, int AddressType); + +XIic_Config *XIic_LookupConfig(u16 DeviceId); + +/* + * Interrupt (currently required) functions in xiic_intr.c + */ +void XIic_InterruptHandler(void *InstancePtr); +void XIic_SetRecvHandler(XIic * InstancePtr, void *CallBackRef, + XIic_Handler FuncPtr); +void XIic_SetSendHandler(XIic * InstancePtr, void *CallBackRef, + XIic_Handler FuncPtr); +void XIic_SetStatusHandler(XIic * InstancePtr, void *CallBackRef, + XIic_StatusHandler FuncPtr); +/* + * Master send and receive functions in xiic_master.c + */ +XStatus XIic_MasterRecv(XIic * InstancePtr, u8 * RxMsgPtr, int ByteCount); +XStatus XIic_MasterSend(XIic * InstancePtr, u8 * TxMsgPtr, int ByteCount); + +/* + * Slave send and receive functions in xiic_slave.c + */ +void XIic_SlaveInclude(void); +XStatus XIic_SlaveRecv(XIic * InstancePtr, u8 * RxMsgPtr, int ByteCount); +XStatus XIic_SlaveSend(XIic * InstancePtr, u8 * TxMsgPtr, int ByteCount); + +/* + * Statistics functions in xiic_stats.c + */ +void XIic_GetStats(XIic * InstancePtr, XIicStats * StatsPtr); +void XIic_ClearStats(XIic * InstancePtr); + +/* + * Self test functions in xiic_selftest.c + */ +XStatus XIic_SelfTest(XIic * InstancePtr); + +/* + * Options functions in xiic_options.c + */ +void XIic_SetOptions(XIic * InstancePtr, u32 Options); +u32 XIic_GetOptions(XIic * InstancePtr); + +/* + * Multi-master functions in xiic_multi_master.c + */ +void XIic_MultiMasterInclude(void); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/drivers/i2c/xilinx_iic/xiic_g.c linuxppc-2.4/drivers/i2c/xilinx_iic/xiic_g.c --- linux-2.4.22/drivers/i2c/xilinx_iic/xiic_g.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/xilinx_iic/xiic_g.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,59 @@ + +/******************************************************************* +* +* CAUTION: This file is automatically generated by libgen. +* Version: Xilinx EDK 3.2.2 EDK_Cm.22 +* DO NOT EDIT. +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +* Description: Driver configuration +* +*******************************************************************/ + +#include "xparameters.h" +#include "xiic.h" + +/* +* The configuration table for devices +*/ + +XIic_Config XIic_ConfigTable[] = { + { + XPAR_OPB_IIC_0_DEVICE_ID, + XPAR_OPB_IIC_0_BASEADDR, + XPAR_OPB_IIC_0_TEN_BIT_ADR} +}; diff -urN -X bkexcl linux-2.4.22/drivers/i2c/xilinx_iic/xiic_i.h linuxppc-2.4/drivers/i2c/xilinx_iic/xiic_i.h --- linux-2.4.22/drivers/i2c/xilinx_iic/xiic_i.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/xilinx_iic/xiic_i.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,480 @@ +/* $Id: xiic_i.h,v 1.2 2002/12/05 19:32:40 meinelte Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xiic_i.h +* +* This header file contains internal identifiers, which are those shared +* between XIic components. The identifiers in this file are not intended for +* use external to the driver. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.01a rfp  10/19/01 release
+* 1.01c ecm  12/05/02 new rev
+* 
+* +******************************************************************************/ + +#ifndef XIIC_I_H /* prevent circular inclusions */ +#define XIIC_I_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xstatus.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/****************************************************************************** +* +* This macro sends the first byte of the address for a 10 bit address during +* both read and write operations. It takes care of the details to format the +* address correctly. +* +* address = 1111_0xxD xx = address MSBits +* D = Tx direction = 0 = write +* +* @param SlaveAddress contains the address of the slave to send to. +* +* @param Operation indicates XIIC_READ_OPERATION or XIIC_WRITE_OPERATION +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mSend10BitAddrByte1(u16 SlaveAddress, u8 Operation); +* +******************************************************************************/ +#define XIic_mSend10BitAddrByte1(SlaveAddress, Operation) \ +{ \ + u8 LocalAddr = (u8)((SlaveAddress) >> 7); \ + LocalAddr = (LocalAddr & 0xF6) | 0xF0 | (Operation); \ + XIo_Out8(InstancePtr->BaseAddress + XIIC_DTR_REG_OFFSET, LocalAddr); \ +} + +/****************************************************************************** +* +* This macro sends the second byte of the address for a 10 bit address during +* both read and write operations. It takes care of the details to format the +* address correctly. +* +* @param SlaveAddress contains the address of the slave to send to. +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mSend10BitAddrByte2(u16 SlaveAddress, +* u8 Operation); +* +******************************************************************************/ +#define XIic_mSend10BitAddrByte2(SlaveAddress) \ + XIo_Out8(InstancePtr->BaseAddress + XIIC_DTR_REG_OFFSET, \ + (u8)(SlaveAddress)); + +/****************************************************************************** +* +* This macro sends the address for a 7 bit address during both read and write +* operations. It takes care of the details to format the address correctly. +* +* @param SlaveAddress contains the address of the slave to send to. +* +* @param Operation indicates XIIC_READ_OPERATION or XIIC_WRITE_OPERATION +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mSend7BitAddr(u16 SlaveAddress, u8 Operation); +* +******************************************************************************/ +#define XIic_mSend7BitAddr(SlaveAddress, Operation) \ +{ \ + u8 LocalAddr = (u8)(SlaveAddress << 1); \ + LocalAddr = (LocalAddr & 0xFE) | (Operation); \ + XIo_Out8(InstancePtr->BaseAddress + XIIC_DTR_REG_OFFSET, LocalAddr); \ +} + +/****************************************************************************** +* +* This macro disables the specified interrupts in the IPIF interrupt enable +* register. It is non-destructive in that the register is read and only the +* interrupts specified is changed. +* +* @param BaseAddress contains the IPIF registers base address. +* +* @param InterruptMask contains the interrupts to be disabled +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mDisableIntr(u32 BaseAddress, +* u32 InterruptMask); +* +******************************************************************************/ +#define XIic_mDisableIntr(BaseAddress, InterruptMask) \ + XIIF_V123B_WRITE_IIER((BaseAddress), \ + XIIF_V123B_READ_IIER(BaseAddress) & ~(InterruptMask)) + +/****************************************************************************** +* +* This macro enables the specified interrupts in the IPIF interrupt enable +* register. It is non-destructive in that the register is read and only the +* interrupts specified is changed. +* +* @param BaseAddress contains the IPIF registers base address. +* +* @param InterruptMask contains the interrupts to be disabled +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mEnableIntr(u32 BaseAddress, +* u32 InterruptMask); +* +******************************************************************************/ +#define XIic_mEnableIntr(BaseAddress, InterruptMask) \ + XIIF_V123B_WRITE_IIER((BaseAddress), \ + XIIF_V123B_READ_IIER(BaseAddress) | (InterruptMask)) + +/****************************************************************************** +* +* This macro clears the specified interrupt in the IPIF interrupt status +* register. It is non-destructive in that the register is read and only the +* interrupt specified is cleared. Clearing an interrupt acknowledges it. +* +* @param BaseAddress contains the IPIF registers base address. +* +* @param InterruptMask contains the interrupts to be disabled +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mClearIntr(u32 BaseAddress, +* u32 InterruptMask); +* +******************************************************************************/ +#define XIic_mClearIntr(BaseAddress, InterruptMask) \ + XIIF_V123B_WRITE_IISR((BaseAddress), \ + XIIF_V123B_READ_IISR(BaseAddress) & (InterruptMask)) + +/****************************************************************************** +* +* This macro clears and enables the specified interrupt in the IPIF interrupt +* status and enable registers. It is non-destructive in that the registers are +* read and only the interrupt specified is modified. +* Clearing an interrupt acknowledges it. +* +* @param BaseAddress contains the IPIF registers base address. +* +* @param InterruptMask contains the interrupts to be cleared and enabled +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mClearEnableIntr(u32 BaseAddress, +* u32 InterruptMask); +* +******************************************************************************/ +#define XIic_mClearEnableIntr(BaseAddress, InterruptMask) \ +{ \ + XIIF_V123B_WRITE_IISR(BaseAddress, \ + (XIIF_V123B_READ_IISR(BaseAddress) & (InterruptMask))); \ + \ + XIIF_V123B_WRITE_IIER(BaseAddress, \ + (XIIF_V123B_READ_IIER(BaseAddress) | (InterruptMask))); \ +} + +/****************************************************************************** +* +* This macro flushes the receive FIFO such that all bytes contained within it +* are discarded. +* +* @param InstancePtr is a pointer to the IIC instance containing the FIFO +* to be flushed. +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mFlushRxFifo(XIic *InstancePtr); +* +******************************************************************************/ +#define XIic_mFlushRxFifo(InstancePtr) \ +{ \ + int LoopCnt; \ + u8 Temp; \ + u8 BytesToRead = XIo_In8(InstancePtr->BaseAddress + \ + XIIC_RFO_REG_OFFSET) + 1; \ + for(LoopCnt = 0; LoopCnt < BytesToRead; LoopCnt++) \ + { \ + Temp = XIo_In8(InstancePtr->BaseAddress + XIIC_DRR_REG_OFFSET); \ + } \ +} + +/****************************************************************************** +* +* This macro flushes the transmit FIFO such that all bytes contained within it +* are discarded. +* +* @param InstancePtr is a pointer to the IIC instance containing the FIFO +* to be flushed. +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mFlushTxFifo(XIic *InstancePtr); +* +******************************************************************************/ +#define XIic_mFlushTxFifo(InstancePtr); \ +{ \ + u8 CntlReg = XIo_In8(InstancePtr->BaseAddress + \ + XIIC_CR_REG_OFFSET); \ + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, \ + CntlReg | XIIC_CR_TX_FIFO_RESET_MASK); \ + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, CntlReg); \ +} + +/****************************************************************************** +* +* This macro reads the next available received byte from the receive FIFO +* and updates all the data structures to reflect it. +* +* @param InstancePtr is a pointer to the IIC instance to be operated on. +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mReadRecvByte(XIic *InstancePtr); +* +******************************************************************************/ +#define XIic_mReadRecvByte(InstancePtr) \ +{ \ + *InstancePtr->RecvBufferPtr++ = \ + XIo_In8(InstancePtr->BaseAddress + XIIC_DRR_REG_OFFSET); \ + InstancePtr->RecvByteCount--; \ + InstancePtr->Stats.RecvBytes++; \ +} + +/****************************************************************************** +* +* This macro writes the next byte to be sent to the transmit FIFO +* and updates all the data structures to reflect it. +* +* @param InstancePtr is a pointer to the IIC instance to be operated on. +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mWriteSendByte(XIic *InstancePtr); +* +******************************************************************************/ +#define XIic_mWriteSendByte(InstancePtr) \ +{ \ + XIo_Out8(InstancePtr->BaseAddress + XIIC_DTR_REG_OFFSET, \ + *InstancePtr->SendBufferPtr++); \ + InstancePtr->SendByteCount--; \ + InstancePtr->Stats.SendBytes++; \ +} + +/****************************************************************************** +* +* This macro sets up the control register for a master receive operation. +* A write is necessary if a 10 bit operation is being performed. +* +* @param InstancePtr is a pointer to the IIC instance to be operated on. +* +* @param ControlRegister contains the contents of the IIC device control +* register +* +* @param ByteCount contains the number of bytes to be received for the +* master receive operation +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mSetControlRegister(XIic *InstancePtr, +* u8 ControlRegister, +* int ByteCount); +* +******************************************************************************/ +#define XIic_mSetControlRegister(InstancePtr, ControlRegister, ByteCount) \ +{ \ + (ControlRegister) &= ~(XIIC_CR_NO_ACK_MASK | XIIC_CR_DIR_IS_TX_MASK); \ + if (InstancePtr->Options & XII_SEND_10_BIT_OPTION) \ + { \ + (ControlRegister) |= XIIC_CR_DIR_IS_TX_MASK; \ + } \ + else \ + { \ + if ((ByteCount) == 1) \ + { \ + (ControlRegister) |= XIIC_CR_NO_ACK_MASK; \ + } \ + } \ +} + +/****************************************************************************** +* +* This macro enters a critical region by disabling the global interrupt bit +* in the IPIF. +* +* @param BaseAddress contains the IPIF registers base address. +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mEnterCriticalRegion(u32 BaseAddress) +* +******************************************************************************/ +#define XIic_mEnterCriticalRegion(BaseAddress) \ + XIIF_V123B_GINTR_DISABLE(BaseAddress) + +/****************************************************************************** +* +* This macro exits a critical region by enabling the global interrupt bit +* in the IPIF. +* +* @param BaseAddress contains the IPIF registers base address. +* +* @return +* +* None. +* +* @note +* +* Signature: void XIic_mExitCriticalRegion(u32 BaseAddress) +* +******************************************************************************/ +#define XIic_mExitCriticalRegion(BaseAddress) \ + XIIF_V123B_GINTR_ENABLE(BaseAddress) + +/****************************************************************************** +* +* This macro clears the statistics of an instance such that it can be common +* such that some parts of the driver may be optional. +* +* @param InstancePtr is a pointer to the IIC instance to be operated on. +* +* @return +* +* None. +* +* @note +* +* Signature: void XIIC_CLEAR_STATS(XIic *InstancePtr) +* +******************************************************************************/ +#define XIIC_CLEAR_STATS(InstancePtr) \ +{ \ + u8 NumBytes; \ + u8 *DestPtr; \ + \ + DestPtr = (u8 *)&InstancePtr->Stats; \ + for (NumBytes = 0; NumBytes < sizeof(XIicStats); NumBytes++) \ + { \ + *DestPtr++ = 0; \ + } \ +} + +/************************** Function Prototypes ******************************/ + +extern XIic_Config XIic_ConfigTable[]; + +/* The following variables are shared across files of the driver and + * are function pointers that are necessary to break dependencies allowing + * optional parts of the driver to be used without condition compilation + */ +extern void (*XIic_AddrAsSlaveFuncPtr) (XIic * InstancePtr); +extern void (*XIic_NotAddrAsSlaveFuncPtr) (XIic * InstancePtr); +extern void (*XIic_RecvSlaveFuncPtr) (XIic * InstancePtr); +extern void (*XIic_SendSlaveFuncPtr) (XIic * InstancePtr); +extern void (*XIic_RecvMasterFuncPtr) (XIic * InstancePtr); +extern void (*XIic_SendMasterFuncPtr) (XIic * InstancePtr); +extern void (*XIic_ArbLostFuncPtr) (XIic * InstancePtr); +extern void (*XIic_BusNotBusyFuncPtr) (XIic * InstancePtr); + +void XIic_TransmitFifoFill(XIic * InstancePtr, int Role); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/drivers/i2c/xilinx_iic/xiic_intr.c linuxppc-2.4/drivers/i2c/xilinx_iic/xiic_intr.c --- linux-2.4.22/drivers/i2c/xilinx_iic/xiic_intr.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/xilinx_iic/xiic_intr.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,437 @@ +/* $Id: xiic_intr.c,v 1.5 2003/05/14 13:43:30 robertm Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xiic_intr.c +* +* Contains interrupt functions of the XIic driver. This file is required +* for the driver. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.01a rfp  10/19/01 release
+* 1.01c ecm  12/05/02 new rev
+* 1.01c rmm  05/14/03 Fixed diab compiler warnings relating to asserts.
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xiic.h" +#include "xiic_i.h" +#include "xio.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions ******************/ + +/*************** Macros (Inline Functions) Definitions ********************/ + +/************************** Function Prototypes ****************************/ + +static void StubFunction(XIic * InstancePtr); +static void TxErrorHandler(XIic * InstancePtr); + +/************************** Variable Definitions *****************************/ + +/* The following function pointers are used to help allow finer partitioning + * of the driver such that some parts of it are optional. These pointers are + * setup by functions in the optional parts of the driver. + */ +void (*XIic_AddrAsSlaveFuncPtr) (XIic * InstancePtr) = StubFunction; +void (*XIic_NotAddrAsSlaveFuncPtr) (XIic * InstancePtr) = StubFunction; +void (*XIic_RecvSlaveFuncPtr) (XIic * InstancePtr) = StubFunction; +void (*XIic_SendSlaveFuncPtr) (XIic * InstancePtr) = StubFunction; +void (*XIic_RecvMasterFuncPtr) (XIic * InstancePtr) = StubFunction; +void (*XIic_SendMasterFuncPtr) (XIic * InstancePtr) = StubFunction; +void (*XIic_ArbLostFuncPtr) (XIic * InstancePtr) = StubFunction; +void (*XIic_BusNotBusyFuncPtr) (XIic * InstancePtr) = StubFunction; + +/*****************************************************************************/ +/** +* +* This function is the interrupt handler for the XIic driver. This function +* should be connected to the interrupt system. +* +* Only one interrupt source is handled for each interrupt allowing +* higher priority system interrupts quicker response time. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* +* @return +* +* None. +* +* @internal +* +* The XIIC_INTR_ARB_LOST_MASK and XIIC_INTR_TX_ERROR_MASK interrupts must have +* higher priority than the other device interrupts so that the IIC device does +* not get into a potentially confused state. The remaining interrupts may be +* rearranged with no harm. +* +* All XIic device interrupts are ORed into one device interrupt. This routine +* reads the pending interrupts via the IpIf interface and masks that with the +* interrupt mask to evaluate only the interrupts enabled. +* +******************************************************************************/ +void +XIic_InterruptHandler(void *InstancePtr) +{ + u8 Status; + u32 IntrStatus; + u32 IntrPending; + u32 IntrEnable; + XIic *IicPtr = NULL; + u32 Clear = 0; + + /* + * Verify that each of the inputs are valid. + */ + XASSERT_VOID(InstancePtr != NULL); + + /* + * Convert the non-typed pointer to an IIC instance pointer + */ + IicPtr = (XIic *) InstancePtr; + + /* Get the interrupt Status from the IPIF. There is no clearing of + * interrupts in the IPIF. Interrupts must be cleared at the source. + * To find which interrupts are pending; AND interrupts pending with + * interrupts masked. + */ + IntrPending = XIIF_V123B_READ_IISR(IicPtr->BaseAddress); + IntrEnable = XIIF_V123B_READ_IIER(IicPtr->BaseAddress); + IntrStatus = IntrPending & IntrEnable; + + /* Do not processes a devices interrupts if the device has no + * interrupts pending or the global interrupts have been disabled + */ + + if ((IntrStatus == 0) | + (XIIF_V123B_IS_GINTR_ENABLED(IicPtr->BaseAddress) == FALSE)) { + return; + } + + /* Update interrupt stats and get the contents of the status register + */ + IicPtr->Stats.IicInterrupts++; + Status = XIo_In8(IicPtr->BaseAddress + XIIC_SR_REG_OFFSET); + + /* Service requesting interrupt + */ + if (IntrStatus & XIIC_INTR_ARB_LOST_MASK) { + /* Bus Arbritration Lost */ + + IicPtr->Stats.ArbitrationLost++; + XIic_ArbLostFuncPtr(IicPtr); + + Clear = XIIC_INTR_ARB_LOST_MASK; + } + + else if (IntrStatus & XIIC_INTR_TX_ERROR_MASK) { + /* Transmit errors (no acknowledge) received */ + + IicPtr->Stats.TxErrors++; + TxErrorHandler(IicPtr); + + Clear = XIIC_INTR_TX_ERROR_MASK; + } + + else if (IntrStatus & XIIC_INTR_NAAS_MASK) { + /* Not Addressed As Slave */ + + XIic_NotAddrAsSlaveFuncPtr(IicPtr); + Clear = XIIC_INTR_NAAS_MASK; + } + + else if (IntrStatus & XIIC_INTR_RX_FULL_MASK) { + /* Receive register/FIFO is full */ + + IicPtr->Stats.RecvInterrupts++; + + if (Status & XIIC_SR_ADDR_AS_SLAVE_MASK) { + XIic_RecvSlaveFuncPtr(InstancePtr); + } else { + XIic_RecvMasterFuncPtr(InstancePtr); + } + + Clear = XIIC_INTR_RX_FULL_MASK; + } + + else if (IntrStatus & XIIC_INTR_AAS_MASK) { + /* Addressed As Slave */ + + XIic_AddrAsSlaveFuncPtr(IicPtr); + Clear = XIIC_INTR_AAS_MASK; + } + + else if (IntrStatus & XIIC_INTR_BNB_MASK) { + /* IIC bus has transitioned to not busy */ + + /* check if send callback needs to run */ + if (IicPtr->BNBOnly == TRUE) { + XIic_BusNotBusyFuncPtr(IicPtr); + IicPtr->BNBOnly = FALSE; + } else { + IicPtr->SendHandler(IicPtr->SendCallBackRef, 0); + } + + Clear = XIIC_INTR_BNB_MASK; + + /* The bus is not busy, disable BusNotBusy interrupt */ + XIic_mDisableIntr(IicPtr->BaseAddress, XIIC_INTR_BNB_MASK); + + } + + else if ((IntrStatus & XIIC_INTR_TX_EMPTY_MASK) || + (IntrStatus & XIIC_INTR_TX_HALF_MASK)) { + /* Transmit register/FIFO is empty or ½ empty * + */ + IicPtr->Stats.SendInterrupts++; + + if (Status & XIIC_SR_ADDR_AS_SLAVE_MASK) { + XIic_SendSlaveFuncPtr(InstancePtr); + } else { + XIic_SendMasterFuncPtr(InstancePtr); + } + + /* Clear Interrupts + */ + IntrStatus = XIIF_V123B_READ_IISR(IicPtr->BaseAddress); + Clear = IntrStatus & (XIIC_INTR_TX_EMPTY_MASK | + XIIC_INTR_TX_HALF_MASK); + } + + XIIF_V123B_WRITE_IISR(IicPtr->BaseAddress, Clear); +} + +/****************************************************************************** +* +* This function fills the FIFO using the occupancy register to determine the +* available space to be filled. When the repeated start option is on, the last +* byte is withheld to allow the control register to be properly set on the last +* byte. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* +* @param Role indicates the role of this IIC device, a slave or a master, on +* the IIC bus (XIIC_SLAVE_ROLE or XIIC_MASTER_ROLE) +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XIic_TransmitFifoFill(XIic * InstancePtr, int Role) +{ + u8 AvailBytes; + s32 LoopCnt; + s32 NumBytesToSend; + + /* Determine number of bytes to write to FIFO. Number of bytes that can be + * put into the FIFO is (FIFO depth) - (current occupancy + 1) + * When more room in FIFO than msg bytes put all of message in the FIFO. + */ + AvailBytes = IIC_TX_FIFO_DEPTH - + (XIo_In8(InstancePtr->BaseAddress + XIIC_TFO_REG_OFFSET) + 1); + + if (InstancePtr->SendByteCount > AvailBytes) { + NumBytesToSend = AvailBytes; + } else { + /* More space in FIFO than bytes in message + */ + if ((InstancePtr->Options & XII_REPEATED_START_OPTION) || + (Role == XIIC_SLAVE_ROLE)) { + NumBytesToSend = InstancePtr->SendByteCount; + } else { + NumBytesToSend = InstancePtr->SendByteCount - 1; + } + } + + /* fill FIFO with amount determined above */ + + for (LoopCnt = 0; LoopCnt < NumBytesToSend; LoopCnt++) { + XIic_mWriteSendByte(InstancePtr); + } +} + +/*****************************************************************************/ +/** +* +* This interrupt occurs four different ways: Two as master and two as slave. +* Master: +*
+*  (1) Transmitter (IMPLIES AN ERROR)
+*      The slave receiver did not acknowledge properly.
+*  (2) Receiver (Implies tx complete)
+*      Interrupt caused by setting TxAck high in the IIC to indicate to the
+*      the last byte has been transmitted.
+* 
+* +* Slave: +*
+*  (3) Transmitter (Implies tx complete)
+*      Interrupt caused by master device indicating last byte of the message
+*      has been transmitted.
+*  (4) Receiver (IMPLIES AN ERROR)
+*      Interrupt caused by setting TxAck high in the IIC to indicate Rx
+*      IIC had a problem - set by this device and condition already known
+*      and interrupt is not enabled.
+* 
+* +* This interrupt is enabled during Master send and receive and disabled +* when this device knows it is going to send a negative acknowledge (Ack = No). +* +* Signals user of Tx error via status callback sending: XII_TX_ERROR_EVENT +* +* When MasterRecv has no message to send and only receives one byte of data +* from the salve device, the TxError must be enabled to catch addressing +* errors, yet there is not opportunity to disable TxError when there is no +* data to send allowing disabling on last byte. When the slave sends the +* only byte the NOAck causes a Tx Error. To disregard this as no real error, +* when there is data in the Receive FIFO/register then the error was not +* a device address write error, but a NOACK read error - to be ignored. +* To work with or without FIFO's, the Rx Data interrupt is used to indicate +* data is in the rx register. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* No action is required to clear this interrupt in the device as it is a +* pulse. The interrupt need only be cleared in the IpIf interface. +* +******************************************************************************/ +static void +TxErrorHandler(XIic * InstancePtr) +{ + u32 IntrStatus; + u8 CntlReg; + + /* When Sending as a slave, Tx error signals end of msg. Not Addressed As + * Slave will handle the callbacks. this is used to only flush the Tx fifo. + * The addressed as slave bit is gone as soon as the bus has been released + * such that the buffer pointers are used to determine the direction of + * transfer (send or receive). + */ + if (InstancePtr->RecvBufferPtr == NULL) { + /* Master Receiver finished reading message. Flush Tx fifo to remove an + * 0xFF that was written to prevent bus throttling, and disable all + * transmit and receive interrupts + */ + XIic_mFlushTxFifo(InstancePtr); + XIic_mDisableIntr(InstancePtr->BaseAddress, + XIIC_TX_RX_INTERRUPTS); + + return; + } + + /* Data in the receive register from either master or slave receive + * When:slave, indicates master sent last byte, message completed. + * When:master, indicates a master Receive with one byte received. When a + * byte is in Rx reg then the Tx error indicates the Rx data was recovered + * normally Tx errors are not enabled such that this should not occur. + */ + IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress); + if (IntrStatus & XIIC_INTR_RX_FULL_MASK) { + /* Rx Reg/FIFO has data, Disable tx error interrupts */ + + XIic_mDisableIntr(InstancePtr->BaseAddress, + XIIC_INTR_TX_ERROR_MASK); + return; + } + + XIic_mFlushTxFifo(InstancePtr); + + /* Disable and clear tx empty, ½ empty, Rx Full or tx error interrupts + */ + XIic_mDisableIntr(InstancePtr->BaseAddress, XIIC_TX_RX_INTERRUPTS); + XIic_mClearIntr(InstancePtr->BaseAddress, XIIC_TX_RX_INTERRUPTS); + + /* Clear MSMS as on TX error when Rxing, the bus will be + * stopped but MSMS bit is still set. Reset to proper state + */ + CntlReg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); + CntlReg &= ~XIIC_CR_MSMS_MASK; + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, CntlReg); + + /* set FIFO occupancy depth = 1 so that the first byte will throttle + * next recieve msg + */ + XIo_Out8(InstancePtr->BaseAddress + XIIC_RFD_REG_OFFSET, 0); + + /* make event callback */ + + InstancePtr->StatusHandler(InstancePtr->StatusCallBackRef, + XII_SLAVE_NO_ACK_EVENT); +} + +/*****************************************************************************/ +/** +* +* This function is a stub function that is used for the default function for +* events that are handled optionally only when the appropriate modules are +* linked in. Function pointers are used to handle some events to allow +* some events to be optionally handled. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* +******************************************************************************/ +static void +StubFunction(XIic * InstancePtr) +{ + XASSERT_VOID_ALWAYS(); +} diff -urN -X bkexcl linux-2.4.22/drivers/i2c/xilinx_iic/xiic_l.h linuxppc-2.4/drivers/i2c/xilinx_iic/xiic_l.h --- linux-2.4.22/drivers/i2c/xilinx_iic/xiic_l.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/xilinx_iic/xiic_l.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,166 @@ +/* $Id: xiic_l.h,v 1.2 2002/12/05 19:32:40 meinelte Exp $ */ +/***************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +*****************************************************************************/ +/****************************************************************************/ +/** +* +* @file xiic_l.h +* +* This header file contains identifiers and low-level driver functions (or +* macros) that can be used to access the device. High-level driver functions +* are defined in xiic.h. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00b jhl  05/07/02 First release
+* 1.01c ecm  12/05/02 new rev
+* 
+* +*****************************************************************************/ + +#ifndef XIIC_L_H /* prevent circular inclusions */ +#define XIIC_L_H /* by using protection macros */ + +/***************************** Include Files ********************************/ + +#include "xbasic_types.h" + +/************************** Constant Definitions ****************************/ + +#define XIIC_MSB_OFFSET 3 + +#define XIIC_REG_OFFSET 0x100 + XIIC_MSB_OFFSET + +/* + * Register offsets in bytes from RegisterBase. Three is added to the + * base offset to access LSB (IBM style) of the word + */ +#define XIIC_CR_REG_OFFSET 0x00+XIIC_REG_OFFSET /* Control Register */ +#define XIIC_SR_REG_OFFSET 0x04+XIIC_REG_OFFSET /* Status Register */ +#define XIIC_DTR_REG_OFFSET 0x08+XIIC_REG_OFFSET /* Data Tx Register */ +#define XIIC_DRR_REG_OFFSET 0x0C+XIIC_REG_OFFSET /* Data Rx Register */ +#define XIIC_ADR_REG_OFFSET 0x10+XIIC_REG_OFFSET /* Address Register */ +#define XIIC_TFO_REG_OFFSET 0x14+XIIC_REG_OFFSET /* Tx FIFO Occupancy */ +#define XIIC_RFO_REG_OFFSET 0x18+XIIC_REG_OFFSET /* Rx FIFO Occupancy */ +#define XIIC_TBA_REG_OFFSET 0x1C+XIIC_REG_OFFSET /* 10 Bit Address reg */ +#define XIIC_RFD_REG_OFFSET 0x20+XIIC_REG_OFFSET /* Rx FIFO Depth reg */ + +/* Control Register masks */ + +#define XIIC_CR_ENABLE_DEVICE_MASK 0x01 /* Device enable = 1 */ +#define XIIC_CR_TX_FIFO_RESET_MASK 0x02 /* Transmit FIFO reset=1 */ +#define XIIC_CR_MSMS_MASK 0x04 /* Master starts Txing=1 */ +#define XIIC_CR_DIR_IS_TX_MASK 0x08 /* Dir of tx. Txing=1 */ +#define XIIC_CR_NO_ACK_MASK 0x10 /* Tx Ack. NO ack = 1 */ +#define XIIC_CR_REPEATED_START_MASK 0x20 /* Repeated start = 1 */ +#define XIIC_CR_GENERAL_CALL_MASK 0x40 /* Gen Call enabled = 1 */ + +/* Status Register masks */ + +#define XIIC_SR_GEN_CALL_MASK 0x01 /* 1=a mstr issued a GC */ +#define XIIC_SR_ADDR_AS_SLAVE_MASK 0x02 /* 1=when addr as slave */ +#define XIIC_SR_BUS_BUSY_MASK 0x04 /* 1 = bus is busy */ +#define XIIC_SR_MSTR_RDING_SLAVE_MASK 0x08 /* 1=Dir: mstr <-- slave */ +#define XIIC_SR_TX_FIFO_FULL_MASK 0x10 /* 1 = Tx FIFO full */ +#define XIIC_SR_RX_FIFO_FULL_MASK 0x20 /* 1 = Rx FIFO full */ +#define XIIC_SR_RX_FIFO_EMPTY_MASK 0x40 /* 1 = Rx FIFO empty */ + +/* IPIF Interrupt Status Register masks Interrupt occurs when... */ + +#define XIIC_INTR_ARB_LOST_MASK 0x01 /* 1 = arbitration lost */ +#define XIIC_INTR_TX_ERROR_MASK 0x02 /* 1=Tx error/msg complete */ +#define XIIC_INTR_TX_EMPTY_MASK 0x04 /* 1 = Tx FIFO/reg empty */ +#define XIIC_INTR_RX_FULL_MASK 0x08 /* 1=Rx FIFO/reg=OCY level */ +#define XIIC_INTR_BNB_MASK 0x10 /* 1 = Bus not busy */ +#define XIIC_INTR_AAS_MASK 0x20 /* 1 = when addr as slave */ +#define XIIC_INTR_NAAS_MASK 0x40 /* 1 = not addr as slave */ +#define XIIC_INTR_TX_HALF_MASK 0x80 /* 1 = TX FIFO half empty */ + +/* IPIF Device Interrupt Register masks */ + +#define XIIC_IPIF_IIC_MASK 0x00000004UL /* 1=inter enabled */ +#define XIIC_IPIF_ERROR_MASK 0x00000001UL /* 1=inter enabled */ +#define XIIC_IPIF_INTER_ENABLE_MASK (XIIC_IPIF_IIC_MASK | \ + XIIC_IPIF_ERROR_MASK) + +#define XIIC_TX_ADDR_SENT 0x00 +#define XIIC_TX_ADDR_MSTR_RECV_MASK 0x02 + +/* The following constants specify the depth of the FIFOs */ + +#define IIC_RX_FIFO_DEPTH 16 /* Rx fifo capacity */ +#define IIC_TX_FIFO_DEPTH 16 /* Tx fifo capacity */ + +/* The following constants specify groups of interrupts that are typically + * enabled or disables at the same time + */ +#define XIIC_TX_INTERRUPTS \ + (XIIC_INTR_TX_ERROR_MASK | XIIC_INTR_TX_EMPTY_MASK | \ + XIIC_INTR_TX_HALF_MASK) + +#define XIIC_TX_RX_INTERRUPTS (XIIC_INTR_RX_FULL_MASK | XIIC_TX_INTERRUPTS) + +/* The following constants are used with the following macros to specify the + * operation, a read or write operation. + */ +#define XIIC_READ_OPERATION 1 +#define XIIC_WRITE_OPERATION 0 + +/* The following constants are used with the transmit FIFO fill function to + * specify the role which the IIC device is acting as, a master or a slave. + */ +#define XIIC_MASTER_ROLE 1 +#define XIIC_SLAVE_ROLE 0 + +/**************************** Type Definitions ******************************/ + +/***************** Macros (Inline Functions) Definitions ********************/ + +/************************** Function Prototypes *****************************/ + +unsigned XIic_Recv(u32 BaseAddress, u8 Address, + u8 * BufferPtr, unsigned ByteCount); + +unsigned XIic_Send(u32 BaseAddress, u8 Address, + u8 * BufferPtr, unsigned ByteCount); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/drivers/i2c/xilinx_iic/xiic_master.c linuxppc-2.4/drivers/i2c/xilinx_iic/xiic_master.c --- linux-2.4.22/drivers/i2c/xilinx_iic/xiic_master.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/xilinx_iic/xiic_master.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,714 @@ +/* $Id: xiic_master.c,v 1.4 2003/01/13 21:50:25 meinelte Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xiic_master.c +* +* Contains master functions for the XIic component. This file is necessary to +* send or receive as a master on the IIC bus. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- --- ------- -----------------------------------------------
+* 1.01b jhl 3/27/02 Reparitioned the driver
+* 1.01c ecm 12/05/02 new rev
+* 
+* +****************************************************************************/ + +/***************************** Include Files *******************************/ + +#include "xiic.h" +#include "xiic_i.h" +#include "xio.h" + +/************************** Constant Definitions ***************************/ + +/**************************** Type Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *******************/ + +/***************************************************************************** +* +* This macro includes master code such that master operations, sending +* and receiving data, may be used. This function hooks the master processing +* to the driver such that events are handled properly and allows master +* processing to be optional. It must be called before any functions which +* are contained in this file are called, such as after the driver is +* initialized. +* +* @note +* +* None +* +******************************************************************************/ +#define XIIC_MASTER_INCLUDE \ +{ \ + XIic_RecvMasterFuncPtr = RecvMasterData; \ + XIic_SendMasterFuncPtr = SendMasterData; \ +} + +/************************** Function Prototypes ****************************/ + +static void SendSlaveAddr(XIic * InstancePtr); +static void RecvMasterData(XIic * InstancePtr); +static void SendMasterData(XIic * InstancePtr); +static u32 IsBusBusy(XIic * InstancePtr); + +/************************** Variable Definitions **************************/ + +/****************************************************************************/ +/** +* This function sends data as a master on the IIC bus. If the bus is busy, it +* will indicate so and then enable an interrupt such that the status handler +* will be called when the bus is no longer busy. The slave address which has +* been set with the XIic_SetAddress() function is the address to which the +* specific data is sent. Sending data on the bus performs a write operation. +* +* @param InstancePtr points to the Iic instance to be worked on. +* @param TxMsgPtr points to the data to be transmitted +* @param ByteCount is the number of message bytes to be sent +* +* @return +* +* - XST_SUCCESS indicates the message transmission has been initiated. +* - XST_IIC_BUS_BUSY indicates the bus was in use and that the BusNotBusy +* interrupt is enabled which will update the EventStatus when the bus is no +* longer busy. +* +* @note +* +* None +* +******************************************************************************/ +XStatus +XIic_MasterSend(XIic * InstancePtr, u8 * TxMsgPtr, int ByteCount) +{ + u8 CntlReg; + + XIic_mEnterCriticalRegion(InstancePtr->BaseAddress); + + /* Ensure that the master processing has been included such that events + * will be properly handled + */ + XIIC_MASTER_INCLUDE; + + /* + * If the busy is busy, then exit the critical region and wait for the + * bus to not be busy, the function enables the bus not busy interrupt + */ + if (IsBusBusy(InstancePtr)) { + XIic_mExitCriticalRegion(InstancePtr->BaseAddress); + + return XST_IIC_BUS_BUSY; + } + + /* If it is already a master on the bus (repeated start), the direction was + * set to tx which is throttling bus. The control register needs to be set + * before putting data into the FIFO + */ + CntlReg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); + + if (CntlReg & XIIC_CR_MSMS_MASK) { + CntlReg &= ~XIIC_CR_NO_ACK_MASK; + CntlReg |= + (XIIC_CR_DIR_IS_TX_MASK | XIIC_CR_REPEATED_START_MASK); + + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, + CntlReg); + InstancePtr->Stats.RepeatedStarts++; + } + + /* Save message state + */ + InstancePtr->SendByteCount = ByteCount; + InstancePtr->SendBufferPtr = TxMsgPtr; + + /* Put the address into the FIFO to be sent and indicate that the operation + * to be performed on the bus is a write operation, a general call address + * handled the same as a 7 bit address even if 10 bit address is selected + * Set the transmit address state to indicate the address has been sent + */ + if ((InstancePtr->Options & XII_SEND_10_BIT_OPTION) && + (InstancePtr->AddrOfSlave != 0)) { + XIic_mSend10BitAddrByte1(InstancePtr->AddrOfSlave, + XIIC_WRITE_OPERATION); + XIic_mSend10BitAddrByte2(InstancePtr->AddrOfSlave); + } else { + XIic_mSend7BitAddr(InstancePtr->AddrOfSlave, + XIIC_WRITE_OPERATION); + } + /* Set the transmit address state to indicate the address has been sent + * for communication with event driven processing + */ + InstancePtr->TxAddrMode = XIIC_TX_ADDR_SENT; + + /* Fill remaining available FIFO with message data + */ + if (InstancePtr->SendByteCount > 1) { + XIic_TransmitFifoFill(InstancePtr, XIIC_MASTER_ROLE); + } + + /* After filling fifo, if data yet to send > 1, enable Tx ½ empty interrupt + */ + if (InstancePtr->SendByteCount > 1) { + XIic_mClearEnableIntr(InstancePtr->BaseAddress, + XIIC_INTR_TX_HALF_MASK); + } + + /* Clear any pending Tx empty, Tx Error and then enable them. + */ + XIic_mClearEnableIntr(InstancePtr->BaseAddress, + XIIC_INTR_TX_ERROR_MASK | + XIIC_INTR_TX_EMPTY_MASK); + + /* When repeated start not used, MSMS must be set after putting data into + * transmit FIFO, start the transmitter + */ + + CntlReg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); + if ((CntlReg & XIIC_CR_MSMS_MASK) == 0) { + CntlReg &= ~XIIC_CR_NO_ACK_MASK; + CntlReg |= XIIC_CR_MSMS_MASK | XIIC_CR_DIR_IS_TX_MASK; + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, + CntlReg); + } + + XIic_mExitCriticalRegion(InstancePtr->BaseAddress); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* This function receives data as a master from a slave device on the IIC bus. +* If the bus is busy, it will indicate so and then enable an interrupt such +* that the status handler will be called when the bus is no longer busy. The +* slave address which has been set with the XIic_SetAddress() function is the +* address from which data is received. Receiving data on the bus performs a +* read operation. +* +* @param InstancePtr is a pointer to the Iic instance to be worked on. +* @param RxMsgPtr is a pointer to the data to be transmitted +* @param ByteCount is the number of message bytes to be sent +* +* @return +* +* - XST_SUCCESS indicates the message reception processes has been initiated. +* - XST_IIC_BUS_BUSY indicates the bus was in use and that the BusNotBusy +* interrupt is enabled which will update the EventStatus when the bus is no +* longer busy. +* - XST_IIC_GENERAL_CALL_ADDRESS indicates the slave address is set to the +* the general call address. This is not allowed for Master receive mode. +* +* @internal +* +* The receive FIFO threshold is a zero based count such that 1 must be +* subtracted from the desired count to get the correct value. When receiving +* data it is also necessary to not receive the last byte with the prior bytes +* because the acknowledge must be setup before the last byte is received. +* +******************************************************************************/ +XStatus +XIic_MasterRecv(XIic * InstancePtr, u8 * RxMsgPtr, int ByteCount) +{ + u8 CntlReg; + u8 Temp; + + /* If the slave address is zero (general call) the master can't perform + * receive operations, indicate an error + */ + if (InstancePtr->AddrOfSlave == 0) { + return XST_IIC_GENERAL_CALL_ADDRESS; + } + + XIic_mEnterCriticalRegion(InstancePtr->BaseAddress); + + /* Ensure that the master processing has been included such that events + * will be properly handled + */ + XIIC_MASTER_INCLUDE; + /* + * If the busy is busy, then exit the critical region and wait for the + * bus to not be busy, the function enables the bus not busy interrupt + */ + if (IsBusBusy(InstancePtr)) { + XIic_mExitCriticalRegion(InstancePtr->BaseAddress); + + return XST_IIC_BUS_BUSY; + } + + /* Save message state for event driven processing + */ + InstancePtr->RecvByteCount = ByteCount; + InstancePtr->RecvBufferPtr = RxMsgPtr; + + /* Clear and enable Rx full interrupt if using 7 bit, If 10 bit, wait until + * last address byte sent incase arbitration gets lost while sending out + * address. + */ + if ((InstancePtr->Options & XII_SEND_10_BIT_OPTION) == 0) { + XIic_mClearEnableIntr(InstancePtr->BaseAddress, + XIIC_INTR_RX_FULL_MASK); + } + + /* If already a master on the bus, the direction was set by Rx Interrupt + * routine to tx which is throttling bus because during Rxing, Tx reg is + * empty = throttle. CR needs setting before putting data or the address + * written will go out as Tx instead of receive. Start Master Rx by setting + * CR Bits MSMS to Master and msg direction. + */ + CntlReg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); + + if (CntlReg & XIIC_CR_MSMS_MASK) { + CntlReg |= XIIC_CR_REPEATED_START_MASK; + XIic_mSetControlRegister(InstancePtr, CntlReg, ByteCount); + + InstancePtr->Stats.RepeatedStarts++; /* increment stats counts */ + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, + CntlReg); + + } + + /* Set receive FIFO occupancy depth which must be done prior to writing the + * address in the FIFO because the transmitter will immediatedly start when + * in repeated start mode followed by the receiver such that the number of + * bytes to receive should be set 1st. + */ + if (ByteCount == 1) { + Temp = 0; + } else { + if (ByteCount < IIC_RX_FIFO_DEPTH) { + Temp = ByteCount - 2; + } else { + Temp = IIC_RX_FIFO_DEPTH - 1; + } + } + XIo_Out8(InstancePtr->BaseAddress + XIIC_RFD_REG_OFFSET, Temp); + + if (InstancePtr->Options & XII_SEND_10_BIT_OPTION) { + /* Send the 1st and 2nd byte of the 10 bit address of a write + * operation, write because it's a 10 bit address + */ + XIic_mSend10BitAddrByte1(InstancePtr->AddrOfSlave, + XIIC_WRITE_OPERATION); + XIic_mSend10BitAddrByte2(InstancePtr->AddrOfSlave); + + /* Set flag to indicate the next byte of the address needs to be + * send, clear and enable tx empty interrupt + */ + InstancePtr->TxAddrMode = XIIC_TX_ADDR_MSTR_RECV_MASK; + XIic_mClearEnableIntr(InstancePtr->BaseAddress, + XIIC_INTR_TX_EMPTY_MASK); + } else { + /* 7 bit slave address, send the address for a read operation + * and set the state to indicate the address has been sent + */ + XIic_mSend7BitAddr(InstancePtr->AddrOfSlave, + XIIC_READ_OPERATION); + InstancePtr->TxAddrMode = XIIC_TX_ADDR_SENT; + } + + /* Tx error is enabled incase the address (7 or 10) has no device to answer + * with Ack. When only one byte of data, must set NO ACK before address goes + * out therefore Tx error must not be enabled as it will go off immediately + * and the Rx full interrupt will be checked. If full, then the one byte + * was received and the Tx error will be disabled without sending an error + * callback msg. + */ + XIic_mClearEnableIntr(InstancePtr->BaseAddress, + XIIC_INTR_TX_ERROR_MASK); + + /* When repeated start not used, MSMS gets set after putting data + * in Tx reg. Start Master Rx by setting CR Bits MSMS to Master and + * msg direction. + */ + CntlReg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); + if ((CntlReg & XIIC_CR_MSMS_MASK) == 0) { + CntlReg |= XIIC_CR_MSMS_MASK; + XIic_mSetControlRegister(InstancePtr, CntlReg, ByteCount); + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, + CntlReg); + } + + XIic_mExitCriticalRegion(InstancePtr->BaseAddress); + + return XST_SUCCESS; +} + +/***************************************************************************** +* +* This function checks to see if the IIC bus is busy. If so, it will enable +* the bus not busy interrupt such that the driver is notified when the bus +* is no longer busy. +* +* @param InstancePtr points to the Iic instance to be worked on. +* +* @return +* +* - FALSE indicates the IIC bus is not busy. +* - TRUE indicates the bus was in use and that the BusNotBusy +* interrupt is enabled which will update the EventStatus when the bus is no +* longer busy. +* +* @note +* +* None +* +******************************************************************************/ +static u32 +IsBusBusy(XIic * InstancePtr) +{ + u8 ControlReg; + u8 StatusReg; + + ControlReg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); + StatusReg = XIo_In8(InstancePtr->BaseAddress + XIIC_SR_REG_OFFSET); + + /* If this device is already master of the bus as when using the repeated + * start and the bus is busy setup to wait for it to not be busy + */ + if (((ControlReg & XIIC_CR_MSMS_MASK) == 0) && /* not master */ + (StatusReg & XIIC_SR_BUS_BUSY_MASK)) { /* is busy */ + /* The bus is busy, clear pending BNB interrupt incase previously set + * and then enable BusNotBusy interrupt + */ + InstancePtr->BNBOnly = TRUE; + XIic_mClearEnableIntr(InstancePtr->BaseAddress, + XIIC_INTR_BNB_MASK); + InstancePtr->Stats.BusBusy++; + + return TRUE; + } + + return FALSE; +} + +/****************************************************************************** +* +* This function sends the proper byte of the address as well as generate the +* proper address bit fields depending on the address byte required and the +* direction of the data (write or read). +* +* A master receiving has the restriction that the direction must be switched +* from write to read when the third address byte is transmitted. +* For the last byte of the 10 bit address, repeated start must be set prior +* to writing the address. If repeated start options is enabled, the +* control register is written before the address is written to the tx reg. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* This function does read/modify/write to the device control register. Calling +* functions must ensure critical sections are used. +* +******************************************************************************/ +static void +SendSlaveAddr(XIic * InstancePtr) +{ + u8 CRreg; + + /* Set the control register for Master Receive, repeated start must be set + * before writing the address, MSMS should be already set, don't set here + * so if arbitration is lost or some other reason we don't want MSMS set + * incase of error + */ + CRreg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); + + CRreg |= XIIC_CR_REPEATED_START_MASK; + CRreg &= ~XIIC_CR_DIR_IS_TX_MASK; + + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, CRreg); + + /* Send the 1st byte of the 10 bit address as a read operation, enable the + * receive interrupt to know when data is received, assuming that the + * receive FIFO threshold has been previously set + */ + XIic_mSend10BitAddrByte1(InstancePtr->AddrOfSlave, XIIC_READ_OPERATION); + + XIic_mClearEnableIntr(InstancePtr->BaseAddress, XIIC_INTR_RX_FULL_MASK); +} + +/****************************************************************************** +* +* When the IIC Tx FIFO/register goes empty, this routine is called by the +* interrupt service routine to fill the transmit FIFO with data to be sent. +* +* This function also is called by the Tx ½ empty interrupt as the data handling +* is identical when you don't assume the FIFO is empty but use the Tx_FIFO_OCY +* register to indicate the available free FIFO bytes. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +SendMasterData(XIic * InstancePtr) +{ + u8 CntlReg; + + /* The device is a master on the bus. If there is still more address bytes + * to send when in master receive operation and the slave device is 10 bit + * addressed. This requires the lower 7 bits of address to be resent when + * the mode switches to Read instead of write (while sending addresses). + */ + if (InstancePtr->TxAddrMode & XIIC_TX_ADDR_MSTR_RECV_MASK) { + /* Send the 1st byte of the slave address in the read operation + * and change the state to indicate this has been done + */ + SendSlaveAddr(InstancePtr); + InstancePtr->TxAddrMode = XIIC_TX_ADDR_SENT; + } + + /* In between 1st and last byte of message, fill the FIFO with more data + * to send, disable the 1/2 empty interrupt based upon data left to send + */ + else if (InstancePtr->SendByteCount > 1) { + XIic_TransmitFifoFill(InstancePtr, XIIC_MASTER_ROLE); + + if (InstancePtr->SendByteCount < 2) { + XIic_mDisableIntr(InstancePtr->BaseAddress, + XIIC_INTR_TX_HALF_MASK); + } + } + /* + * If there is only one byte left to send, processing differs between + * repeated start and normal messages + */ + else if (InstancePtr->SendByteCount == 1) { + /* When using repeated start, another interrupt is expected after the + * last byte has been sent, so the message is not done yet + */ + if (InstancePtr->Options & XII_REPEATED_START_OPTION) { + XIic_mWriteSendByte(InstancePtr); + } + + /* When not using repeated start, the stop condition must be generated + * after the last byte is written. The bus is throttled waiting for the last + * byte. + */ + else { + /* Set the stop condition before sending the last byte of data so that + * the stop condition will be generated immediately following the data + * another transmit interrupt is not expected so the message is done + */ + + CntlReg = + XIo_In8(InstancePtr->BaseAddress + + XIIC_CR_REG_OFFSET); + CntlReg &= ~XIIC_CR_MSMS_MASK; + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, + CntlReg); + + XIic_mWriteSendByte(InstancePtr); + + /* Wait for bus to not be busy before declaring message has + * been sent for the no repeated start operation. The callback + * will be called from the BusNotBusy part of the Interrupt + * handler to ensure that the message is completely sent. + * Disable the TX interrupts and enable the BNB interrupt + */ + + InstancePtr->BNBOnly = FALSE; + XIic_mDisableIntr(InstancePtr->BaseAddress, + XIIC_TX_INTERRUPTS); + XIic_mEnableIntr(InstancePtr->BaseAddress, + XIIC_INTR_BNB_MASK); + + } + } else { + if (InstancePtr->Options & XII_REPEATED_START_OPTION) { + /* The message being sent has completed. When using repeated start + * with no more bytes to send repeated start needs to be set in + * the control register so that the bus will still be held by this + * master + */ + + CntlReg = + XIo_In8(InstancePtr->BaseAddress + + XIIC_CR_REG_OFFSET); + CntlReg |= XIIC_CR_REPEATED_START_MASK; + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, + CntlReg); + + /* If the message that was being sent has finished, disable all + *transmit interrupts and call the callback that was setup to + * indicate the message was sent, with 0 bytes remaining + */ + + XIic_mDisableIntr(InstancePtr->BaseAddress, + XIIC_TX_INTERRUPTS); + InstancePtr->SendHandler(InstancePtr->SendCallBackRef, + 0); + } + } + + return; +} + +/*****************************************************************************/ +/** +* +* This function is called when the receive register is full. The number +* of bytes received to cause the interrupt is adjustable using the Receive FIFO +* Depth register. The number of bytes in the register is read in the Receive +* FIFO occupancy register. Both these registers are zero based values (0-15) +* such that a value of zero indicates 1 byte. +* +* For a Master Receiver to properly signal the end of a message, the data must +* be read in up to the message length - 1, where control register bits will be +* set for bus controls to occur on reading of the last byte. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +RecvMasterData(XIic * InstancePtr) +{ + u8 LoopCnt; + int BytesInFifo; + int BytesToRead; + u8 CntlReg; + + /* Device is a master receiving, get the contents of the control register + * and determine the number of bytes in fifo to be read out + */ + CntlReg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); + BytesInFifo = XIo_In8(InstancePtr->BaseAddress + XIIC_RFO_REG_OFFSET) + + 1; + + /* If data in FIFO holds all data to be retrieved - 1, set NOACK and + * disable the tx error + */ + if ((InstancePtr->RecvByteCount - BytesInFifo) == 1) { + /* Disable tx error interrupt to prevent interrupt + * as this device will cause it when it set NO ACK next + */ + XIic_mDisableIntr(InstancePtr->BaseAddress, + XIIC_INTR_TX_ERROR_MASK); + XIic_mClearIntr(InstancePtr->BaseAddress, + XIIC_INTR_TX_ERROR_MASK); + + /* Write control reg with NO ACK allowing last byte to + * have the No ack set to indicate to slave last byte read. + */ + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, + (CntlReg | XIIC_CR_NO_ACK_MASK)); + + /* Read one byte to clear a place for the last byte to be read + * which will set the NO ACK + */ + XIic_mReadRecvByte(InstancePtr); + } + + /* If data in FIFO is all the data to be received then get the data + * and also leave the device in a good state for the next transaction + */ + else if ((InstancePtr->RecvByteCount - BytesInFifo) == 0) { + /* If repeated start option is off then the master should stop + * using the bus, otherwise hold the bus, setting repeated start + * stops the slave from transmitting data when the FIFO is read + */ + if ((InstancePtr->Options & XII_REPEATED_START_OPTION) == 0) { + CntlReg &= ~XIIC_CR_MSMS_MASK; + } else { + CntlReg |= XIIC_CR_REPEATED_START_MASK; + } + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, + CntlReg); + + /* Read data from the FIFO then set zero based FIFO read depth for a byte + */ + for (LoopCnt = 0; LoopCnt < BytesInFifo; LoopCnt++) { + XIic_mReadRecvByte(InstancePtr); + } + XIo_Out8(InstancePtr->BaseAddress + XIIC_RFD_REG_OFFSET, 0); + + /* Disable Rx full interrupt and write the control reg with ACK allowing + * next byte sent to be acknowledged automatically + */ + XIic_mDisableIntr(InstancePtr->BaseAddress, + XIIC_INTR_RX_FULL_MASK); + + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, + (CntlReg & ~XIIC_CR_NO_ACK_MASK)); + + /* Send notification of msg Rx complete in RecvHandler callback + */ + InstancePtr->RecvHandler(InstancePtr->RecvCallBackRef, 0); + } else { + /* Fifo data not at n-1, read all but the last byte of data from the + * slave, if more than a FIFO full yet to receive read a FIFO full + */ + BytesToRead = InstancePtr->RecvByteCount - BytesInFifo - 1; + if (BytesToRead > IIC_RX_FIFO_DEPTH) { + BytesToRead = IIC_RX_FIFO_DEPTH; + } + + /* Read in data from the FIFO */ + + for (LoopCnt = 0; LoopCnt < BytesToRead; LoopCnt++) { + XIic_mReadRecvByte(InstancePtr); + } + } +} diff -urN -X bkexcl linux-2.4.22/drivers/i2c/xilinx_iic/xiic_options.c linuxppc-2.4/drivers/i2c/xilinx_iic/xiic_options.c --- linux-2.4.22/drivers/i2c/xilinx_iic/xiic_options.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/i2c/xilinx_iic/xiic_options.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,177 @@ +/* $Id: xiic_options.c,v 1.2 2002/12/05 19:32:40 meinelte Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xiic_options.c +* +* Contains options functions for the XIic component. This file is not required +* unless the functions in this file are called. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- --- ------- -----------------------------------------------
+* 1.01b jhl 3/26/02 repartioned the driver
+* 1.01c ecm 12/05/02 new rev
+* 
+* +****************************************************************************/ + +/***************************** Include Files *******************************/ + +#include "xiic.h" +#include "xiic_i.h" +#include "xio.h" + +/************************** Constant Definitions ***************************/ + +/**************************** Type Definitions *****************************/ + +/***************** Macros (Inline Functions) Definitions *******************/ + +/************************** Function Prototypes ****************************/ + +/************************** Variable Definitions **************************/ + +/*****************************************************************************/ +/** +* +* This function sets the options for the IIC device driver. The options control +* how the device behaves relative to the IIC bus. If an option applies to +* how messages are sent or received on the IIC bus, it must be set prior to +* calling functions which send or receive data. +* +* To set multiple options, the values must be ORed together. To not change +* existing options, read/modify/write with the current options using +* XIic_GetOptions(). +* +* USAGE EXAMPLE: +* +* Read/modify/write to enable repeated start: +*
+*   u8 Options;
+*   Options = XIic_GetOptions(&Iic);
+*   XIic_SetOptions(&Iic, Options | XII_REPEATED_START_OPTION);
+* 
+* +* Disabling General Call: +*
+*   Options = XIic_GetOptions(&Iic);
+*   XIic_SetOptions(&Iic, Options &= ~XII_GENERAL_CALL_OPTION);
+* 
+* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* +* @param NewOptions are the options to be set. See xiic.h for a list of +* the available options. +* +* @return +* +* None. +* +* @note +* +* Sending or receiving messages with repeated start enabled, and then +* disabling repeated start, will not take effect until another master +* transaction is completed. i.e. After using repeated start, the bus will +* continue to be throttled after repeated start is disabled until a master +* transaction occurs allowing the IIC to release the bus. +*

+* Options enabled will have a 1 in its appropriate bit position. +* +****************************************************************************/ +void +XIic_SetOptions(XIic * InstancePtr, u32 NewOptions) +{ + u8 CntlReg; + + XASSERT_VOID(InstancePtr != NULL); + + XIic_mEnterCriticalRegion(InstancePtr->BaseAddress); + + /* Update the options in the instance and get the contents of the control + * register such that the general call option can be modified + */ + InstancePtr->Options = NewOptions; + CntlReg = XIo_In8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET); + + /* The general call option is the only option that maps directly to + * a hardware register feature + */ + if (NewOptions & XII_GENERAL_CALL_OPTION) { + CntlReg |= XIIC_CR_GENERAL_CALL_MASK; + } else { + CntlReg &= ~XIIC_CR_GENERAL_CALL_MASK; + } + + /* Write the new control register value to the register */ + + XIo_Out8(InstancePtr->BaseAddress + XIIC_CR_REG_OFFSET, CntlReg); + + XIic_mExitCriticalRegion(InstancePtr->BaseAddress); +} + +/*****************************************************************************/ +/** +* +* This function gets the current options for the IIC device. Options control +* the how the device behaves on the IIC bus. See SetOptions for more information +* on options. +* +* @param InstancePtr is a pointer to the XIic instance to be worked on. +* +* @return +* +* The options of the IIC device. See xiic.h for a list of available options. +* +* @note +* +* Options enabled will have a 1 in its appropriate bit position. +* +****************************************************************************/ +u32 +XIic_GetOptions(XIic * InstancePtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + + return InstancePtr->Options; +} diff -urN -X bkexcl linux-2.4.22/drivers/ide/Config.in linuxppc-2.4/drivers/ide/Config.in --- linux-2.4.22/drivers/ide/Config.in 2003-08-24 09:04:15.000000000 +1000 +++ linuxppc-2.4/drivers/ide/Config.in 2003-08-26 11:57:12.000000000 +1000 @@ -96,6 +96,16 @@ if [ "$CONFIG_SIBYTE_GENBUS_IDE" = "y" ]; then bool ' Broadcom SiByte onboard IDE support' CONFIG_BLK_DEV_IDE_SIBYTE fi + + if [ "$CONFIG_REDWOOD_4" = "y" -o "$CONFIG_REDWOOD_6" = "y" ] ; then + bool ' IBM STB03xxx/STBx25xx IDE support ' CONFIG_BLK_DEV_REDWOOD_IDE + define_bool CONFIG_BLK_DEV_IDEDMA y + fi + if [ "$CONFIG_IBM_OCP" = "y" ]; then + bool ' IBM on-chip IDE' CONFIG_IBM_OCP_IDE + define_bool CONFIG_BLK_DEV_IDEDMA y + fi + if [ "$CONFIG_ARCH_ACORN" = "y" ]; then dep_bool ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE $CONFIG_ARCH_ACORN dep_bool ' ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS $CONFIG_BLK_DEV_IDE_ICSIDE @@ -116,6 +126,9 @@ if [ "$CONFIG_MAC" = "y" ]; then dep_bool ' Macintosh Quadra/Powerbook IDE interface support' CONFIG_BLK_DEV_MAC_IDE $CONFIG_MAC fi + if [ "$CONFIG_CPCI405" = "y" ]; then + dep_bool ' CPCI-405 IDE interface support' CONFIG_BLK_DEV_CPCI405_IDE $CONFIG_CPCI405 + fi if [ "$CONFIG_Q40" = "y" ]; then dep_bool ' Q40/Q60 IDE interface support' CONFIG_BLK_DEV_Q40IDE $CONFIG_Q40 fi @@ -162,7 +175,9 @@ bool ' IGNORE word93 Validation BITS' CONFIG_IDEDMA_IVB fi -if [ "$CONFIG_BLK_DEV_TIVO" = "y" ]; then +if [ "$CONFIG_BLK_DEV_TIVO" = "y" -o \ + "$CONFIG_BLK_DEV_REDWOOD_IDE" = "y" -o \ + "$CONFIG_IBM_OCP_IDE" = "y" ]; then define_bool CONFIG_DMA_NONPCI y else define_bool CONFIG_DMA_NONPCI n @@ -210,6 +225,7 @@ "$CONFIG_BLK_DEV_SIS5513" = "y" -o \ "$CONFIG_BLK_DEV_SL82C105" = "y" -o \ "$CONFIG_BLK_DEV_SLC90E66" = "y" -o \ + "$CONFIG_STB03xxx" = "y" -o \ "$CONFIG_BLK_DEV_VIA82CXXX" = "y" ]; then define_bool CONFIG_BLK_DEV_IDE_MODES y else diff -urN -X bkexcl linux-2.4.22/drivers/ide/Makefile linuxppc-2.4/drivers/ide/Makefile --- linux-2.4.22/drivers/ide/Makefile 2003-08-24 09:04:15.000000000 +1000 +++ linuxppc-2.4/drivers/ide/Makefile 2003-08-26 11:57:12.000000000 +1000 @@ -18,6 +18,9 @@ obj-m := ide-obj-y := +obj-$(CONFIG_IBM_OCP_IDE) += ibm_ocp_ide.o +obj-$(CONFIG_BLK_DEV_REDWOOD_IDE) += ocp_stbxxxxx.o + subdir-$(CONFIG_BLK_DEV_IDE) += legacy ppc arm raid pci # First come modules that register themselves with the core diff -urN -X bkexcl linux-2.4.22/drivers/ide/ibm_ocp_ide.c linuxppc-2.4/drivers/ide/ibm_ocp_ide.c --- linux-2.4.22/drivers/ide/ibm_ocp_ide.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/ide/ibm_ocp_ide.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,713 @@ +/* + * IDE driver for IBM On-chip IDE contollers + * Copyright 2001 - 2002 MontaVista Software Inc. + * Dan Malek. + * + * I snagged bits and pieces from a variety of drives, primarily + * ide-pmac.c.....thanks to previous authors! + * + * Version 1.2 (01/30/12) Armin + * Converted to ocp + * merger up to new ide-timing.h + * + * Version 2.0 (05/02/15) - armin + * converted to new core_ocp and only supports one interface for now. + * + * Version 2.1 (05/25/02) - armin + * name change from *_driver to *_dev + * Version 2.2 06/13/02 - Armin + * changed irq_resource array to just irq + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ide-timing.h" +#define OCPVR "2.3" + +/* The structure of the PRD entry. The address must be word aligned, + * and the count must be an even number of bytes. + */ +typedef struct { + unsigned int prd_physptr; + unsigned int prd_count; /* Count only in lower 16 bits */ +} prd_entry_t; +#define PRD_EOT (uint)0x80000000 /* Set in prd_count */ + +/* The number of PRDs required in a single transfer from the upper IDE + * functions. I believe the maximum number is 128, but most seem to + * code to 256. It's probably best to keep this under one page...... + */ +#define NUM_PRD 256 + +#define MK_TIMING(AS, DIOP, DIOY, DH) \ + ((FIT((AS), 0, 15) << 27) | \ + (FIT((DIOP), 0, 63) << 20) | \ + (FIT((DIOY), 0, 63) << 13) | \ + (FIT((DH), 0, 7) << 9)) + +#define UTIMING_SETHLD (EZ(20 /*tACK*/, SYS_CLOCK_NS) - 1 /*fixed cycles*/) +#define UTIMING_ENV (EZ(20 /*tENV*/, SYS_CLOCK_NS) - 1 /*fixed cycles*/) +#define UTIMING_SS (EZ(50 /*tSS */, SYS_CLOCK_NS) - 3 /*fixed cycles*/) +#define MK_UTIMING(CYC, RP) \ + ((FIT(UTIMING_SETHLD, 0, 15) << 27) | \ + (FIT(UTIMING_ENV, 0, 15) << 22) | \ + (FIT((CYC), 0, 15) << 17) | \ + (FIT((RP), 0, 63) << 10) | \ + (FIT(UTIMING_SS, 0, 15) << 5) | \ + 1 /* Turn on Ultra DMA */) + +/* Define the period of the STB clock used to generate the + * IDE bus timing. The clock is actually 63 MHz, but it + * get rounded in a favorable direction. + */ +#define IDE_SYS_FREQ 63 /* MHz */ +#define SYS_CLOCK_NS (1000 / IDE_SYS_FREQ) + +struct whold_timing { + short mode; + short whold; +}; + +static struct whold_timing whold_timing[] = { + + {XFER_UDMA_5, 0}, + {XFER_UDMA_4, 0}, + {XFER_UDMA_3, 0}, + + {XFER_UDMA_2, 0}, + {XFER_UDMA_1, 0}, + {XFER_UDMA_0, 0}, + + {XFER_UDMA_SLOW, 0}, + + {XFER_MW_DMA_2, 0}, + {XFER_MW_DMA_1, 0}, + {XFER_MW_DMA_0, 0}, + + {XFER_SW_DMA_2, 0}, + {XFER_SW_DMA_1, 0}, + {XFER_SW_DMA_0, 10}, + + {XFER_PIO_5, 10}, + {XFER_PIO_4, 10}, + {XFER_PIO_3, 15}, + + {XFER_PIO_2, 20}, + {XFER_PIO_1, 30}, + {XFER_PIO_0, 50}, + + {XFER_PIO_SLOW,}, + + {-1} +}; + +/* The interface doesn't have register/PIO timing for each device, + * but rather "fast" and "slow" timing. We have to determeine + * which is the "fast" device based upon their capability. + */ +static int pio_mode[2]; + +/* Pointer to the IDE controller registers. +*/ +static volatile ide_t *idp; + +/* Virtual and physical address of the PRD page. +*/ +static prd_entry_t *prd_table; +static dma_addr_t prd_phys; + +int +nonpci_ide_default_irq(ide_ioreg_t base) +{ + return IDE0_IRQ; +} + +/* this iis barrowed from ide_timing_find_mode so we can find the proper + * whold parameter + */ + +static short +whold_timing_find_mode(short speed) +{ + struct whold_timing *t; + + for (t = whold_timing; t->mode != speed; t++) + if (t->mode < 0) + return 0; + return t->whold; +} + +/* The STB04 has a fixed number of cycles that get added in + * regardless. Adjust an ide_timing struct to accommodate that. + */ +static + void +stb04xxx_ide_adjust_timing(struct ide_timing *t) +{ + t->setup -= 2; + t->act8b -= 1; + t->rec8b -= 1; + t->active -= 1; + t->recover -= 1; +} + +static int +stb04xxx_ide_set_drive(ide_drive_t * drive, unsigned char speed) +{ + ide_drive_t *peer; + struct ide_timing d, p, merge, *fast; + int fast_device; + unsigned int ctl; + volatile unsigned int *dtiming; + + if (speed != XFER_PIO_SLOW && speed != drive->current_speed) + if (ide_config_drive_speed(drive, speed)) + printk(KERN_WARNING + "ide%d: Drive %d didn't accept speed setting. Oh, well.\n", + drive->dn >> 1, drive->dn & 1); + + ide_timing_compute(drive, speed, &d, SYS_CLOCK_NS, SYS_CLOCK_NS); + stb04xxx_ide_adjust_timing(&d); + + /* This should be set somewhere else, but it isn't..... + */ + drive->dn = ((drive->select.all & 0x10) != 0); + peer = HWIF(drive)->drives + (~drive->dn & 1); + + if (peer->present) { + ide_timing_compute(peer, peer->current_speed, &p, + SYS_CLOCK_NS, SYS_CLOCK_NS); + stb04xxx_ide_adjust_timing(&p); + ide_timing_merge(&p, &d, &merge, + IDE_TIMING_8BIT | IDE_TIMING_SETUP); + } else { + merge = d; + } + + if (!drive->init_speed) + drive->init_speed = speed; + drive->current_speed = speed; + + /* Now determine which drive is faster, and set up the + * interface timing. It would sure be nice if they would + * have just had the timing registers for each device...... + */ + if (drive->dn & 1) + pio_mode[1] = (int) speed; + else + pio_mode[0] = (int) speed; + + if (pio_mode[0] > pio_mode[1]) + fast_device = 0; + else + fast_device = 1; + + /* Now determine which of the drives + * the first call we only know one device, and on subsequent + * calls the user may manually change drive parameters. + * Make timing[0] the fast device and timing[1] the slow. + */ + if (fast_device == (drive->dn & 1)) + fast = &d; + else + fast = &p; + + /* Now we know which device is the fast one and which is + * the slow one. The merged timing goes into the "regular" + * timing registers and represents the slower of both times. + */ + + idp->si_c0rt = MK_TIMING(merge.setup, merge.act8b, + merge.rec8b, + whold_timing_find_mode(merge.mode)); + + idp->si_c0fpt = MK_TIMING(fast->setup, fast->act8b, + fast->rec8b, + whold_timing_find_mode(fast->mode)); + + /* Tell the interface which drive is the fast one. + */ + ctl = idp->si_c0c; /* Chan 0 Control */ + ctl &= ~0x10000000; + ctl |= fast_device << 28; + idp->si_c0c = ctl; + + /* Set up DMA timing. + */ + if ((speed & XFER_MODE) != XFER_PIO) { + /* NOTE: si_c0d0m and si_c0d0u are two different names + * for the same register. Whether it is used for + * Multi-word DMA timings or Ultra DMA timings is + * determined by the LSB written into it. This is also + * true for si_c0d1m and si_c0d1u. */ + if (drive->dn & 1) + dtiming = &(idp->si_c0d1m); + else + dtiming = &(idp->si_c0d0m); + + if ((speed & XFER_MODE) == XFER_UDMA) { + static const int tRP[] = { + EZ(160, SYS_CLOCK_NS) - 2 /*fixed cycles */ , + EZ(125, SYS_CLOCK_NS) - 2 /*fixed cycles */ , + EZ(100, SYS_CLOCK_NS) - 2 /*fixed cycles */ , + EZ(100, SYS_CLOCK_NS) - 2 /*fixed cycles */ , + EZ(100, SYS_CLOCK_NS) - 2 /*fixed cycles */ , + EZ(85, SYS_CLOCK_NS) - 2 /*fixed cycles */ + }; + static const int NUMtRP = + (sizeof (tRP) / sizeof (tRP[0])); + *dtiming = + MK_UTIMING(d.udma, + tRP[FIT(speed & 0xf, 0, NUMtRP - 1)]); + } else { + /* Multi-word DMA. Note that d.recover/2 is an + * approximation of MAX(tH, MAX(tJ, tN)) */ + *dtiming = MK_TIMING(d.setup, d.active, + d.recover, d.recover / 2); + } + drive->using_dma = 1; + } + + return 0; +} + +static void +stb04xxx_ide_tuneproc(ide_drive_t * drive, unsigned char pio) +{ + + if (pio == 255) + pio = ide_find_best_mode(drive, XFER_PIO | XFER_EPIO); + else + pio = XFER_PIO_0 + MIN(pio, 5); + + stb04xxx_ide_set_drive(drive, pio); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA + +/* DMA stuff mostly stolen from PMac....thanks Ben :-)! +*/ + +static int +stb04xxx_ide_build_dmatable(ide_drive_t * drive, int wr) +{ + prd_entry_t *table; + int count = 0; + struct request *rq = HWGROUP(drive)->rq; + struct buffer_head *bh = rq->bh; + unsigned int size, addr; + + table = prd_table; + + do { + /* + * Determine addr and size of next buffer area. We assume that + * individual virtual buffers are always composed linearly in + * physical memory. For example, we assume that any 8kB buffer + * is always composed of two adjacent physical 4kB pages rather + * than two possibly non-adjacent physical 4kB pages. + * We also have to ensure cache coherency here. If writing, + * flush the data cache to memory. Logically, if reading + * we should do it after the DMA is complete, but it is + * more convenient to do it here. If someone is messing + * with a buffer space after it is handed to us, they + * shouldn't be surprised by corrupted data, anyway :-). + */ + if (bh == NULL) { /* paging requests have (rq->bh == NULL) */ + addr = virt_to_bus(rq->buffer); + size = rq->nr_sectors << 9; + if (wr) + consistent_sync(rq->buffer, size, + PCI_DMA_TODEVICE); + else + consistent_sync(rq->buffer, size, + PCI_DMA_FROMDEVICE); + } else { + /* group sequential buffers into one large buffer */ + addr = virt_to_bus(bh->b_data); + size = bh->b_size; + if (wr) + consistent_sync(bh->b_data, size, + PCI_DMA_TODEVICE); + else + consistent_sync(bh->b_data, size, + PCI_DMA_FROMDEVICE); + while ((bh = bh->b_reqnext) != NULL) { + if ((addr + size) != virt_to_bus(bh->b_data)) + break; + size += bh->b_size; + if (wr) + consistent_sync(bh->b_data, + bh->b_size, + PCI_DMA_TODEVICE); + else + consistent_sync(bh->b_data, + bh->b_size, + PCI_DMA_FROMDEVICE); + } + } + + /* + * Fill in the next PRD entry. + * Note that one PRD entry can transfer + * at most 65535 bytes. + */ + while (size) { + unsigned int tc = (size < 0xfe00) ? size : 0xfe00; + + if (++count >= NUM_PRD) { + printk(KERN_WARNING "%s: DMA table too small\n", + drive->name); + return 0; /* revert to PIO for this request */ + } + table->prd_physptr = (addr & 0xfffffffe); + if (table->prd_physptr & 0xF) { + printk(KERN_WARNING + "%s: DMA buffer not 16 byte aligned.\n", + drive->name); + return 0; /* revert to PIO for this request */ + } + table->prd_count = (tc & 0xfffe); + addr += tc; + size -= tc; + ++table; + } + } while (bh != NULL); + + /* Add the EOT to the last table entry. + */ + if (count) { + table--; + table->prd_count |= PRD_EOT; + } else { + printk(KERN_DEBUG "%s: empty DMA table?\n", drive->name); + } + + return 1; +} + +/* + * dma_intr() is the handler for disk read/write DMA interrupts + * This is taken directly from ide-dma.c, which we can't use because + * it requires PCI support. + */ +ide_startstop_t +ide_dma_intr(ide_drive_t * drive) +{ + int i; + byte stat, dma_stat; + + dma_stat = HWIF(drive)->ide_dma_end(drive); + stat = HWIF(drive)->INB(IDE_STATUS_REG); /* get drive status */ + if (OK_STAT(stat, DRIVE_READY, drive->bad_wstat | DRQ_STAT)) { + if (!dma_stat) { + struct request *rq = HWGROUP(drive)->rq; + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(drive, 1); + } + return ide_stopped; + } + printk("%s: dma_intr: bad DMA status (dma_stat=%x)\n", + drive->name, dma_stat); + } + return ide_error(drive, "dma_intr", stat); +} + +/* ....and another one.... +*/ +int +report_drive_dmaing(ide_drive_t * drive) +{ + struct hd_driveid *id = drive->id; + + if ((id->field_valid & 4) && (eighty_ninty_three(drive)) && + (id->dma_ultra & (id->dma_ultra >> 11) & 7)) { + if ((id->dma_ultra >> 13) & 1) { + printk(", UDMA(100)"); /* UDMA BIOS-enabled! */ + } else if ((id->dma_ultra >> 12) & 1) { + printk(", UDMA(66)"); /* UDMA BIOS-enabled! */ + } else { + printk(", UDMA(44)"); /* UDMA BIOS-enabled! */ + } + } else if ((id->field_valid & 4) && + (id->dma_ultra & (id->dma_ultra >> 8) & 7)) { + if ((id->dma_ultra >> 10) & 1) { + printk(", UDMA(33)"); /* UDMA BIOS-enabled! */ + } else if ((id->dma_ultra >> 9) & 1) { + printk(", UDMA(25)"); /* UDMA BIOS-enabled! */ + } else { + printk(", UDMA(16)"); /* UDMA BIOS-enabled! */ + } + } else if (id->field_valid & 4) { + printk(", (U)DMA"); /* Can be BIOS-enabled! */ + } else { + printk(", DMA"); + } + return 1; +} + +static int +stb04xxx_ide_check_dma(ide_drive_t * drive) +{ + struct hd_driveid *id = drive->id; + int enable = 1; + int speed; + + drive->using_dma = 0; + + if (drive->media == ide_floppy) + enable = 0; + + /* Check timing here, we may be able to include XFER_UDMA_66 + * and XFER_UDMA_100. This basically tells the 'best_mode' + * function to also consider UDMA3 to UDMA5 device timing. + */ + if (enable) { + /* Section 1.6.2.6 "IDE Controller, ATA/ATAPI-5" in the STB04xxx + * Datasheet says the following modes are supported: + * PIO modes 0 to 4 + * Multiword DMA modes 0 to 2 + * UltraDMA modes 0 to 4 + */ + int map = XFER_PIO | XFER_EPIO | XFER_MWDMA | XFER_UDMA; + /* XFER_EPIO includes both PIO modes 4 and 5. Mode 5 is not + * valid for the STB04, so mask it out of consideration just + * in case some drive sets it... + */ + id->eide_pio_modes &= ~4; + + /* Allow UDMA_66 only if an 80 conductor cable is connected. */ + if (eighty_ninty_three(drive)) + map |= XFER_UDMA_66; + + speed = ide_find_best_mode(drive, map); + stb04xxx_ide_set_drive(drive, speed); + + if (HWIF(drive)->autodma && + (((speed & XFER_MODE) == XFER_PIO) || + ((speed & XFER_MODE) == XFER_EPIO))) { + drive->using_dma = 0; + } + } + + return 0; +} + +static int stb04xxx_ide_dma_off_quietly(ide_drive_t * drive) +{ + drive->using_dma = 0; + return 0; +} + +static int stb04xxx_ide_dma_off(ide_drive_t * drive) +{ + printk(KERN_INFO "%s: DMA disabled\n", drive->name); + return stb04xxx_ide_dma_off_quietly(drive); +} + +static int stb04xxx_ide_dma_on(ide_drive_t * drive) +{ + return stb04xxx_ide_check_dma(drive); +} + +static int stb04xxx_ide_dma_check(ide_drive_t * drive) +{ + return stb04xxx_ide_dma_on(drive); +} + +static int stb04xxx_ide_dma_begin(ide_drive_t * drive, int writing) +{ + idp->si_c0tb = (unsigned int) prd_phys; + idp->si_c0s0 = 0xdc800000; /* Clear all status */ + idp->si_c0ie = 0x90000000; /* Enable all intr */ + idp->si_c0dcm = 0; + idp->si_c0dcm = + (writing ? 0x09000000 : 0x01000000); + return 0; +} + +static int stb04xxx_ide_dma_io(ide_drive_t * drive, int writing) +{ + if (!stb04xxx_ide_build_dmatable(drive, writing)) + return 1; + drive->waiting_for_dma = 1; + if (drive->media != ide_disk) + return 0; + ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); + HWIF(drive)->OUTB(writing ? WIN_WRITEDMA : WIN_READDMA, + IDE_COMMAND_REG); + return stb04xxx_ide_dma_begin(drive, writing); +} + +static int stb04xxx_ide_dma_read(ide_drive_t * drive) +{ + return stb04xxx_ide_dma_io(drive, 0); +} + +static int stb04xxx_ide_dma_write(ide_drive_t * drive) +{ + return stb04xxx_ide_dma_io(drive, 1); +} + +static int stb04xxx_ide_dma_end(ide_drive_t * drive) +{ + unsigned int dstat; + + drive->waiting_for_dma = 0; + dstat = idp->si_c0s1; + idp->si_c0s0 = 0xdc800000; /* Clear all status */ + /* verify good dma status */ + return (dstat & 0x80000000); +} + +static int stb04xxx_ide_dma_test_irq(ide_drive_t * drive) +{ + return idp->si_c0s0 & 0x10000000 ? 1 : 0; +} + +static int stb04xxx_ide_dma_verbose(ide_drive_t * drive) +{ + return report_drive_dmaing(drive); +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +void +nonpci_ide_init_hwif_ports(hw_regs_t * hw, + ide_ioreg_t data_port, ide_ioreg_t ctrl_port, + int *irq) +{ + ide_ioreg_t *p; + unsigned char *ip; + unsigned int uicdcr; + int i; + struct ocp_dev *ide_dev; + int curr_ide; + ide_hwif_t *hwif; + + curr_ide = 0; + hwif = &ide_hwifs[0]; + + p = hw->io_ports; + *p = 0; + if (irq) + *irq = 0; + + if (data_port != 0) + return; + printk("IBM STB04xxx OCP IDE driver version %s\n", OCPVR); + if (!(ide_dev = ocp_alloc_dev(0))) + return; + + ide_dev->type = IDE; + if ((curr_ide = ocp_register(ide_dev)) == -ENXIO) { + ocp_free_dev(ide_dev); + return; + } else { + + if ((idp = (ide_t *) ioremap(ide_dev->paddr, + IDE0_SIZE)) == NULL) { + printk(KERN_WARNING "ide: failed ioremap\n"); + return; + } + + /* Enable the interface. + */ + idp->si_control = 0x80000000; + idp->si_c0s0 = 0xdc800000; /* Clear all status */ +#if 0 + idp->si_c0sr = 0xf0000000; /* Mandated val to Slew Rate Control */ +#endif + idp->si_intenable = 0x80000000; + /* Per the STB04 data sheet: + * 1) tTO = ((8*RDYT) + 1) * SYS_CLK + * and: + * 2) tTO >= 1250 + (2 * SYS_CLK) - t2 + * Solving the first equation for RDYT: + * (tTO/SYS_CLK) - 1 + * 3) RDYT = ----------------- + * 8 + * Substituting equation 2) for tTO in equation 3: + * ((1250 + (2 * SYS_CLK) - t2)/SYS_CLK) - 1 + * 3) RDYT = ----------------------------------------- + * 8 + * It's just the timeout so having it too long isn't too + * significant, so we'll just assume t2 is zero. All this math + * is handled by the compiler and RDYT ends up being 11 assuming + * that SYS_CLOCK_NS is 15. + */ + idp->si_c0timo = (EZ(EZ(1250 + 2 * SYS_CLOCK_NS, SYS_CLOCK_NS) - 1, 8)) << 23; /* Chan 0 timeout */ + + /* Stuff some slow default PIO timing. + */ + idp->si_c0rt = MK_TIMING(6, 19, 15, 2); + idp->si_c0fpt = MK_TIMING(6, 19, 15, 2); + + ip = (unsigned char *) (&(idp->si_c0d)); /* Chan 0 data */ + + for (i = 0; i <= IDE_STATUS_OFFSET; i++) { + *p++ = (int) (ip++); + } + hw->io_ports[IDE_CONTROL_OFFSET] = (int) (&(idp->si_c0adc)); + + if (irq) + *irq = ide_dev->irq; + + pio_mode[0] = pio_mode[1] = -1; + + /* We should probably have UIC functions to set external + * interrupt level/edge. + */ + uicdcr = mfdcr(DCRN_UIC_PR(UIC0)); + uicdcr &= ~(0x80000000 >> IDE0_IRQ); + mtdcr(DCRN_UIC_PR(UIC0), uicdcr); + mtdcr(DCRN_UIC_TR(UIC0), 0x80000000 >> IDE0_IRQ); + + /* Grab a page for the PRD Table. + */ + prd_table = (prd_entry_t *) consistent_alloc(GFP_KERNEL, + NUM_PRD * + sizeof + (prd_entry_t), + &prd_phys); + + hwif->tuneproc = &stb04xxx_ide_tuneproc; + hwif->speedproc = &stb04xxx_ide_set_drive; + /* Figure out if an 80 conductor cable is connected or not. */ + hwif->udma_four = + (idp->si_c0s1 & 0x20000000) != 0; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->autodma = 1; + hwif->ide_dma_off = &stb04xxx_ide_dma_off; + hwif->ide_dma_off_quietly = &stb04xxx_ide_dma_off_quietly; + hwif->ide_dma_host_off = &stb04xxx_ide_dma_off_quietly; + hwif->ide_dma_on = &stb04xxx_ide_dma_on; + hwif->ide_dma_host_on = &stb04xxx_ide_dma_on; + hwif->ide_dma_check = &stb04xxx_ide_dma_check; + hwif->ide_dma_read = &stb04xxx_ide_dma_read; + hwif->ide_dma_write = &stb04xxx_ide_dma_write; + hwif->ide_dma_begin = &stb04xxx_ide_dma_begin; + hwif->ide_dma_end = &stb04xxx_ide_dma_end; + hwif->ide_dma_test_irq = &stb04xxx_ide_dma_test_irq; + hwif->ide_dma_verbose = &stb04xxx_ide_dma_verbose; +#endif + + hwif->noprobe = 0; + + memcpy(hwif->io_ports, hw->io_ports, + sizeof (hw->io_ports)); + hwif->irq = ide_dev->irq; + } +} diff -urN -X bkexcl linux-2.4.22/drivers/ide/ocp_stbxxxxx.c linuxppc-2.4/drivers/ide/ocp_stbxxxxx.c --- linux-2.4.22/drivers/ide/ocp_stbxxxxx.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/ide/ocp_stbxxxxx.c 2003-05-26 09:40:53.000000000 +1000 @@ -0,0 +1,625 @@ +/* + * Copyright 2002 MontaVista Software Inc. + * Completed implementation. + * Author: Armin Kuster + * MontaVista Software, Inc. + * + * Module name: ocp_stbxxxx.c + * + * Description: + * + * Based on stb03xxx.c + * + * Version 07/23/02 - Armin + * removed many mtdcr/mfdcr dma calls to standard 4xx dma calls + */ + +#include +#include +#include +#include + +#include +#include + +#include "ide_modes.h" + +#define IDEVR "1.3" +ppc_dma_ch_t dma_ch; + +/* use DMA channel 2 for IDE DMA operations */ +#define IDE_DMACH 2 /* 2nd DMA channel */ +#define IDE_DMA_INT 6 /* IDE dma channel 2 interrupt */ + +#define WMODE 0 /* default to DMA line mode */ +#define PIOMODE 0 + +/* psc=00, pwc=000001 phc=010, resvd-must-be-one=1 */ + +unsigned long dmacr_def_line = 0x00002A02; + +/* psc=00, pwc=000110 phc=010, resvd-must-be-one=1 */ + +unsigned long dmacr_def_word = 0x0000CA02; + +#ifdef CONFIG_REDWOOD_4 +#define DCRXBCR_MDMA2 0xC0000000 +#else /* CONFIG_REDWOOD_6 */ +#define DCRXBCR_MDMA2 0x80000000 +#endif + +#define DCRXBCR_WRITE 0x20000000 +#define DCRXBCR_ACTIVATE 0x10000000 + +#ifdef CONFIG_REDWOOD_4 +#define IDE_CMD_OFF 0x00100000 +#define IDE_CTL_OFF 0x00100000 +#endif + +/* Function Prototypes */ +static void redwood_ide_tune_drive(ide_drive_t *, byte); +static byte redwood_ide_dma_2_pio(byte); +static int redwood_ide_tune_chipset(ide_drive_t *, byte); + +#ifdef DEBUG +static void dump_dcrs(void) +{ + printk("DMASR=%x\n", mfdcr(DCRN_DMASR)); + printk("DMACR2=%x\n", mfdcr(DCRN_DMACR2)); + printk("DMACT2=%d\n", mfdcr(DCRN_DMACT2)); + printk("DMAS2=%x\n", mfdcr(DCRN_DMAS2)); + printk("DMASA2=%x\n", mfdcr(DCRN_DMASA2)); + printk("DMADA2=%x\n", mfdcr(DCRN_DMADA2)); + + if (mfdcr(DCRN_DMASR) & 0x00200000) { + printk("BESR=%x\n", mfdcr(DCRN_BESR)); + printk("BEAR=%x\n", mfdcr(DCRN_BEAR)); + printk("PLB0_BESR=%x\n", mfdcr(DCRN_PLB0_BESR)); + printk("PLB0_BEAR=%x\n", mfdcr(DCRN_PLB0_BEAR)); + printk("PLB1_BESR=%x\n", mfdcr(DCRN_PLB1_BESR)); + printk("PLB1_BEAR=%x\n", mfdcr(DCRN_PLB1_BEAR)); + printk("OPB0_BESR0=%x\n", mfdcr(DCRN_POB0_BESR0)); + printk("OPB0_BEAR=%x\n", mfdcr(DCRN_POB0_BEAR)); + printk("SDRAM0_BESR=%x\n", mfdcr(0x1E1)); + printk("SDRAM0_BEAR=%x\n", mfdcr(0x1E2)); + printk("SDRAM1_BESR=%x\n", mfdcr(0x1C1)); + printk("SDRAM1_BEAR=%x\n", mfdcr(0x1C2)); + } + + return; +} +#endif /* DEBUG */ + +static void +redwood_ide_tune_drive(ide_drive_t * drive, byte pio) +{ + pio = ide_get_best_pio_mode(drive, pio, 5, NULL); +} + +static byte +redwood_ide_dma_2_pio(byte xfer_rate) +{ + switch (xfer_rate) { + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + case XFER_MW_DMA_2: + case XFER_PIO_4: + return 4; + case XFER_MW_DMA_1: + case XFER_PIO_3: + return 3; + case XFER_SW_DMA_2: + case XFER_PIO_2: + return 2; + case XFER_MW_DMA_0: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + case XFER_PIO_1: + case XFER_PIO_0: + case XFER_PIO_SLOW: + default: + return 0; + } +} + +static int +redwood_ide_tune_chipset(ide_drive_t * drive, byte speed) +{ + int err = 0; + + redwood_ide_tune_drive(drive, redwood_ide_dma_2_pio(speed)); + + if (!drive->init_speed) + drive->init_speed = speed; + err = ide_config_drive_speed(drive, speed); + drive->current_speed = speed; + return err; +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static int redwood_config_drive_for_dma(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + + if (id && (id->capability & 1) && hwif->autodma) { + /* + * Enable DMA on any drive that has + * UltraDMA (mode 0/1/2/3/4/5/6) enabled + */ + if ((id->field_valid & 4) && ((id->dma_ultra >> 8) & 0x7f)) + return hwif->ide_dma_on(drive); + /* + * Enable DMA on any drive that has mode2 DMA + * (multi or single) enabled + */ + if (id->field_valid & 2) /* regular DMA */ + if ((id->dma_mword & 0x404) == 0x404 || + (id->dma_1word & 0x404) == 0x404) + return hwif->ide_dma_on(drive); + } + + return hwif->ide_dma_off_quietly(drive); +} + +ide_startstop_t +redwood_ide_intr(ide_drive_t * drive) +{ + int i; + byte dma_stat; + unsigned int nsect; + ide_hwgroup_t *hwgroup = HWGROUP(drive); + struct request *rq = hwgroup->rq; + unsigned long block, b1, b2, b3, b4; + + nsect = rq->current_nr_sectors; + + dma_stat = HWIF(drive)->ide_dma_end(drive); + + rq->sector += nsect; + rq->buffer += nsect << 9; + rq->errors = 0; + i = (rq->nr_sectors -= nsect); + ide_end_request(drive, 1); + if (i > 0) { + b1 = HWIF(drive)->INB(IDE_SECTOR_REG); + b2 = HWIF(drive)->INB(IDE_LCYL_REG); + b3 = HWIF(drive)->INB(IDE_HCYL_REG); + b4 = HWIF(drive)->INB(IDE_SELECT_REG); + block = ((b4 & 0x0f) << 24) + (b3 << 16) + (b2 << 8) + (b1); + block++; + if (drive->select.b.lba) { + HWIF(drive)->OUTB(block, IDE_SECTOR_REG); + HWIF(drive)->OUTB(block >>= 8, IDE_LCYL_REG); + HWIF(drive)->OUTB(block >>= 8, IDE_HCYL_REG); + HWIF(drive)->OUTB(((block >> 8) & 0x0f) | drive->select.all, + IDE_SELECT_REG); + } else { + unsigned int sect, head, cyl, track; + track = block / drive->sect; + sect = block % drive->sect + 1; + HWIF(drive)->OUTB(sect, IDE_SECTOR_REG); + head = track % drive->head; + cyl = track / drive->head; + HWIF(drive)->OUTB(cyl, IDE_LCYL_REG); + HWIF(drive)->OUTB(cyl >> 8, IDE_HCYL_REG); + HWIF(drive)->OUTB(head | drive->select.all, IDE_SELECT_REG); + } + + if (rq->cmd == READ) + dma_stat = HWIF(drive)->ide_dma_read(drive); + else + dma_stat = HWIF(drive)->ide_dma_write(drive); + return ide_started; + } + return ide_stopped; +} + +static int redwood_dma_timer_expiry(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 dma_stat = hwif->INB(hwif->dma_status); + + printk(KERN_WARNING "%s: dma_timer_expiry: dma status == 0x%02x\n", + drive->name, dma_stat); + + if ((dma_stat & 0x18) == 0x18) /* BUSY Stupid Early Timer !! */ + return WAIT_CMD; + + HWGROUP(drive)->expiry = NULL; /* one free ride for now */ + + /* 1 dmaing, 2 error, 4 intr */ + + if (dma_stat & 2) { /* ERROR */ + (void) hwif->ide_dma_end(drive); + return DRIVER(drive)->error(drive, + "dma_timer_expiry", hwif->INB(IDE_STATUS_REG)); + } + if (dma_stat & 1) /* DMAing */ + return WAIT_CMD; + + if (dma_stat & 4) /* Got an Interrupt */ + HWGROUP(drive)->handler(drive); + + return 0; +} + +static int redwood_ide_dma_end(ide_drive_t *drive) +{ + drive->waiting_for_dma = 0; + + /* disable DMA */ + ppc4xx_disable_dma_interrupt(IDE_DMACH); + ppc4xx_disable_dma(IDE_DMACH); + return 0; +} + +static int redwood_ide_dma_off_quietly(ide_drive_t *drive) +{ + drive->using_dma = 0; + return redwood_ide_dma_end(drive); +} + +static int redwood_ide_dma_off(ide_drive_t *drive) +{ + printk(KERN_INFO "%s: DMA disabled\n", drive->name); + return redwood_ide_dma_off_quietly(drive); +} + +static int redwood_ide_dma_on(ide_drive_t *drive) +{ + mtdcr(DCRN_DMACR2, 0); + ppc4xx_clr_dma_status(IDE_DMACH); + +#if WMODE + mtdcr(DCRN_DCRXBCR, 0); + mtdcr(DCRN_CICCR, mfdcr(DCRN_CICCR) | 0x00000400); +#else + /* Configure CIC reg for line mode dma */ + mtdcr(DCRN_CICCR, mfdcr(DCRN_CICCR) & ~0x00000400); +#endif + drive->using_dma = 1; + return 0; +} + +static int redwood_ide_dma_check(ide_drive_t *drive) +{ + return redwood_config_drive_for_dma(drive); +} + +static int redwood_ide_dma_begin(ide_drive_t *drive) +{ + /* enable DMA */ + + ppc4xx_enable_dma_interrupt(IDE_DMACH); + ppc4xx_enable_dma(IDE_DMACH); + return 0; +} + +static int redwood_ide_dma_io(ide_drive_t *drive, int reading) +{ + ide_hwif_t *hwif = HWIF(drive); + struct request *rq = HWGROUP(drive)->rq; + unsigned long length; + + if (drive->media != ide_disk) + return 0; + + if (ppc4xx_get_channel_config(IDE_DMACH, &dma_ch) & DMA_CHANNEL_BUSY ) /* DMA is busy? */ + return -1; + + if (reading) { + dma_cache_inv((unsigned long) rq->buffer, + rq->current_nr_sectors * 512); + +#if WMODE + ppc4xx_set_src_addr(IDE_DMACH, 0); + ppc4xx_set_dma_addr(IDE_DMACH, virt_to_bus(rq->buffer)); +#else + ppc4xx_set_src_addr(IDE_DMACH, IDE_DMA_ADDR); + ppc4xx_set_dst_addr(IDE_DMACH, virt_to_bus(rq->buffer)); +#endif + } else { + dma_cache_wback_inv((unsigned long) rq->buffer, + rq->current_nr_sectors * 512); +#if WMODE + ppc4xx_set_dma_addr(IDE_DMACH, virt_to_bus(rq->buffer)); + ppc4xx_set_dst_addr(IDE_DMACH, 0); +#else + ppc4xx_set_src_addr(IDE_DMACH, virt_to_bus(rq->buffer)); + ppc4xx_set_dst_addr(IDE_DMACH, IDE_DMA_ADDR); +#endif + } + + hwif->OUTB(rq->current_nr_sectors, IDE_NSECTOR_REG); + length = rq->current_nr_sectors * 512; + + /* set_dma_count doesn't do M2M line xfer sizes right. */ + +#if WMODE + mtdcr(DCRN_DMACT2, length >> 2); +#else + mtdcr(DCRN_DMACT2, length >> 4); +#endif + + if (reading) { +#if WMODE + mtdcr(DCRN_DMACR2, DMA_TD | + SET_DMA_TM(TM_PERIPHERAL) | + SET_DMA_PW(PW_16) | + SET_DMA_DAI(1) | dmacr_def_word); + set_dma_mode(IDE_DMACH, DMA_MODE_READ); + +#else + mtdcr(DCRN_DCRXBCR, DCRXBCR_MDMA2 | DCRXBCR_ACTIVATE); + mtdcr(DCRN_DMACR2, SET_DMA_DAI(1) | SET_DMA_SAI(0) | + DMA_MODE_MM_DEVATSRC | SET_DMA_PW(PW_64) | + dmacr_def_line); + ppc4xx_set_dma_mode(IDE_DMACH, DMA_MODE_MM_DEVATSRC); +#endif + } else { +#if WMODE + mtdcr(DCRN_DMACR2, SET_DMA_TM(TM_PERIPHERAL) | + SET_DMA_PW(PW_16) | + SET_DMA_DAI(1) | dmacr_def_word); + ppc4xx_set_dma_mode(IDE_DMACH, DMA_MODE_WRITE); +#else + mtdcr(DCRN_DCRXBCR, DCRXBCR_WRITE | DCRXBCR_MDMA2 | + DCRXBCR_ACTIVATE); + mtdcr(DCRN_DMACR2,SET_DMA_DAI(0) | SET_DMA_SAI(1) | + DMA_MODE_MM_DEVATDST| SET_DMA_PW(PW_64) | + dmacr_def_line); + ppc4xx_set_dma_mode(IDE_DMACH, DMA_MODE_MM_DEVATDST); +#endif + } + + drive->waiting_for_dma = 1; + ide_set_handler(drive, &redwood_ide_intr, 2*WAIT_CMD, + redwood_dma_timer_expiry); + hwif->OUTB(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); + return HWIF(drive)->ide_dma_begin(drive); +} + +static int redwood_ide_dma_read(ide_drive_t *drive) +{ + return redwood_ide_dma_io(drive, 1); +} + +static int redwood_ide_dma_write(ide_drive_t *drive) +{ + return redwood_ide_dma_io(drive, 0); +} + +/* returns 1 if dma irq issued, 0 otherwise */ +static int redwood_ide_dma_test_irq(ide_drive_t *drive) +{ + return 1; +} + +static int redwood_ide_dma_verbose(ide_drive_t * drive) +{ + struct hd_driveid *id = drive->id; + + if (id->field_valid & 2) { + if (id->dma_mword & 0x0004) { + printk(", mdma2"); + } else if (id->dma_mword & 0x0002) { + printk(", mdma1"); + } else if (id->dma_mword & 1) { + printk(", mdma0"); + } else if (id->dma_1word & 0x0004) { + printk(", sdma2"); + } else { + printk(", pio%d", + ide_get_best_pio_mode(drive, 255, 5, NULL)); + } + } + + return 0; +} + +#endif + +void +ibm4xx_ide_spinup(int index) +{ + int i; + ide_ioreg_t *io_ports; + + printk("ide_redwood: waiting for drive ready.."); + io_ports = ide_hwifs[index].io_ports; + + /* wait until drive is not busy (it may be spinning up) */ + for (i = 0; i < 30; i++) { + unsigned char stat; + stat = inb_p(io_ports[7]); + /* wait for !busy & ready */ + if ((stat & 0x80) == 0) { + break; + } + + udelay(1000 * 1000); /* 1 second */ + } + + printk(".."); + + /* select slave */ + outb_p(0xa0 | 0x10, io_ports[6]); + + for (i = 0; i < 30; i++) { + unsigned char stat; + stat = inb_p(io_ports[7]); + /* wait for !busy & ready */ + if ((stat & 0x80) == 0) { + break; + } + + udelay(1000 * 1000); /* 1 second */ + } + + printk(".."); + + outb_p(0xa0, io_ports[6]); + printk("Drive spun up \n"); +} + +int +nonpci_ide_default_irq(ide_ioreg_t base) +{ + return IDE0_IRQ; +} + +void +nonpci_ide_init_hwif_ports(hw_regs_t * hw, ide_ioreg_t data_port, + ide_ioreg_t ctrl_port, int *irq) +{ + unsigned long ioaddr; +#ifdef CONFIG_REDWOOD_4 + unsigned long reg = data_port; + unsigned long xilinx; +#endif + int i, index; + ide_hwif_t *hwif; + + printk("IBM Redwood 4/6 IDE driver version %s\n", IDEVR); + + if (!request_region(REDWOOD_IDE_CMD, 0x10, "IDE")) + return; + + if (!request_region(REDWOOD_IDE_CTRL, 2, "IDE")) { + release_region(REDWOOD_IDE_CMD, 0x10); + return; + } + +#ifdef CONFIG_REDWOOD_4 + mtdcr(DCRN_DCRXICR, 0x40000000); /* set dcrx internal arbiter */ + + /* add RE & OEN to value set by boot rom */ + mtdcr(DCRN_BRCR3, 0x407cfffe); + + /* reconstruct phys addrs from EBIU config regs for CS2# */ + reg = ((mfdcr(DCRN_BRCR2) & 0xff000000) >> 4) | 0xf0000000; + xilinx = reg | 0x00040000; + reg = reg | IDE_CMD_OFF; + + ioaddr = (unsigned long)ioremap(reg, 0x10); + xilinx = (unsigned long)ioremap(xilinx, 0x10); + + i=readw(xilinx); + if(i & 0x0001) { + writew( i & ~0x8001, xilinx); + writew( 0, xilinx+7*2); + udelay(10*1000); /* 10 ms */ + } + + /* init xilinx control registers - enable ide mux, clear reset bit */ + writew( i | 0x8001, xilinx); + writew( 0, xilinx+7*2); + +#else /* CONFIG_REDWOOD_6 */ + ioaddr = (unsigned long) ioremap(REDWOOD_IDE_CMD, 0x10); +#endif + + hw->irq = IDE0_IRQ; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw->io_ports[i] = ioaddr; + ioaddr += 2; + } + hw->io_ports[IDE_CONTROL_OFFSET] = + (unsigned long) ioremap(REDWOOD_IDE_CTRL, 2); + + /* use DMA channel 2 for IDE DMA operations */ + hw->dma = IDE_DMACH; +#if WMODE + /*Word Mode psc(11-12)=00,pwc(13-18)=000110, phc(19-21)=010, 22=1, 30=1 ---- 0xCB02*/ + + dma_ch.mode =DMA_MODE_READ; /* xfer from peripheral to mem */ + dma_ch.td = 1; + dma_ch.buffer_enable = 0; + dma_ch.tce_enable = 0; + dma_ch.etd_output = 0; + dma_ch.pce = 0; + dma_ch.pl = EXTERNAL_PERIPHERAL; /* no op */ + dma_ch.pwidth = PW_16; + dma_ch.dai = 1; + dma_ch.sai = 0; + dma_ch.psc = 0; /* set the max setup cycles */ + dma_ch.pwc = 6; /* set the max wait cycles */ + dma_ch.phc = 2; /* set the max hold cycles */ + dma_ch.cp = PRIORITY_LOW; + dma_ch.int_enable = 0; + dma_ch.ch_enable = 0; /* No chaining */ + dma_ch.tcd_disable = 1; /* No chaining */ +#else +/*Line Mode psc(11-12)=00,pwc(13-18)=000001, phc(19-21)=010, 22=1, 30=1 ---- 0x2B02*/ + + dma_ch.mode =DMA_MODE_MM_DEVATSRC; /* xfer from peripheral to mem */ + dma_ch.td = 1; + dma_ch.buffer_enable = 0; + dma_ch.tce_enable = 0; + dma_ch.etd_output = 0; + dma_ch.pce = 0; + dma_ch.pl = EXTERNAL_PERIPHERAL; /* no op */ + dma_ch.pwidth = PW_64; /* Line mode on stbs */ + dma_ch.dai = 1; + dma_ch.sai = 0; + dma_ch.psc = 0; /* set the max setup cycles */ + dma_ch.pwc = 1; /* set the max wait cycles */ + dma_ch.phc = 2; /* set the max hold cycles */ + dma_ch.cp = PRIORITY_LOW; + dma_ch.int_enable = 0; + dma_ch.ch_enable = 0; /* No chaining */ + dma_ch.tcd_disable = 1; /* No chaining */ +#endif + + if (ppc4xx_init_dma_channel(IDE_DMACH, &dma_ch) != DMA_STATUS_GOOD) + return; + + ppc4xx_disable_dma_interrupt(IDE_DMACH); + + /* init CIC control reg to enable IDE interface PIO mode */ + mtdcr(DCRN_CICCR, (mfdcr(DCRN_CICCR) & 0xffff7bff) | 0x0003); + + /* + * init CIC select2 reg to connect external DMA port 3 to internal + * DMA channel 2 + */ + + /* FIXME: EXT_DMA_3 is out of bounds for map_dma_port. */ + /* map_dma_port(IDE_DMACH,EXT_DMA_3,DMA_CHAN_2); */ + mtdcr(DCRN_DMAS2, (mfdcr(DCRN_DMAS2) & 0xfffffff0) | 0x00000002); + + /* Verified BRCR7 already set per manual. */ + + index = 0; + hwif = &ide_hwifs[index]; + hwif->tuneproc = &redwood_ide_tune_drive; + hwif->drives[0].autotune = 1; +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->autodma = 1; + hwif->ide_dma_off = &redwood_ide_dma_off; + hwif->ide_dma_off_quietly = &redwood_ide_dma_off_quietly; + hwif->ide_dma_host_off = &redwood_ide_dma_off_quietly; + hwif->ide_dma_on = &redwood_ide_dma_on; + hwif->ide_dma_host_on = &redwood_ide_dma_on; + hwif->ide_dma_check = &redwood_ide_dma_check; + hwif->ide_dma_read = &redwood_ide_dma_read; + hwif->ide_dma_write = &redwood_ide_dma_write; + hwif->ide_dma_begin = &redwood_ide_dma_begin; + hwif->ide_dma_end = &redwood_ide_dma_end; + hwif->ide_dma_test_irq = &redwood_ide_dma_test_irq; + hwif->ide_dma_verbose = &redwood_ide_dma_verbose; +#endif + hwif->speedproc = &redwood_ide_tune_chipset; + hwif->noprobe = 0; + + memcpy(hwif->io_ports, hw->io_ports, sizeof (hw->io_ports)); + hwif->irq = hw->irq; + ibm4xx_ide_spinup(index); +} + diff -urN -X bkexcl linux-2.4.22/drivers/ide/ppc/Makefile linuxppc-2.4/drivers/ide/ppc/Makefile --- linux-2.4.22/drivers/ide/ppc/Makefile 2002-12-19 11:50:38.000000000 +1100 +++ linuxppc-2.4/drivers/ide/ppc/Makefile 2003-03-14 20:46:44.000000000 +1100 @@ -4,6 +4,7 @@ obj-y := obj-m := +obj-$(CONFIG_BLK_DEV_CPCI405_IDE) += cpci405ide.o obj-$(CONFIG_BLK_DEV_MPC8xx_IDE) += mpc8xx.o obj-$(CONFIG_BLK_DEV_IDE_PMAC) += pmac.o obj-$(CONFIG_BLK_DEV_IDE_SWARM) += swarm.o diff -urN -X bkexcl linux-2.4.22/drivers/ide/ppc/cpci405ide.c linuxppc-2.4/drivers/ide/ppc/cpci405ide.c --- linux-2.4.22/drivers/ide/ppc/cpci405ide.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/ide/ppc/cpci405ide.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,72 @@ +/* + * linux/drivers/ide/ppc/cpci405ide.c -- CPCI405 IDE Driver + * + * Copyright (C) 2001-2003 Stefan Roese, stefan.roese@esd-electronics.de + * + * This driver was written based on information obtained from the MacOS IDE + * driver binary by Mikael Forselius + * + * 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 + + +/* + * Base of the IDE interface + */ + +#define CPCI405_HD_BASE 0xf0100000 + +/* + * Offsets from the above base (scaling 4) + */ + +#define CPCI405_HD_DATA 0x00 +#define CPCI405_HD_ERROR 0x01 /* see err-bits */ +#define CPCI405_HD_NSECTOR 0x02 /* nr of sectors to read/write */ +#define CPCI405_HD_SECTOR 0x03 /* starting sector */ +#define CPCI405_HD_LCYL 0x04 /* starting cylinder */ +#define CPCI405_HD_HCYL 0x05 /* high byte of starting cyl */ +#define CPCI405_HD_SELECT 0x06 /* 101dhhhh , d=drive, hhhh=head */ +#define CPCI405_HD_STATUS 0x07 /* see status-bits */ +#define CPCI405_HD_CONTROL 0x0e /* control/altstatus */ + +#define CPCI405_IRQ_IDE 0x1f + +static int cpci405ide_offsets[IDE_NR_PORTS] __initdata = { + CPCI405_HD_DATA, CPCI405_HD_ERROR, CPCI405_HD_NSECTOR, CPCI405_HD_SECTOR, + CPCI405_HD_LCYL, CPCI405_HD_HCYL, CPCI405_HD_SELECT, CPCI405_HD_STATUS, + CPCI405_HD_CONTROL +}; + +static void *cpci405_ide_base_mapped; + + +/* + * Probe for a CPCI405 IDE interface + */ + +void __init cpci405ide_init(void) +{ + hw_regs_t hw; + int index = -1; + + cpci405_ide_base_mapped = + ioremap((unsigned long) CPCI405_HD_BASE, 0x200) - _IO_BASE; + ide_setup_ports(&hw, (ide_ioreg_t)cpci405_ide_base_mapped, + cpci405ide_offsets, 0, 0, NULL, CPCI405_IRQ_IDE); + index = ide_register_hw(&hw, NULL); + if (index != -1) + printk("ide%d: CPCI405 IDE interface\n", index); + +} diff -urN -X bkexcl linux-2.4.22/drivers/ide/stb03xxx.c linuxppc-2.4/drivers/ide/stb03xxx.c --- linux-2.4.22/drivers/ide/stb03xxx.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/ide/stb03xxx.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,469 @@ +/* + * Copyright 2001 MontaVista Software Inc. + * Completed implementation. + * Author: MontaVista Software, Inc. + * Hai-Pao Fan + * + * Module name: stb03xxx.c + * + * Description: + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "ide_modes.h" + +#define REDWOOD_IDE_CMD 0xf2100000 +#define REDWOOD_IDE_CTRL 0xf4100000 +#define IDE_CMD_OFF 0x00100000 +#define IDE_CTL_OFF 0x00100000 + +#define IDE_INTRQ 25 /* interrupt level(int0) */ + +#define REDWOOD_DMA_ADDR 0xfce00000 + + +/* use DMA channel 2 for IDE DMA operations */ + +#define IDE_DMA_INT 6 /* IDE dma channel 2 interrupt */ + +/* DMACR2 */ +#define IDE_DMACR_CE 0x80000000 /* 00 channel enable */ +#define IDE_DMACR_CIE 0x40000000 /* 01 channel interrupt enable */ +#define IDE_DMACR_TD 0x20000000 /* 02 transfer direction */ + /* 0 = write 1 = read */ +#define IDE_DMACR_PL 0x10000000 /* 03 peripheral location */ +#define IDE_DMACR_PW 0x0C000000 /* 04-05 peripheral width */ +#define IDE_DMACR_DAI 0x02000000 /* 06 destination address increment */ +#define IDE_DMACR_SAI 0x01000000 /* 07 source address increment */ +#define IDE_DMACR_CP 0x00800000 /* 08 high order channel priority bit*/ +#define IDE_DMACR_TM 0x00600000 /* 09-10 transfer mode */ +#define IDE_DMACR_PSC 0x00180000 /* 11-12 peripheral setup cycles */ +#define IDE_DMACR_PWC 0x0007E000 /* 13-18 peripheral wait cycles */ +#define IDE_DMACR_PHC 0x00001C00 /* 19-21 peripheral hold cycles */ +#define IDE_DMACR_ETD 0x00000200 /* 22 EOT/TC */ +#define IDE_DMACR_TCE 0x00000100 /* 23 TC Enable */ +#define IDE_DMACR_CH 0x00000080 /* 24 chaining enable */ +#define IDE_DMACR_ECE 0x00000020 /* 26 EOT chain mode enable */ +#define IDE_DMACR_TCD 0x00000010 /* 27 TC chain mode enable */ +#define IDE_DMACR_DEC 0x00000004 /* 29 destination address decrement */ +#define IDE_DMACR_CP1 0x00000001 /* 31 low order channel priority bit */ + +#define IDE_DMASR_TC 0x20000000 +#define IDE_DMASR_EOT 0x02000000 +#define IDE_DMASR_ERR 0x00200000 +#define IDE_DMASR_CB 0x00000100 +#define IDE_DMASR_CT 0x00000020 + +unsigned long dmacr_def = 0x0000AB02; /* pwc=101 phc=10, res:30=1 */ +extern char *ide_dmafunc_verbose(ide_dma_action_t dmafunc); + + +#define WMODE 0 /* default to DMA line mode */ +#define PIOMODE 0 +static volatile unsigned long dmastat; + +/* Function Prototypes */ +static void redwood_ide_tune_drive(ide_drive_t *, byte); +static byte redwood_ide_dma_2_pio(byte); +static int redwood_ide_tune_chipset(ide_drive_t *, byte); +static int redwood_ide_dmaproc(ide_dma_action_t, ide_drive_t *); + + +static void redwood_ide_tune_drive(ide_drive_t *drive, byte pio) +{ + pio = ide_get_best_pio_mode(drive, pio, 5, NULL); +} + +static byte redwood_ide_dma_2_pio(byte xfer_rate) +{ + switch(xfer_rate) { + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + case XFER_MW_DMA_2: + case XFER_PIO_4: + return 4; + case XFER_MW_DMA_1: + case XFER_PIO_3: + return 3; + case XFER_SW_DMA_2: + case XFER_PIO_2: + return 2; + case XFER_MW_DMA_0: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + case XFER_PIO_1: + case XFER_PIO_0: + case XFER_PIO_SLOW: + default: + return 0; + } +} + +static int redwood_ide_tune_chipset (ide_drive_t *drive, byte speed) +{ + int err=0; + + redwood_ide_tune_drive(drive, redwood_ide_dma_2_pio(speed)); + + if (!drive->init_speed) + drive->init_speed = speed; + err = ide_config_drive_speed(drive, speed); + drive->current_speed = speed; + return err; +} + +static int redwood_config_drive_for_dma(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + byte speed; + int func = ide_dma_off; + + /* + * Enable DMA on any drive that has multiword DMA + */ + if (id->field_valid & 2) { + if (id->dma_mword & 0x0004) { + speed = XFER_MW_DMA_2; + func = ide_dma_on; + } else if (id->dma_mword & 0x0002) { + speed = XFER_MW_DMA_1; + func = ide_dma_on; + } else if (id->dma_mword & 1) { + speed = XFER_MW_DMA_0; + func = ide_dma_on; + } else if (id->dma_1word & 0x0004) { + speed = XFER_SW_DMA_2; + func = ide_dma_on; + } else { + speed = XFER_PIO_0 + + ide_get_best_pio_mode(drive, 255, 5, NULL); + } + } + + redwood_ide_tune_drive(drive, redwood_ide_dma_2_pio(speed)); + return redwood_ide_dmaproc(func, drive); +} + +ide_startstop_t redwood_ide_intr (ide_drive_t *drive) +{ + int i; + byte dma_stat; + unsigned int nsect; + ide_hwgroup_t *hwgroup = HWGROUP(drive); + struct request *rq = hwgroup->rq; + unsigned long block,b1,b2,b3,b4; + + nsect = rq->current_nr_sectors; + + dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive); + + rq->sector += nsect; + rq->buffer += nsect<<9; + rq->errors = 0; + i = (rq->nr_sectors -= nsect); + ide_end_request(1, HWGROUP(drive)); + if (i > 0) { + b1 = IN_BYTE(IDE_SECTOR_REG); + b2 = IN_BYTE(IDE_LCYL_REG); + b3 = IN_BYTE(IDE_HCYL_REG); + b4 = IN_BYTE(IDE_SELECT_REG); + block = ((b4 & 0x0f) << 24) + (b3 << 16) + (b2 << 8) + (b1); + block++; + if (drive->select.b.lba) { + OUT_BYTE(block,IDE_SECTOR_REG); + OUT_BYTE(block>>=8,IDE_LCYL_REG); + OUT_BYTE(block>>=8,IDE_HCYL_REG); + OUT_BYTE(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG); + } else { + unsigned int sect,head,cyl,track; + track = block / drive->sect; + sect = block % drive->sect + 1; + OUT_BYTE(sect,IDE_SECTOR_REG); + head = track % drive->head; + cyl = track / drive->head; + OUT_BYTE(cyl,IDE_LCYL_REG); + OUT_BYTE(cyl>>8,IDE_HCYL_REG); + OUT_BYTE(head|drive->select.all,IDE_SELECT_REG); + } + + if (rq->cmd == READ) + dma_stat = HWIF(drive)->dmaproc(ide_dma_read, drive); + else + dma_stat = HWIF(drive)->dmaproc(ide_dma_write, drive); + return ide_started; + } + return ide_stopped; +} + +void redwood_ide_dma_intr (int irq, void *dev_id, struct pt_regs *regs) +{ + dmastat = mfdcr(DCRN_DMASR); + + mtdcr(DCRN_DMASR, + IDE_DMASR_TC | IDE_DMASR_EOT | IDE_DMASR_ERR | IDE_DMASR_CT); + + /* clear UIC bit */ + mtdcr(DCRN_UIC_SR(UIC0), (0x80000000 >> IDE_DMA_INT)); +} + +static int redwood_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + int i, reading = 0; + struct request *rq=HWGROUP(drive)->rq; + unsigned long flags; + unsigned long length; + + switch (func) { + case ide_dma_off: + case ide_dma_off_quietly: + /*disable_dma*/ + return 0; + + case ide_dma_on: +#if PIOMODE + return 1; +#endif + + mtdcr(DCRN_DMACR2,0); + mtdcr(DCRN_DMASR, IDE_DMASR_TC | IDE_DMASR_EOT | IDE_DMASR_ERR + | IDE_DMASR_CT); + + /* level-sensitive (UICTR bit = 0) and positive-active (UICPR bit = 1) */ + mtdcr(DCRN_UIC_PR(UIC0), (mfdcr(DCRN_UIC_PR(UIC0)) | UIC_D2)); + save_flags(flags); + cli(); + if ( ide_request_irq(IDE_DMA_INT, &redwood_ide_dma_intr, SA_INTERRUPT, + hwif->name, hwif->hwgroup) ) { + printk("ide_redwood: ide_request_irq failed int=%d\n", + IDE_DMA_INT); + restore_flags(flags); + return 1; + } + restore_flags(flags); + + drive->using_dma = (func == ide_dma_on); +#if WMODE + mtdcr(DCRN_DCRXBCR, 0); + mtdcr(DCRN_CICCR, mfdcr(DCRN_CICCR) | 0x00000400); +#else + /* Configure CIC reg for line mode dma */ + mtdcr(DCRN_CICCR, mfdcr(DCRN_CICCR) & ~0x00000400); +#endif + return 0; + + case ide_dma_check: + return redwood_config_drive_for_dma(drive); + case ide_dma_read: + reading = 1; + case ide_dma_write: + if (drive->media != ide_disk) + return -1; + + if (mfdcr(DCRN_DMACR2) & IDE_DMACR_CE) /* DMA is busy? */ + return -1; + + mtdcr(DCRN_DMASR, IDE_DMASR_TC | IDE_DMASR_EOT | IDE_DMASR_ERR + | IDE_DMASR_CT); + + if (reading) { + dma_cache_inv((unsigned long) rq->buffer, + rq->current_nr_sectors * 512); +#if WMODE + mtdcr(DCRN_DMASA2,0); +#else + mtdcr(DCRN_DMASA2,0xfce00000); +#endif + mtdcr(DCRN_DMADA2, virt_to_bus(rq->buffer)); + } else { + dma_cache_wback_inv((unsigned long) rq->buffer, + rq->current_nr_sectors * 512); + mtdcr(DCRN_DMASA2, virt_to_bus(rq->buffer)); +#if WMODE + mtdcr(DCRN_DMADA2,0); +#else + mtdcr(DCRN_DMADA2,0xfce00000); +#endif + } + +#if WMODE + length = rq->current_nr_sectors * 512/2; +#else + length = rq->current_nr_sectors * 512/16; +#endif + OUT_BYTE(rq->current_nr_sectors,IDE_NSECTOR_REG); + mtdcr(DCRN_DMACT2, length); + + /* CE=0 disable DMA */ + /* Set up the Line Buffer Control Register + * 11d1xxxx0.. - 11=Mode 2 (120 ns cycle), d=1/0(read/write) + * 1=active, 0=1X clock mode. + */ + + if (reading) { +#if WMODE + mtdcr(DCRN_DMACR2, 0x66000000 | dmacr_def); +#else + mtdcr(DCRN_DCRXBCR,0xD0000000); + mtdcr(DCRN_DMACR2, 0x6E600000 | dmacr_def); +#endif + } else { +#if WMODE + mtdcr(DCRN_DMACR2, 0x46000000 | dmacr_def); +#else + mtdcr(DCRN_DCRXBCR, 0xF0000000); + mtdcr(DCRN_DMACR2, 0x4D600000 | dmacr_def); +#endif + } + + set_dma_mode(hwif->hw.dma, reading + ? DMA_MODE_READ : DMA_MODE_WRITE); + drive->waiting_for_dma = 1; + ide_set_handler(drive, &redwood_ide_intr, WAIT_CMD, NULL); + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); + + case ide_dma_begin: + /* enable DMA */ + mtdcr(DCRN_DMACR2,mfdcr(DCRN_DMACR2) | 0x80000000); + + /* wait for dma to complete (channel 2 terminal count) */ + for (i=0; i < 5000000; i++) { + if (dmastat & DMA_CS2) + break; + } + dmastat = 0; + return 0; + + case ide_dma_end: + drive->waiting_for_dma = 0; + + /* disable DMA */ + mtdcr(DCRN_DMACR2, mfdcr(DCRN_DMACR2) & ~0x80000000); + mtdcr(DCRN_DCRXBCR,0); + return 0; + + case ide_dma_test_irq: + return 1; /* returns 1 if dma irq issued, 0 otherwise */ + + case ide_dma_bad_drive: + case ide_dma_good_drive: + case ide_dma_verbose: + case ide_dma_timeout: + case ide_dma_retune: + case ide_dma_lostirq: + printk("ide_dmaproc: chipset supported %s func only: %d\n", + ide_dmafunc_verbose(func), func); + return 1; + default: + printk("ide_dmaproc: unsupported %s func: %d\n", ide_dmafunc_verbose(func), func); + return 1; + + //return ide_dmaproc(func, drive); + } + +} + +int nonpci_ide_default_irq(ide_ioreg_t base) +{ + return IDE_INTRQ; +} + + +void +nonpci_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) +{ + unsigned long reg = data_port; + unsigned long ioaddr; + unsigned long xilinx; + int i, index; + + if (!request_region(REDWOOD_IDE_CMD, 0x10, "IDE")) + return; + + if (!request_region(REDWOOD_IDE_CTRL, 2, "IDE")) { + release_region(REDWOOD_IDE_CMD, 0x10); + return; + } + + mtdcr(DCRN_DCRXICR, 0x40000000); /* set dcrx internal arbiter */ + + /* reconstruct phys addrs from EBIU config regs for CS2# */ + reg = ((mfdcr(DCRN_BRCR2) & 0xff000000) >> 4) | 0xf0000000; + xilinx = reg | 0x00040000; + reg = reg | IDE_CMD_OFF; + + ioaddr = (unsigned long)ioremap(reg, 0x10); + xilinx = (unsigned long)ioremap(xilinx, 0x10); + + hw->irq = IDE_INTRQ; + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw->io_ports[i] = ioaddr; + ioaddr += 2; + } + hw->io_ports[IDE_CONTROL_OFFSET] = (unsigned long)ioremap(REDWOOD_IDE_CTRL,2); + + /* add RE & OEN to value set by boot rom */ + mtdcr(DCRN_BRCR3, 0x407cfffe); + + /* init CIC control reg to enable IDE interface PIO mode */ + mtdcr(DCRN_CICCR, (mfdcr(DCRN_CICCR) & 0xffff7bff) | 0x0003); + + i=readw(xilinx); + if(i & 0x0001) { + writew( i & ~0x8001, xilinx); + writew( 0, xilinx+7*2); + udelay(10*1000); /* 10 ms */ + } + + /* init xilinx control registers - enable ide mux, clear reset bit */ + writew( i | 0x8001, xilinx); + writew( 0, xilinx+7*2); + + /* wait until drive is not busy (it may be spinning up) */ + for (i=0; i < 10; i++) { + unsigned char stat; + stat = inb_p(hw->io_ports[7]); + /* wait for !busy & ready */ + if ((stat & 0x80) == 0) + break; + + udelay(1000*1000); /* 1 second */ + } + + /* use DMA channel 2 for IDE DMA operations */ + hw->dma = 2; + + mtdcr(DCRN_DMACR2, 0x4d600000 | dmacr_def); + mtdcr(DCRN_DMASR, 0xffffffff); /* clear status register */ + + /* init CIC select2 reg to connect external DMA port 3 to internal + * DMA channel 2 + */ + mtdcr(DCRN_DMAS2, (mfdcr(DCRN_DMAS2) & 0xfffffff0) | 0x00000002); + + index = 0; + + ide_hwifs[index].tuneproc = &redwood_ide_tune_drive; + ide_hwifs[index].drives[0].autotune = 1; + ide_hwifs[index].autodma = 1; + ide_hwifs[index].dmaproc = &redwood_ide_dmaproc; + ide_hwifs[index].speedproc = &redwood_ide_tune_chipset; + ide_hwifs[index].noprobe = 0; + + memcpy(ide_hwifs[index].io_ports, hw->io_ports, sizeof(hw->io_ports)); + ide_hwifs[index].irq = IDE_INTRQ; +} + diff -urN -X bkexcl linux-2.4.22/drivers/mtd/maps/Config.in linuxppc-2.4/drivers/mtd/maps/Config.in --- linux-2.4.22/drivers/mtd/maps/Config.in 2003-01-30 07:39:12.000000000 +1100 +++ linuxppc-2.4/drivers/mtd/maps/Config.in 2003-03-14 20:46:44.000000000 +1100 @@ -38,13 +38,24 @@ dep_tristate ' BIOS flash chip on Intel SCB2 boards' CONFIG_MTD_SCB2_FLASH $CONFIG_MTD_GEN_PROBE fi -if [ "$CONFIG_PPC" = "y" ]; then +if [ "$CONFIG_PPC32" = "y" ]; then dep_tristate ' CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI $CONFIG_TQM8xxL dep_tristate ' CFI Flash device mapped on RPX Lite or CLLF' CONFIG_MTD_RPXLITE $CONFIG_MTD_CFI dep_tristate ' System flash on MBX860 board' CONFIG_MTD_MBX860 $CONFIG_MTD_CFI dep_tristate ' CFI Flash device mapped on D-Box2' CONFIG_MTD_DBOX2 $CONFIG_MTD_CFI dep_tristate ' CFI Flash device mapping on FlagaDM' CONFIG_MTD_CFI_FLAGADM $CONFIG_MTD_CFI - dep_tristate ' CFI Flash device mapped on IBM Redwood-4/5' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI + if [ "$CONFIG_4xx" = "y" ]; then + if [ "$CONFIG_40x" = "y" ]; then + if [ "$CONFIG_REDWOOD_4" = "y" -o "$CONFIG_REDWOOD_5" = "y" -o "$CONFIG_REDWOOD_6" = "y" ]; then + dep_tristate ' CFI Flash device mapped on IBM Redwood' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI + fi + dep_tristate ' CFI Flash device mapped on IBM Beech' CONFIG_MTD_BEECH $CONFIG_MTD_CFI $CONFIG_BEECH + dep_tristate ' CFI Flash device mapped on IBM Arctic' CONFIG_MTD_ARCTIC $CONFIG_MTD_CFI $CONFIG_ARCTIC2 + fi + if [ "$CONFIG_440" = "y" ]; then + dep_tristate ' Flash devices mapped on IBM Ebony' CONFIG_MTD_EBONY $CONFIG_MTD_CFI $CONFIG_EBONY + fi + fi fi if [ "$CONFIG_MIPS" = "y" ]; then diff -urN -X bkexcl linux-2.4.22/drivers/mtd/maps/Makefile linuxppc-2.4/drivers/mtd/maps/Makefile --- linux-2.4.22/drivers/mtd/maps/Makefile 2003-01-30 07:39:12.000000000 +1100 +++ linuxppc-2.4/drivers/mtd/maps/Makefile 2003-03-14 20:46:44.000000000 +1100 @@ -61,5 +61,8 @@ obj-$(CONFIG_MTD_UCLINUX) += uclinux.o obj-$(CONFIG_MTD_NETtel) += nettel.o obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o +obj-$(CONFIG_MTD_EBONY) += ebony.o +obj-$(CONFIG_MTD_BEECH) += beech-mtd.o +obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.o include $(TOPDIR)/Rules.make diff -urN -X bkexcl linux-2.4.22/drivers/mtd/maps/arctic-mtd.c linuxppc-2.4/drivers/mtd/maps/arctic-mtd.c --- linux-2.4.22/drivers/mtd/maps/arctic-mtd.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/mtd/maps/arctic-mtd.c 2003-06-04 15:21:24.000000000 +1000 @@ -0,0 +1,186 @@ +/* + * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for + * IBM 405LP Arctic boards. + * + * 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 + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * Bishop Brock + * IBM Research, Austin Center for Low-Power Computing + * bcbrock@us.ibm.com + * March 2002 + * + * modified for Arctic by, + * David Gibson + * IBM OzLabs, Canberra, Australia + * + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* + * 0 : 0xFE00 0000 - 0xFEFF FFFF : Filesystem 1 (16MiB) + * 1 : 0xFF00 0000 - 0xFF4F FFFF : kernel (5.12MiB) + * 2 : 0xFF50 0000 - 0xFFF5 FFFF : Filesystem 2 (10.624MiB) (if non-XIP) + * 3 : 0xFFF6 0000 - 0xFFFF FFFF : PIBS Firmware (640KiB) + */ + +#define FFS1_SIZE 0x01000000 /* 16MiB */ +#define KERNEL_SIZE 0x00500000 /* 5.12MiB */ +#define FFS2_SIZE 0x00a60000 /* 10.624MiB */ +#define FIRMWARE_SIZE 0x000a0000 /* 640KiB */ + + +#define NAME "Arctic Linux Flash" +#define PADDR SUBZERO_BOOTFLASH_PADDR +#define BUSWIDTH 2 +#define SIZE SUBZERO_BOOTFLASH_SIZE +#define PARTITIONS 4 + +/* Flash memories on these boards are memory resources, accessed big-endian. */ + +static u8 +arctic_mtd_read8(struct map_info *map, unsigned long offset) +{ + return __raw_readb(map->map_priv_1 + offset); +} + +static u16 +arctic_mtd_read16(struct map_info *map, unsigned long offset) +{ + return __raw_readw(map->map_priv_1 + offset); +} + +static u32 +arctic_mtd_read32(struct map_info *map, unsigned long offset) +{ + return __raw_readl(map->map_priv_1 + offset); +} + +static void +arctic_mtd_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, (void *) (map->map_priv_1 + from), len); +} + +static void +arctic_mtd_write8(struct map_info *map, u8 data, unsigned long address) +{ + __raw_writeb(data, map->map_priv_1 + address); + mb(); +} + +static void +arctic_mtd_write16(struct map_info *map, u16 data, unsigned long address) +{ + __raw_writew(data, map->map_priv_1 + address); + mb(); +} + +static void +arctic_mtd_write32(struct map_info *map, u32 data, unsigned long address) +{ + __raw_writel(data, map->map_priv_1 + address); + mb(); +} + +static void +arctic_mtd_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); +} + +static struct map_info arctic_mtd_map = { + .name = NAME, + .size = SIZE, + .buswidth = BUSWIDTH, + .read8 = arctic_mtd_read8, + .read16 = arctic_mtd_read16, + .read32 = arctic_mtd_read32, + .copy_from = arctic_mtd_copy_from, + .write8 = arctic_mtd_write8, + .write16 = arctic_mtd_write16, + .write32 = arctic_mtd_write32, + .copy_to = arctic_mtd_copy_to, +}; + +static struct mtd_info *arctic_mtd; + +static struct mtd_partition arctic_partitions[PARTITIONS] = { + { .name = "Filesystem", + .size = FFS1_SIZE, + .offset = 0,}, + { .name = "Kernel", + .size = KERNEL_SIZE, + .offset = FFS1_SIZE,}, + { .name = "Filesystem", + .size = FFS2_SIZE, + .offset = FFS1_SIZE + KERNEL_SIZE,}, + { .name = "Firmware", + .size = FIRMWARE_SIZE, + .offset = SUBZERO_BOOTFLASH_SIZE - FIRMWARE_SIZE,}, +}; + +static int __init +init_arctic_mtd(void) +{ + printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); + + arctic_mtd_map.map_priv_1 = (unsigned long) ioremap(PADDR, SIZE); + + if (!arctic_mtd_map.map_priv_1) { + printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); + return -EIO; + } + + printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); + arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map); + + if (!arctic_mtd) + return -ENXIO; + + arctic_mtd->module = THIS_MODULE; + + return add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS); +} + +static void __exit +cleanup_arctic_mtd(void) +{ + if (arctic_mtd) { + del_mtd_partitions(arctic_mtd); + map_destroy(arctic_mtd); + iounmap((void *) arctic_mtd_map.map_priv_1); + } +} + +module_init(init_arctic_mtd); +module_exit(cleanup_arctic_mtd); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Gibson "); +MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Arctic boards"); diff -urN -X bkexcl linux-2.4.22/drivers/mtd/maps/beech-mtd.c linuxppc-2.4/drivers/mtd/maps/beech-mtd.c --- linux-2.4.22/drivers/mtd/maps/beech-mtd.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/mtd/maps/beech-mtd.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,164 @@ +/* + * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for + * IBM 405LP Beech boards. + * + * 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 + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * Bishop Brock + * IBM Research, Austin Center for Low-Power Computing + * bcbrock@us.ibm.com + * March 2002 + * + */ + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#define NAME "Beech Linux Flash" +#define PADDR BEECH_BIGFLASH_PADDR +#define SIZE BEECH_BIGFLASH_SIZE +#define BUSWIDTH 1 + +/* Flash memories on these boards are memory resources, accessed big-endian. */ + +static __u8 +beech_mtd_read8(struct map_info *map, unsigned long offset) +{ + return __raw_readb(map->map_priv_1 + offset); +} + +static __u16 +beech_mtd_read16(struct map_info *map, unsigned long offset) +{ + return __raw_readw(map->map_priv_1 + offset); +} + +static __u32 +beech_mtd_read32(struct map_info *map, unsigned long offset) +{ + return __raw_readl(map->map_priv_1 + offset); +} + +static void +beech_mtd_copy_from(struct map_info *map, + void *to, unsigned long from, ssize_t len) +{ + memcpy(to, (void *) (map->map_priv_1 + from), len); +} + +static void +beech_mtd_write8(struct map_info *map, __u8 data, unsigned long address) +{ + __raw_writeb(data, map->map_priv_1 + address); + mb(); +} + +static void +beech_mtd_write16(struct map_info *map, __u16 data, unsigned long address) +{ + __raw_writew(data, map->map_priv_1 + address); + mb(); +} + +static void +beech_mtd_write32(struct map_info *map, __u32 data, unsigned long address) +{ + __raw_writel(data, map->map_priv_1 + address); + mb(); +} + +static void +beech_mtd_copy_to(struct map_info *map, + unsigned long to, const void *from, ssize_t len) +{ + memcpy((void *) (map->map_priv_1 + to), from, len); +} + +static struct map_info beech_mtd_map = { + name:NAME, + size:SIZE, + buswidth:BUSWIDTH, + read8:beech_mtd_read8, + read16:beech_mtd_read16, + read32:beech_mtd_read32, + copy_from:beech_mtd_copy_from, + write8:beech_mtd_write8, + write16:beech_mtd_write16, + write32:beech_mtd_write32, + copy_to:beech_mtd_copy_to +}; + +static struct mtd_info *beech_mtd; + +static struct mtd_partition beech_partitions[2] = { + { + name:"Linux Kernel", + size:BEECH_KERNEL_SIZE, + offset:BEECH_KERNEL_OFFSET}, { + name: "Free Area", + size: BEECH_FREE_AREA_SIZE, + offset: BEECH_FREE_AREA_OFFSET} +}; + +static int __init +init_beech_mtd(void) +{ + printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); + + beech_mtd_map.map_priv_1 = (unsigned long) ioremap(PADDR, SIZE); + + if (!beech_mtd_map.map_priv_1) { + printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); + return -EIO; + } + + printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); + beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map); + + if (!beech_mtd) + return -ENXIO; + + beech_mtd->module = THIS_MODULE; + + return add_mtd_partitions(beech_mtd, beech_partitions, 2); +} + +static void __exit +cleanup_beech_mtd(void) +{ + if (beech_mtd) { + del_mtd_partitions(beech_mtd); + map_destroy(beech_mtd); + iounmap((void *) beech_mtd_map.map_priv_1); + } +} + +module_init(init_beech_mtd); +module_exit(cleanup_beech_mtd); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bishop Brock, bcbrock@us.ibm.com"); +MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Beech boards"); diff -urN -X bkexcl linux-2.4.22/drivers/mtd/maps/ebony.c linuxppc-2.4/drivers/mtd/maps/ebony.c --- linux-2.4.22/drivers/mtd/maps/ebony.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/mtd/maps/ebony.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,210 @@ +/* + * Mapping for Ebony user flash + * + * Matt Porter + * + * Copyright 2002 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mtd_info *flash; + +static __u8 ebony_read8(struct map_info *map, unsigned long ofs) +{ + return __raw_readb(map->map_priv_1 + ofs); +} + +static __u16 ebony_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(map->map_priv_1 + ofs); +} + +static __u32 ebony_read32(struct map_info *map, unsigned long ofs) +{ + return __raw_readl(map->map_priv_1 + ofs); +} + +static void ebony_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->map_priv_1 + from, len); +} + +static void ebony_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + __raw_writeb(d, map->map_priv_1 + adr); + mb(); +} + +static void ebony_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + __raw_writew(d, map->map_priv_1 + adr); + mb(); +} + +static void ebony_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + __raw_writel(d, map->map_priv_1 + adr); + mb(); +} + +static void ebony_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 ebony_small_map = { + name: "Ebony small flash", + size: EBONY_SMALL_FLASH_SIZE, + buswidth: 1, + read8: ebony_read8, + read16: ebony_read16, + read32: ebony_read32, + copy_from: ebony_copy_from, + write8: ebony_write8, + write16: ebony_write16, + write32: ebony_write32, + copy_to: ebony_copy_to, +}; + +static struct map_info ebony_large_map = { + name: "Ebony large flash", + size: EBONY_LARGE_FLASH_SIZE, + buswidth: 1, + read8: ebony_read8, + read16: ebony_read16, + read32: ebony_read32, + copy_from: ebony_copy_from, + write8: ebony_write8, + write16: ebony_write16, + write32: ebony_write32, + copy_to: ebony_copy_to, +}; + +static struct mtd_partition ebony_small_partitions[] = { + { + name: "OpenBIOS", + offset: 0x0, + size: 0x80000, + } +}; + +static struct mtd_partition ebony_large_partitions[] = { + { + name: "fs", + offset: 0, + size: 0x380000, + }, + { + name: "firmware", + offset: 0x380000, + size: 0x80000, + } +}; + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +int __init init_ebony(void) +{ + u8 fpga0_reg; + unsigned long long small_flash_base, large_flash_base; + + fpga0_reg = readb(ioremap64(EBONY_FPGA_ADDR, 16)); + + if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && + !EBONY_FLASH_SEL(fpga0_reg)) + small_flash_base = EBONY_SMALL_FLASH_HIGH2; + else if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && + EBONY_FLASH_SEL(fpga0_reg)) + small_flash_base = EBONY_SMALL_FLASH_HIGH1; + else if (!EBONY_BOOT_SMALL_FLASH(fpga0_reg) && + !EBONY_FLASH_SEL(fpga0_reg)) + small_flash_base = EBONY_SMALL_FLASH_LOW2; + else + small_flash_base = EBONY_SMALL_FLASH_LOW1; + + if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && + !EBONY_ONBRD_FLASH_EN(fpga0_reg)) + large_flash_base = EBONY_LARGE_FLASH_LOW; + else + large_flash_base = EBONY_LARGE_FLASH_HIGH; + + ebony_small_map.map_priv_1 = + (unsigned long)ioremap64(small_flash_base, + ebony_small_map.size); + + if (!ebony_small_map.map_priv_1) { + printk("Failed to ioremap flash\n"); + return -EIO; + } + + flash = do_map_probe("map_rom", &ebony_small_map); + if (flash) { + flash->module = THIS_MODULE; + add_mtd_partitions(flash, ebony_small_partitions, + NB_OF(ebony_small_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + ebony_large_map.map_priv_1 = + (unsigned long)ioremap64(large_flash_base, + ebony_large_map.size); + + if (!ebony_large_map.map_priv_1) { + printk("Failed to ioremap flash\n"); + return -EIO; + } + + flash = do_map_probe("cfi_probe", &ebony_large_map); + if (flash) { + flash->module = THIS_MODULE; + add_mtd_partitions(flash, ebony_large_partitions, + NB_OF(ebony_large_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + return 0; +} + +static void __exit cleanup_ebony(void) +{ + if (flash) { + del_mtd_partitions(flash); + map_destroy(flash); + } + + if (ebony_small_map.map_priv_1) { + iounmap((void *)ebony_small_map.map_priv_1); + ebony_small_map.map_priv_1 = 0; + } + + if (ebony_large_map.map_priv_1) { + iounmap((void *)ebony_large_map.map_priv_1); + ebony_large_map.map_priv_1 = 0; + } +} + +module_init(init_ebony); +module_exit(cleanup_ebony); + + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Ebony flash map"); diff -urN -X bkexcl linux-2.4.22/drivers/net/Config.in linuxppc-2.4/drivers/net/Config.in --- linux-2.4.22/drivers/net/Config.in 2003-08-26 08:31:47.000000000 +1000 +++ linuxppc-2.4/drivers/net/Config.in 2003-08-28 09:03:34.000000000 +1000 @@ -41,10 +41,17 @@ fi dep_tristate ' BMAC (G3 ethernet) support' CONFIG_BMAC $CONFIG_ALL_PPC dep_tristate ' GMAC (G4/iBook ethernet) support (OBSOLETE, use Sun GEM)' CONFIG_GMAC $CONFIG_ALL_PPC - if [ "$CONFIG_4xx" = "y" ]; then - if [ "$CONFIG_STB03xxx" = "y" -o "$CONFIG_403GCX" = "y" ]; then - tristate ' National DP83902AV (Oak ethernet) support' CONFIG_OAKNET - fi + if [ "$CONFIG_REDWOOD_4" = "y" -o "$CONFIG_403GCX" = "y" ]; then + tristate ' National DP83902AV (Oak ethernet) support' CONFIG_OAKNET + fi + if [ "$CONFIG_BEECH" = "y" ]; then + tristate ' Beech onboard CS8900A Ethernet support' CONFIG_CS89x0 + fi + if [ "$CONFIG_ARCTIC2" = "y" ]; then + tristate ' Arctic-II debug sled ethernet support' CONFIG_ARCTIC_ENET + fi + if [ "$CONFIG_XILINX_OCP" = "y" ]; then + tristate ' Xilinx on-chip ethernet' CONFIG_XILINX_ENET fi fi if [ "$CONFIG_ZORRO" = "y" ]; then @@ -278,6 +285,11 @@ fi if [ "$CONFIG_4xx" = "y" ]; then + mainmenu_option next_comment + comment 'Backplane Networking' + dep_tristate 'IBM NPNet NP4GS3 Backplane Networking' CONFIG_NPNET $CONFIG_PCI + endmenu + source drivers/net/ibm_emac/Config.in fi diff -urN -X bkexcl linux-2.4.22/drivers/net/Makefile linuxppc-2.4/drivers/net/Makefile --- linux-2.4.22/drivers/net/Makefile 2003-07-30 23:33:24.000000000 +1000 +++ linuxppc-2.4/drivers/net/Makefile 2003-08-05 09:59:45.000000000 +1000 @@ -152,6 +152,7 @@ obj-$(CONFIG_NE3210) += ne3210.o 8390.o obj-$(CONFIG_NET_SB1250_MAC) += sb1250-mac.o obj-$(CONFIG_B44) += b44.o +obj-$(CONFIG_ARCTIC_ENET) += arctic_enet.o 8390.o obj-$(CONFIG_PPP) += ppp_generic.o slhc.o obj-$(CONFIG_PPP_ASYNC) += ppp_async.o @@ -239,6 +240,10 @@ obj-$(CONFIG_R8169) += r8169.o obj-$(CONFIG_AMD8111_ETH) += amd8111e.o mii.o +mod-subdirs += xilinx_enet +subdir-$(CONFIG_XILINX_ENET) += xilinx_enet +obj-$(CONFIG_XILINX_ENET) += xilinx_enet/xilinx_enet.o + # non-drivers/net drivers who want mii lib obj-$(CONFIG_PCMCIA_SMC91C92) += mii.o diff -urN -X bkexcl linux-2.4.22/drivers/net/arctic_enet.c linuxppc-2.4/drivers/net/arctic_enet.c --- linux-2.4.22/drivers/net/arctic_enet.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/net/arctic_enet.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,652 @@ +/* + * IPE405 (IBM IAP 405 chip evaluation board) Debug Support Board + * Ehernet Driver + * (C) Copyright 2001 by S.nishino (jl04348@jp.ibm.com) IBM-Japan + * + * ---------- Strategy ---------- + * + * This NIC is RTL8019AS, simply connected to External Bus Controller + * of IAP 405 chip. As many folks of 8390 based NIC, 8390 core driver + * is usable. luckily, the following driver is already available for + * Amiga zorro bus (however I don't know this architecture beyond + * below), this is modified based on this driver (ariadne2). + * + * ---------- original header ---------- + * Amiga Linux/m68k Ariadne II Ethernet Driver + * + * (C) Copyright 1998 by some Elitist 680x0 Users(TM) + * + * --------------------------------------------------------------------------- + * + * This program is based on all the other NE2000 drivers for Linux + * + * --------------------------------------------------------------------------- + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of the Linux + * distribution for more details. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#if defined(CONFIG_ARCTIC2) +#include +#else +#error The driver only works on Arctic +#endif + +#include "8390.h" + + +#define ARCTIC_ENET_BASE8 (ARCTIC2_FPGA8_PADDR + 256*1024) +#define ARCTIC_ENET_BASE16 (ARCTIC2_FPGA16_PADDR + 0) + +#define ARCTIC_ENET_IOBASE 0x0300 /* io base offset from NIC region */ + +#define ARCTIC_ENET_IRQ 29 /* irq number in UIC */ +#define ARCTIC_ENET_IRQ_MASK (0x80000000 >> ARCTIC_ENET_IRQ) + +#define NE_BASE (ARCTIC_ENET_BASE8 + ARCTIC_ENET_IOBASE) +#define NE_BASE16 (ARCTIC_ENET_BASE16 + ARCTIC_ENET_IOBASE) + +/* 8390 register address */ +#define NE_CMD (0x00) +#define NE_DATAPORT (0x10) /* NatSemi-defined port window offset. */ +#define NE_DATAPORT16 (NE_DATAPORT / sizeof(u16)) +#define NE_RESET (0x1f) /* Issue a read to reset, a write to clear. */ +#define NE_IO_EXTENT (0x20) /* region extent */ + +#define NE_EN0_ISR (0x07) +#define NE_EN0_DCFG (0x0e) + +#define NE_EN0_RSARLO (0x08) +#define NE_EN0_RSARHI (0x09) +#define NE_EN0_RCNTLO (0x0a) +#define NE_EN0_RXCR (0x0c) +#define NE_EN0_TXCR (0x0d) +#define NE_EN0_RCNTHI (0x0b) +#define NE_EN0_IMR (0x0f) + +/* 8390 packet buffer page number */ +#define NESM_START_PG 0x40 /* First page of TX buffer */ +#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */ + +static u8 *iobase8; +static u16 *iobase16; + +static int arctic_enet_probe(struct net_device *dev); +static int arctic_enet_init(struct net_device *dev); + +static int arctic_enet_open(struct net_device *dev); +static int arctic_enet_close(struct net_device *dev); + +static void arctic_enet_reset_8390(struct net_device *dev); +static void arctic_enet_get_8390_hdr(struct net_device *dev, + struct e8390_pkt_hdr *hdr, + int ring_page); +static void arctic_enet_block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset); +static void arctic_enet_block_output(struct net_device *dev, + const int count, + const unsigned char *buf, + const int start_page); + +/* These macros will do something on Arctic-I if we ever add support + * for it back in */ +#define switch_16bit_bank() do { } while (0) +#define switch_8bit_bank() do { } while (0) + +void p_dump(unsigned char *p, int sz) +{ + int i; + unsigned char *wp; + + wp = p; + + printk("------ PACKET START : %d Bytes ------ \n", sz); + + for (i = 0; i < sz; i++) { + if (i % 16 == 0) { + printk("\n %04X: %02X ", i, *wp); + } else if (i % 16 == 15) { + printk("%02X", *wp); + } else { + printk("%02X ", *wp); + } + wp++; + } + + printk("------ PACKET END ------ \n"); +} + +/* Code for reading the MAC address from the Arctic ethernet based on + * similar code in PIBS */ + +static void __init writereg_9346(volatile u8 *iobase, u8 value) +{ + /* Switch to register page 3 */ + writeb(readb(iobase + NE_CMD) | 0xc0, iobase + NE_CMD); + writeb(value, iobase + 0x01); +} + +static u8 __init readreg_9346(volatile u8 *iobase) +{ + /* Switch to register page 3 */ + writeb(readb(iobase + NE_CMD) | 0xc0, iobase + NE_CMD); + return readb(iobase + 0x01); +} + +static void __init write_bit_9346(volatile u8 *iobase, u8 bit) +{ + u8 mask = ~0x06; + + writereg_9346(iobase, (readreg_9346(iobase) & mask) | bit); + udelay(1000); + writereg_9346(iobase, (readreg_9346(iobase) & mask) | bit | 0x04); + udelay(1000); +} + +static u8 __init read_bit_9346(volatile u8 *iobase) +{ + u8 bit; + u8 mask = ~0x05; + + mask = ~0x05; + writereg_9346(iobase, readreg_9346(iobase) & mask); + udelay(1000); + writereg_9346(iobase, (readreg_9346(iobase) & mask) | 0x04); + bit = readreg_9346(iobase) & 0x01; + udelay(1000); + + return bit; +} + +static u16 __init arctic_read_9346(volatile u8 *iobase, unsigned long addr) +{ + unsigned long flags; + int i; + u16 data; + + local_irq_save(flags); + + /* Put the chip into 8390 programming mode */ + writereg_9346(iobase, (readreg_9346(iobase) & ~0xc0) | 0x80); + udelay(1000); + + /* Send command (read 16-bit value) to EEPROM */ + /* Bring CS Low */ + writereg_9346(iobase, readreg_9346(iobase) & ~0x0f); + udelay(1000); + /* Bring CS High */ + writereg_9346(iobase, (readreg_9346(iobase) & ~0x0f) | 0x08); + udelay(1000); + + /* Send a 1 */ + write_bit_9346(iobase, 0x02); + /* Send opcode 0b10 */ + write_bit_9346(iobase, 0x02); + write_bit_9346(iobase, 0x00); + /* Send address to read */ + for (i = 0; i < 6; i++) { + if (addr & 0x20) + write_bit_9346(iobase, 0x02); + else + write_bit_9346(iobase, 0x00); + addr <<= 1; + } + + /* Read the value back, bit by bit */ + data = 0; + for (i = 0; i < 16; i++) { + data <<= 1; + if (read_bit_9346(iobase)) + data |= 0x1; + } + + /* Bring CS Low */ + writereg_9346(iobase, readreg_9346(iobase) & ~0x0f); + udelay(1000); + /* Bring the chip out of 8390 programming mode */ + writereg_9346(iobase, readreg_9346(iobase) & ~0xc0); + udelay(1000); + + /* Return to register page 0 */ + writeb(readb(iobase + NE_CMD) & ~0xc0, iobase + NE_CMD); + udelay(1000); + + local_irq_restore(flags); + + return data; +} + +static void __init arctic_get_macaddr(struct net_device *dev) +{ + u16 t0, t1, t2, v0, v1; + + t0 = arctic_read_9346(iobase8, 0); + t1 = arctic_read_9346(iobase8, 2); + t2 = arctic_read_9346(iobase8, 4); + v0 = arctic_read_9346(iobase8, 6); + v1 = arctic_read_9346(iobase8, 8); + + if ( (v0 != 0x4d50) || (v1 != 0x5400) ) { + printk(KERN_WARNING "%s: MAC address is not set in EEPROM\n", dev->name); + return; + } + + dev->dev_addr[0] = t0 >> 8; + dev->dev_addr[1] = t0 & 0xff; + dev->dev_addr[2] = t1 >> 8; + dev->dev_addr[3] = t1 & 0xff; + dev->dev_addr[4] = t2 >> 8; + dev->dev_addr[5] = t2 & 0xff; +} + +int __init arctic_enet_probe(struct net_device *dev) +{ + unsigned long reset_start_time; + + switch_8bit_bank(); + /* Reset card. Who knows what dain-bramaged state it was left in. */ + reset_start_time = jiffies; + + writeb(readb(iobase8 + NE_RESET), iobase8 + NE_RESET); + + while ((readb(iobase8 + NE_EN0_ISR) & ENISR_RESET) == 0) + if (jiffies - reset_start_time > 2 * HZ / 100) { + printk("arctic_enet: not found (no reset ack).\n"); + return -ENODEV; + } + + writeb(0xff, iobase8 + NE_EN0_ISR); /* Ack all intr. */ + + arctic_get_macaddr(dev); + + printk("arctic_enet: found at 0x%08x/0x%08x, MAC address " + "%02x:%02x:%02x:%02x:%02x:%02x\n", + NE_BASE, NE_BASE16, + dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], + dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]); + + /* Hack to let 8390.c work properly - it assumes IO space + * addresses */ + dev->base_addr = (unsigned long)iobase8 - _IO_BASE; + dev->irq = ARCTIC_ENET_IRQ; + + return 0; +} + +static int __init arctic_enet_init(struct net_device *dev) +{ + static u32 arctic_enet_offsets[16] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + }; + + /* Since this irq is connected to uic as edge interrupt, its pending must be cleared. */ + /* FIXME: it would be nice to get rid of the direct reference + * to the 4xx irq structure */ + ppc4xx_pic->ack(dev->irq); + + /* Install the Interrupt handler */ + if (request_irq(dev->irq, ei_interrupt, SA_SHIRQ, dev->name, dev)) + return -EAGAIN; + + /* Allocate dev->priv and fill in 8390 specific dev fields. */ + if (ethdev_init(dev)) { + printk(" Unable to get memory for dev->priv.\n"); + return -ENOMEM; + } + + /* + * Fill 8390 specific member for 8390 core driver + */ + ei_status.name = "RTL8019AS"; + ei_status.tx_start_page = NESM_START_PG; + ei_status.stop_page = NESM_STOP_PG; + ei_status.word16 = 1; + ei_status.rx_start_page = NESM_START_PG + TX_PAGES; + + ei_status.reset_8390 = &arctic_enet_reset_8390; + ei_status.block_input = &arctic_enet_block_input; + ei_status.block_output = &arctic_enet_block_output; + ei_status.get_8390_hdr = &arctic_enet_get_8390_hdr; + ei_status.reg_offset = arctic_enet_offsets; + + NS8390_init(dev, 0); + return 0; +} + +static int arctic_enet_open(struct net_device *dev) +{ + int err; + err = ei_open(dev); + if (err) + return err; + + MOD_INC_USE_COUNT; + return 0; +} + +static int arctic_enet_close(struct net_device *dev) +{ + int err; + + err = ei_close(dev); + if (err) + return err; + + MOD_DEC_USE_COUNT; + return 0; +} + +/* Hard reset the card. This used to pause for the same period that a + 8390 reset command required, but that shouldn't be necessary. */ +static void arctic_enet_reset_8390(struct net_device *dev) +{ + unsigned long reset_start_time = jiffies; + + if (ei_debug > 1) + printk("resetting the 8390 t=%ld...", jiffies); + + writeb(readb(iobase8 + NE_RESET), iobase8 + NE_RESET); + + ei_status.txing = 0; + ei_status.dmaing = 0; + + /* This check _should_not_ be necessary, omit eventually. */ + while ((readb(iobase8 + NE_EN0_ISR) & ENISR_RESET) == 0) + if (jiffies - reset_start_time > 2 * HZ / 100) { + printk("%s: ne_reset_8390() did not complete.\n", + dev->name); + break; + } + writeb(ENISR_RESET, iobase8 + NE_EN0_ISR); /* Ack intr. */ +} + +/* Grab the 8390 specific header. Similar to the block_input routine, but + we don't need to be concerned with ring wrap as the header will be at + the start of a page, so we optimize accordingly. */ + +static void arctic_enet_get_8390_hdr(struct net_device *dev, + struct e8390_pkt_hdr *hdr, + int ring_page) +{ + int cnt; + u16 *ptrs; + unsigned char *ptrc; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne_get_8390_hdr " + "[DMAstat:%d][irqlock:%d].\n", dev->name, + ei_status.dmaing, ei_status.irqlock); + return; + } + + ei_status.dmaing |= 0x01; + writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, iobase8 + NE_CMD); + writeb(ENISR_RDC, iobase8 + NE_EN0_ISR); + writeb(sizeof(struct e8390_pkt_hdr), iobase8 + NE_EN0_RCNTLO); + writeb(0, iobase8 + NE_EN0_RCNTHI); + writeb(0, iobase8 + NE_EN0_RSARLO); /* On page boundary */ + writeb(ring_page, iobase8 + NE_EN0_RSARHI); + writeb(E8390_RREAD + E8390_START, iobase8 + NE_CMD); + + if (ei_status.word16) { + switch_16bit_bank(); + ptrs = (u16 *) hdr; + for (cnt = 0; cnt < (sizeof(struct e8390_pkt_hdr) >> 1); + cnt++) + *ptrs++ = in_be16((u16 *) (iobase16 + NE_DATAPORT16)); + switch_8bit_bank(); + } else { + + ptrc = (unsigned char *) hdr; + for (cnt = 0; cnt < sizeof(struct e8390_pkt_hdr); cnt++) + *ptrc++ = readb(iobase8 + NE_DATAPORT); + } + + + writeb(ENISR_RDC, iobase8 + NE_EN0_ISR); /* Ack intr. */ + + /* I am Big Endian, but received byte count is Little Endian. */ + hdr->count = le16_to_cpu(hdr->count); + + ei_status.dmaing &= ~0x01; +} + +/* Block input and output, similar to the Crynwr packet driver. If you + are porting to a new ethercard, look at the packet driver source for hints. + The NEx000 doesn't share the on-board packet memory -- you have to put + the packet out through the "remote DMA" dataport using writeb. */ + +static void arctic_enet_block_input(struct net_device *dev, int count, + struct sk_buff *skb, int ring_offset) +{ + char *buf = skb->data; + u16 *ptrs; + unsigned char *ptrc; + + int cnt; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne_block_input " + "[DMAstat:%d][irqlock:%d].\n", + dev->name, ei_status.dmaing, ei_status.irqlock); + return; + } + ei_status.dmaing |= 0x01; + writeb(E8390_NODMA + E8390_PAGE0 + E8390_START, iobase8 + NE_CMD); + writeb(ENISR_RDC, iobase8 + NE_EN0_ISR); + writeb(count & 0xff, iobase8 + NE_EN0_RCNTLO); + writeb(count >> 8, iobase8 + NE_EN0_RCNTHI); + writeb(ring_offset & 0xff, iobase8 + NE_EN0_RSARLO); + writeb(ring_offset >> 8, iobase8 + NE_EN0_RSARHI); + writeb(E8390_RREAD + E8390_START, iobase8 + NE_CMD); + + + if (ei_status.word16) { + + switch_16bit_bank(); + + ptrs = (u16 *) buf; + for (cnt = 0; cnt < (count >> 1); cnt++) + /* At 16 bits mode, bus acts as Little Endian mode + That's swap is needed ??? */ + *ptrs++ = in_be16((u16 *) (iobase16 + NE_DATAPORT16)); + switch_8bit_bank(); + + if (count & 0x01) + buf[count - 1] = readb(iobase8 + NE_DATAPORT); + + } else { + + + ptrc = (unsigned char *) buf; + for (cnt = 0; cnt < count; cnt++) + *ptrc++ = readb(iobase8 + NE_DATAPORT); + } + + writeb(ENISR_RDC, iobase8 + NE_EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; +} + +static void arctic_enet_block_output(struct net_device *dev, int count, + const unsigned char *buf, + const int start_page) +{ + unsigned long dma_start; + u16 *ptrs; + unsigned char *ptrc; + int cnt; + + /* Round the count up for word writes. Do we need to do this? + What effect will an odd byte count have on the 8390? + I should check someday. */ + if (count & 0x01) + count++; + + /* This *shouldn't* happen. If it does, it's the last thing you'll see */ + if (ei_status.dmaing) { + printk("%s: DMAing conflict in ne_block_output." + "[DMAstat:%d][irqlock:%d]\n", dev->name, + ei_status.dmaing, ei_status.irqlock); + return; + } + ei_status.dmaing |= 0x01; + +#if 1 /* FIXME: not sure what this is for -dwg */ + writeb(0x42, iobase8 + EN0_RCNTLO); + writeb(0x00, iobase8 + EN0_RCNTHI); + writeb(0x42, iobase8 + EN0_RSARLO); + writeb(0x00, iobase8 + EN0_RSARHI); +#endif + /* We should already be in page 0, but to be safe... */ + writeb(E8390_PAGE0 + E8390_START + E8390_NODMA, iobase8 + NE_CMD); + + writeb(ENISR_RDC, iobase8 + NE_EN0_ISR); + + /* Now the normal output. */ + writeb(count & 0xff, iobase8 + NE_EN0_RCNTLO); + writeb(count >> 8, iobase8 + NE_EN0_RCNTHI); + writeb(0x00, iobase8 + NE_EN0_RSARLO); + writeb(start_page, iobase8 + NE_EN0_RSARHI); + + writeb(E8390_RWRITE + E8390_START, iobase8 + NE_CMD); + + if (ei_status.word16) { + switch_16bit_bank(); + + ptrs = (u16 *) buf; + for (cnt = 0; cnt < count >> 1; cnt++) { + /* At 16 bits mode, bus acts as Little Endian mode + That's swap is needed ??? */ + out_be16((u16 *) (iobase16 + NE_DATAPORT16), + *ptrs); + ptrs++; + } + + switch_8bit_bank(); + + } else { + ptrc = (unsigned char *) buf; + for (cnt = 0; cnt < count; cnt++) + writeb(*ptrc++, iobase8 + NE_DATAPORT); + } + + dma_start = jiffies; + + while ((readb(iobase8 + NE_EN0_ISR) & ENISR_RDC) == 0) + if (jiffies - dma_start > 2 * HZ / 100) { /* 20ms */ + printk("%s: timeout waiting for Tx RDC.\n", + dev->name); + arctic_enet_reset_8390(dev); + NS8390_init(dev, 1); + break; + } + + writeb(ENISR_RDC, iobase8 + NE_EN0_ISR); /* Ack intr. */ + ei_status.dmaing &= ~0x01; + return; +} + +static struct net_device arctic_enet_dev = { + .init = arctic_enet_init, + .open = arctic_enet_open, + .stop = arctic_enet_close, +}; + +int init_arctic_enet(void) +{ + struct net_device *dev = &arctic_enet_dev; + int rsvd8 = 0; + int rsvd16 = 0; + int err; + + /* First set up our IO regions */ + if (! request_mem_region(NE_BASE, NE_IO_EXTENT, "arctic_enet")) + goto fail; + rsvd8 = 1; + + iobase8 = ioremap(NE_BASE, NE_IO_EXTENT); + if (! iobase8) { + err = -EBUSY; + goto fail; + } + + if (NE_BASE16 != NE_BASE) { + if (! request_mem_region(NE_BASE16, NE_IO_EXTENT, "arctic_enet")) + goto fail; + rsvd16 = 1; + } + + iobase16 = ioremap(NE_BASE16, NE_IO_EXTENT); + if (! iobase16) { + err = -EBUSY; + goto fail; + } + + /* Configure IRQ */ + cli(); + mtdcr(DCRN_UIC0_TR, mfdcr(DCRN_UIC0_TR) | ARCTIC_ENET_IRQ_MASK); + mtdcr(DCRN_UIC0_PR, mfdcr(DCRN_UIC0_PR) | ARCTIC_ENET_IRQ_MASK); + mtdcr(DCRN_UIC0_SR, ARCTIC_ENET_IRQ_MASK); + sti(); + + err = arctic_enet_probe(dev); + if (err) { + printk(KERN_ERR "arctic_enet: No Arctic ethernet card found.\n"); + goto fail; + } + + err = register_netdev(dev); + if (err) + goto fail; + + return 0; + + fail: + if (iobase16) + iounmap(iobase16); + if (rsvd16) + release_mem_region(NE_BASE16, NE_IO_EXTENT); + if (iobase8) + iounmap(iobase8); + if (rsvd8) + release_mem_region(NE_BASE, NE_IO_EXTENT); + + return err; + +} + +void remove_arctic_enet(void) +{ + unregister_netdev(&arctic_enet_dev); + free_irq(ARCTIC_ENET_IRQ, &arctic_enet_dev); + + if (iobase16) { + iounmap(iobase16); + release_mem_region(NE_BASE16, NE_IO_EXTENT); + } + if (iobase8) { + iounmap(iobase8); + release_mem_region(NE_BASE, NE_IO_EXTENT); + } +} + +module_init(init_arctic_enet); +module_exit(remove_arctic_enet); diff -urN -X bkexcl linux-2.4.22/drivers/net/cs89x0.c linuxppc-2.4/drivers/net/cs89x0.c --- linux-2.4.22/drivers/net/cs89x0.c 2003-06-25 20:56:52.000000000 +1000 +++ linuxppc-2.4/drivers/net/cs89x0.c 2003-06-25 21:30:08.000000000 +1000 @@ -98,7 +98,11 @@ * Note that even if DMA is turned off we still support the 'dma' and 'use_dma' * module options so we don't break any startup scripts. */ +#ifdef CONFIG_BEECH /* IBM 405LP/Beech does not have external DMA */ +#define ALLOW_DMA 0 +#else #define ALLOW_DMA 1 +#endif /* * Set this to zero to remove all the debug statements via @@ -142,7 +146,7 @@ #include "cs89x0.h" static char version[] __initdata = -"cs89x0.c: v2.4.3-pre1 Russell Nelson , Andrew Morton \n"; +"cs89x0.c: v2.4.3-pre1 Russell Nelson , Andrew Morton , Modified by Ralph Blach for the Earl\n"; /* First, a few definitions that the brave might change. A zero-terminated list of I/O addresses to be probed. Some special flags.. @@ -163,6 +167,19 @@ static unsigned int netcard_portlist[] __initdata = { 0x0300, 0}; static unsigned int cs8900_irq_map[] = {1,0,0,0}; +#elif defined (CONFIG_BEECH) +static unsigned int netcard_portlist[] __initdata = +{ 0 /* filled in with mapped virtual addr later */, 0}; +static unsigned int cs8900_irq_map[] = {UIC_IRQ_EIR4,0,0,0}; + +/* + * Undo PCMCIA definition and replace with zero for no offset from the + * ioremapped address. + */ + +#undef _IO_BASE +#define _IO_BASE 0 + #else static unsigned int netcard_portlist[] __initdata = { 0x300, 0x320, 0x340, 0x360, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0}; @@ -292,11 +309,22 @@ else if (base_addr != 0) /* Don't probe at all. */ return -ENXIO; +#ifdef CONFIG_BEECH + netcard_portlist[0] = + (int) ioremap(BEECH_ETHERNET_PADDR, BEECH_ETHERNET_SIZE) + + 0x301; +#endif + for (i = 0; netcard_portlist[i]; i++) { if (cs89x0_probe1(dev, netcard_portlist[i]) == 0) return 0; } printk(KERN_WARNING "cs89x0: no cs8900 or cs8920 detected. Be sure to disable PnP with SETUP\n"); + +#ifdef CONFIG_BEECH + iounmap(netcard_portlist[0]); +#endif + return -ENODEV; } @@ -476,7 +504,14 @@ lp->chip_revision, dev->base_addr); +#ifdef CONFIG_BEECH + /* + * Leave firmware settings alone, Beech does not currently provide + * a means of retrieving MAC address, etc. known only to f/w. + */ +#else reset_chip(dev); +#endif /* Here we read the current configuration of the chip. If there is no Extended EEPROM then the idea is to not disturb the chip @@ -519,6 +554,15 @@ if ((readreg(dev, PP_SelfST) & (EEPROM_OK | EEPROM_PRESENT)) == (EEPROM_OK|EEPROM_PRESENT)) { + printk( "[Cirrus EEPROM] "); +#ifdef CONFIG_BEECH + } + /* HACK: Beech needs to do this configuration even though it + * doesn't have a Cirrus EEPROM */ + { + printk( "[Beech] "); +#endif + /* Load the MAC. */ for (i=0; i < ETH_ALEN/2; i++) { unsigned int Addr; @@ -563,8 +607,6 @@ /* IRQ. Other chips already probe, see below. */ if (lp->chip_type == CS8900) lp->isa_config = readreg(dev, PP_CS8900_ISAINT) & INT_NO_MASK; - - printk( "[Cirrus EEPROM] "); } printk("\n"); diff -urN -X bkexcl linux-2.4.22/drivers/net/cs89x0.h linuxppc-2.4/drivers/net/cs89x0.h --- linux-2.4.22/drivers/net/cs89x0.h 2002-08-30 14:05:06.000000000 +1000 +++ linuxppc-2.4/drivers/net/cs89x0.h 2003-03-14 20:46:44.000000000 +1100 @@ -439,6 +439,8 @@ #define PNP_IRQ_FRMT 0x0022 /* PNP small item IRQ format */ #ifdef CONFIG_SH_HICOSH4 #define CS8900_IRQ_MAP 0x0002 /* HiCO-SH4 board has its IRQ on #1 */ +#elif defined(CONFIG_BEECH) +#define CS8900_IRQ_MAP 1 << UIC_IRQ_EIR4 #else #define CS8900_IRQ_MAP 0x1c20 /* This IRQ map is fixed */ #endif diff -urN -X bkexcl linux-2.4.22/drivers/net/oaknet.c linuxppc-2.4/drivers/net/oaknet.c --- linux-2.4.22/drivers/net/oaknet.c 2002-02-05 18:52:40.000000000 +1100 +++ linuxppc-2.4/drivers/net/oaknet.c 2003-03-14 20:46:44.000000000 +1100 @@ -12,6 +12,9 @@ * Additional inspiration from the "tcd8390.c" driver from TiVo, Inc. * and "enetLib.c" from IBM. * + * Modified to work on "Redwood 4" board also by someone at + * Montavista software (brad@heeltoe.com) + * */ #include @@ -21,11 +24,70 @@ #include #include -#include #include #include "8390.h" +#ifdef CONFIG_REDWOOD_4 +#define OAKNET_CMD 0x00 +#define OAKNET_DATA 0x20 /* NS-defined port window offset. */ +#define OAKNET_BASE (dev->base_addr) +#define E8390_BASE (dev->base_addr) + + +#undef outb_p +#undef insb +#undef insw + +#define FTR_DELAY_HACK +#ifdef FTR_DELAY_HACK + +static inline void outb_p(unsigned char value, long address) +{ + readb((void *)(address & 0xffffff00) + 0x40); + writeb(value, address); +} + +static inline void ei_obp(unsigned char value, long address) +{ + readb((void *)(address & 0xffffff00) + 0x40); + writeb(value, address); +} + +static inline unsigned char ei_ibp(long address) +{ + readb((void *)(address & 0xffffff00) + 0x40); + return readb(address); +} + +static inline unsigned char ei_ib(long address) +{ + readb((void *)(address & 0xffffff00) + 0x40); + return readb(address); +} + +#else + +#define outb_p writeb + +#define ei_obp writeb +#define ei_ibp readb +#define ei_ib readb + +#endif + + +/* don't add _IO_BASE to port */ + +#define ei_osw _outsw_ns +#define ei_isw _insw_ns +#define ei_osb _outsb +#define ei_isb _insb + +#define insb _insb +#define insw _insw_ns + +#endif /* Preprocessor Defines */ @@ -37,17 +99,23 @@ #define FALSE 0 #endif +#ifdef CONFIG_REDWOOD_4 +#define OAKNET_START_PG 0x40 /* First page of TX buffer */ +#define OAKNET_STOP_PG 0x80 /* Last page +1 of RX ring */ +#else #define OAKNET_START_PG 0x20 /* First page of TX buffer */ -#define OAKNET_STOP_PG 0x40 /* Last pagge +1 of RX ring */ +#define OAKNET_STOP_PG 0x40 /* Last page +1 of RX ring */ +#endif + #define OAKNET_WAIT (2 * HZ / 100) /* 20 ms */ /* Experimenting with some fixes for a broken driver... */ - +#ifndef CONFIG_REDWOOD_4 #define OAKNET_DISINT #define OAKNET_HEADCHECK #define OAKNET_RWFIX - +#endif /* Global Variables */ @@ -58,6 +126,8 @@ /* Function Prototypes */ +int oaknet_probe(struct net_device *dev); +static int oaknet_init(struct net_device *dev); static int oaknet_open(struct net_device *dev); static int oaknet_close(struct net_device *dev); @@ -71,6 +141,23 @@ static void oaknet_dma_error(struct net_device *dev, const char *name); +/* + * Maintain the initial probe as a sub function just in case it is wished + * to have this driver as a module later. + */ +int oaknet_probe_done = 0; + +int +oaknet_probe(struct net_device *dev) +{ + if (oaknet_probe_done) + return -ENODEV; + + oaknet_init(dev); + + oaknet_probe_done = 1; + return 0; +} /* * int oaknet_init() @@ -90,32 +177,25 @@ * 0 if OK, otherwise system error number on error. * */ -static int __init oaknet_init(void) +static int __init oaknet_init(struct net_device *dev) { register int i; int reg0, regd; int ret; - struct net_device tmp, *dev = NULL; -#if 0 unsigned long ioaddr = OAKNET_IO_BASE; -#else - unsigned long ioaddr = ioremap(OAKNET_IO_BASE, OAKNET_IO_SIZE); -#endif bd_t *bip = (bd_t *)__res; if (!ioaddr) return -ENOMEM; - /* - * This MUST happen here because of the nic_* macros - * which have an implicit dependency on dev->base_addr. - */ - tmp.base_addr = ioaddr; - dev = &tmp; + dev->base_addr = ioaddr; - ret = -EBUSY; - if (!request_region(OAKNET_IO_BASE, OAKNET_IO_SIZE, name)) - goto out_unmap; +#if !defined(CONFIG_REDWOOD_4) + if (!request_region(OAKNET_IO_BASE, OAKNET_IO_SIZE, name)) { + dev->base_addr = 0; + return -EBUSY; + } +#endif /* Quick register check to see if the device is really there. */ @@ -130,30 +210,27 @@ */ ei_obp(E8390_NODMA + E8390_PAGE1 + E8390_STOP, ioaddr + E8390_CMD); +#ifdef CONFIG_REDWOOD_4 + regd = ei_ibp(ioaddr + EI_SHIFT(0x0D)); + ei_obp(0xFF, ioaddr + EI_SHIFT(0x0D)); +#else regd = ei_ibp(ioaddr + 0x0D); ei_obp(0xFF, ioaddr + 0x0D); +#endif ei_obp(E8390_NODMA + E8390_PAGE0, ioaddr + E8390_CMD); ei_ibp(ioaddr + EN0_COUNTER0); /* It's no good. Fix things back up and leave. */ - ret = -ENODEV; if (ei_ibp(ioaddr + EN0_COUNTER0) != 0) { ei_obp(reg0, ioaddr); ei_obp(regd, ioaddr + 0x0D); - goto out_region; +#if !defined(CONFIG_REDWOOD_4) + release_region(dev->base_addr, OAKNET_IO_SIZE); +#endif + return (-ENODEV); } - /* - * We're not using the old-style probing API, so we have to allocate - * our own device structure. - */ - - dev = init_etherdev(NULL, 0); - ret = -ENOMEM; - if (!dev) - goto out_region; - SET_MODULE_OWNER(dev); oaknet_devs = dev; /* @@ -166,10 +243,12 @@ /* Allocate 8390-specific device-private area and fields. */ - ret = -ENOMEM; if (ethdev_init(dev)) { printk(" unable to get memory for dev->priv.\n"); - goto out_dev; +#if !defined(CONFIG_REDWOOD_4) + release_region(dev->base_addr, OAKNET_IO_SIZE); +#endif + return (-ENOMEM); } /* @@ -182,11 +261,14 @@ /* Attempt to get the interrupt line */ - ret = -EAGAIN; if (request_irq(dev->irq, ei_interrupt, 0, name, dev)) { printk("%s: unable to request interrupt %d.\n", dev->name, dev->irq); - goto out_priv; + dev->priv = NULL; +#if !defined(CONFIG_REDWOOD_4) + release_region(dev->base_addr, OAKNET_IO_SIZE); +#endif + return (EAGAIN); } /* Tell the world about what and where we've found. */ @@ -196,7 +278,7 @@ dev->dev_addr[i] = bip->bi_enetaddr[i]; printk("%c%.2x", (i ? ':' : ' '), dev->dev_addr[i]); } - printk(", found at %#lx, using IRQ %d.\n", dev->base_addr, dev->irq); + printk(", found at %#lx, irq=%d.\n", dev->base_addr, dev->irq); /* Set up some required driver fields and then we're done. */ @@ -251,7 +333,9 @@ static int oaknet_open(struct net_device *dev) { - int status = ei_open(dev); + int status; + + status = ei_open(dev); return (status); } @@ -300,7 +384,7 @@ static void oaknet_reset_8390(struct net_device *dev) { - int base = E8390_BASE; + int base = E8390_BASE; /* * We have no provision of reseting the controller as is done @@ -360,10 +444,10 @@ outb_p(E8390_RREAD + E8390_START, base + OAKNET_CMD); if (ei_status.word16) - insw(base + OAKNET_DATA, hdr, + insw((u16 *)base + OAKNET_DATA, hdr, sizeof(struct e8390_pkt_hdr) >> 1); else - insb(base + OAKNET_DATA, hdr, + insb((unsigned char *)(base + OAKNET_DATA), hdr, sizeof(struct e8390_pkt_hdr)); /* Byte-swap the packet byte count */ @@ -381,6 +465,12 @@ oaknet_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset) { +#ifdef OAKNET_DISINT + unsigned long flags; +#endif +#ifdef OAKNET_HEADCHECK + int bytes = count; +#endif int base = OAKNET_BASE; char *buf = skb->data; @@ -394,6 +484,10 @@ return; } +#ifdef CONFIG_REDWOOD_4 + dma_cache_wback_inv((long)skb->data, (long)count); +#endif + #ifdef OAKNET_DISINT save_flags(flags); cli(); @@ -407,15 +501,16 @@ ei_obp(ring_offset >> 8, base + EN0_RSARHI); ei_obp(E8390_RREAD + E8390_START, base + E8390_CMD); if (ei_status.word16) { - ei_isw(base + E8390_DATA, buf, count >> 1); + ei_isw((unsigned short *)(base + OAKNET_DATA), buf, count >> 1); + if (count & 0x01) { - buf[count - 1] = ei_ib(base + E8390_DATA); + buf[count - 1] = ei_ib(base + OAKNET_DATA); #ifdef OAKNET_HEADCHECK bytes++; #endif } } else { - ei_isb(base + E8390_DATA, buf, count); + ei_isb((unsigned char *)(base + OAKNET_DATA), buf, count); } #ifdef OAKNET_HEADCHECK /* @@ -437,10 +532,11 @@ if (((ring_offset + bytes) & 0xff) == low) break; } while (--tries > 0); - if (tries <= 0) + if (tries <= 0) { printk("%s: RX transfer address mismatch," "%#4.4x (expected) vs. %#4.4x (actual).\n", dev->name, ring_offset + bytes, addr); + } } #endif ei_obp(ENISR_RDC, base + EN0_ISR); /* ACK Remote DMA interrupt */ @@ -512,6 +608,10 @@ ei_obp(E8390_PAGE0 + E8390_START + E8390_NODMA, base + E8390_CMD); +#ifdef CONFIG_REDWOOD_4 + dma_cache_wback_inv((long)buf, (long)count); +#endif + #ifdef OAKNET_HEADCHECK retry: #endif @@ -581,9 +681,9 @@ ei_obp(E8390_RWRITE + E8390_START, base + E8390_CMD); if (ei_status.word16) { - ei_osw(E8390_BASE + E8390_DATA, buf, count >> 1); + ei_osw((unsigned short *)(base + OAKNET_DATA), buf, count >> 1); } else { - ei_osb(E8390_BASE + E8390_DATA, buf, count); + ei_osb((unsigned char *)(base + OAKNET_DATA), buf, count); } #ifdef OAKNET_DISINT @@ -658,7 +758,7 @@ printk(KERN_EMERG "%s: DMAing conflict in %s." "[DMAstat:%d][irqlock:%d][intr:%ld]\n", dev->name, name, ei_status.dmaing, ei_status.irqlock, - dev->interrupt); + dev->irq); } /* @@ -666,10 +766,18 @@ */ static int __init oaknet_init_module (void) { + struct net_device *dev; + if (oaknet_devs != NULL) return (-EBUSY); - return (oaknet_init()); + dev = init_etherdev(NULL, 0); + if (!dev) + return (-ENOMEM); + + SET_MODULE_OWNER(dev); + + return (oaknet_init(dev)); } /* @@ -681,12 +789,14 @@ return; if (oaknet_devs->priv != NULL) { - int ioaddr = oaknet_devs->base_addr; + unsigned long ioaddr = oaknet_devs->base_addr; void *priv = oaknet_devs->priv; free_irq(oaknet_devs->irq, oaknet_devs); +#if !defined(CONFIG_REDWOOD_4) release_region(ioaddr, OAKNET_IO_SIZE); +#endif iounmap(ioaddr); - unregister_netdev(oaknet_dev); + unregister_netdev(oaknet_devs); kfree(priv); } diff -urN -X bkexcl linux-2.4.22/drivers/net/xilinx_enet/Makefile linuxppc-2.4/drivers/net/xilinx_enet/Makefile --- linux-2.4.22/drivers/net/xilinx_enet/Makefile 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/net/xilinx_enet/Makefile 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,21 @@ +# +# Makefile for the Xilinx ethernet driver +# + +EXTRA_CFLAGS += -I$(TOPDIR)/arch/ppc/platforms/xilinx_ocp + +list-multi := xilinx_enet.o + +# The Linux adapter for the Xilinx driver code. +xilinx_enet-objs += adapter.o + +# The Xilinx OS independent code. +xilinx_enet-objs += xemac.o xemac_g.o xemac_intr.o xemac_intr_dma.o \ + xemac_intr_fifo.o xemac_options.o xemac_phy.o + +obj-$(CONFIG_XILINX_ENET) := xilinx_enet.o + +xilinx_enet.o: $(xilinx_enet-objs) + $(LD) -r -o $@ $(xilinx_enet-objs) + +include $(TOPDIR)/Rules.make diff -urN -X bkexcl linux-2.4.22/drivers/net/xilinx_enet/adapter.c linuxppc-2.4/drivers/net/xilinx_enet/adapter.c --- linux-2.4.22/drivers/net/xilinx_enet/adapter.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/net/xilinx_enet/adapter.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,1064 @@ +/* + * adapter.c + * + * Xilinx Ethernet Adapter component to interface XEmac component to Linux + * + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * 2002 (c) MontaVista, Software, Inc. This file is licensed under the terms + * of the GNU General Public License version 2. This program is licensed + * "as is" without any warranty of any kind, whether express or implied. + */ + +/* + * This driver is a bit unusual in that it is composed of two logical + * parts where one part is the OS independent code and the other part is + * the OS dependent code. Xilinx provides their drivers split in this + * fashion. This file represents the Linux OS dependent part known as + * the Linux adapter. The other files in this directory are the OS + * independent files as provided by Xilinx with no changes made to them. + * The names exported by those files begin with XEmac_. All functions + * in this file that are called by Linux have names that begin with + * xenet_. The functions in this file that have Handler in their name + * are registered as callbacks with the underlying Xilinx OS independent + * layer. Any other functions are static helper functions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "xemac.h" +#include "xemac_i.h" + +MODULE_AUTHOR("MontaVista Software, Inc. "); +MODULE_DESCRIPTION("Xilinx Ethernet Media Access Controller driver"); +MODULE_LICENSE("GPL"); + +#define TX_TIMEOUT (60*HZ) /* Transmission timeout is 60 seconds. */ + +/* + * Our private per device data. When a net_device is allocated we will + * ask for enough extra space for this. + */ +struct net_local { + struct net_device_stats stats; /* Statistics for this device */ + struct net_device *next_dev; /* The next device in dev_list */ + struct timer_list phy_timer; /* PHY monitoring timer */ + u32 index; /* Which interface is this */ + u32 save_BaseAddress; /* Saved physical base address */ + XInterruptHandler Isr; /* Pointer to the XEmac ISR routine */ + struct sk_buff *saved_skb; /* skb being transmitted */ + spinlock_t skb_lock; /* For atomic access to saved_skb */ + u8 mii_addr; /* The MII address of the PHY */ + /* + * The underlying OS independent code needs space as well. A + * pointer to the following XEmac structure will be passed to + * any XEmac_ function that requires it. However, we treat the + * data as an opaque object in this file (meaning that we never + * reference any of the fields inside of the structure). + */ + XEmac Emac; +}; + +/* List of devices we're handling and a lock to give us atomic access. */ +static struct net_device *dev_list = NULL; +static spinlock_t dev_lock = SPIN_LOCK_UNLOCKED; + +/* SAATODO: This function will be moved into the Xilinx code. */ +/*****************************************************************************/ +/** +* +* Lookup the device configuration based on the emac instance. The table +* EmacConfigTable contains the configuration info for each device in the system. +* +* @param Instance is the index of the emac being looked up. +* +* @return +* +* A pointer to the configuration table entry corresponding to the given +* device ID, or NULL if no match is found. +* +* @note +* +* None. +* +******************************************************************************/ +XEmac_Config *XEmac_GetConfig(int Instance) +{ + if (Instance < 0 || Instance >= XPAR_XEMAC_NUM_INSTANCES) + { + return NULL; + } + + return &XEmac_ConfigTable[Instance]; +} + +/* + * The following are notes regarding the critical sections in this + * driver and how they are protected. + * + * saved_skb + * The pointer to the skb that is currently being transmitted is + * protected by a spinlock. It is conceivable that the transmit timeout + * could fire in the middle of a transmit completion. The spinlock + * protects the critical section of getting the pointer and NULLing it + * out. + * + * dev_list + * There is a spinlock protecting the device list. It isn't really + * necessary yet because the list is only manipulated at init and + * cleanup, but it's there because it is basically free and if we start + * doing hot add and removal of ethernet devices when the FPGA is + * reprogrammed while the system is up, we'll need to protect the list. + * + * XEmac_Start, XEmac_Stop and XEmac_SetOptions are not thread safe. + * These functions are called from xenet_open(), xenet_close(), reset(), + * and xenet_set_multicast_list(). xenet_open() and xenet_close() + * should be safe because when they do start and stop, they don't have + * interrupts or timers enabled. The other side is that they won't be + * called while a timer or interrupt is being handled. Next covered is + * the interaction between reset() and xenet_set_multicast_list(). This + * is taken care of by disabling the ethernet IRQ and bottom half when + * the multicast list is being set. The inverse case is covered because + * a call to xenet_set_multicast_list() will not happen in the middle of + * a timer or interrupt being serviced. Finally, the interaction + * between reset() being called from various places needs to be + * considered. reset() is called from poll_mii() and xenet_tx_timeout() + * which are timer bottom halves as well as FifoRecvHandler() and + * ErrorHandler() which are interrupt handlers. The timer bottom halves + * won't interrupt an interrupt handler (or each other) so that is + * covered. The interrupt handlers won't interrupt each other either. + * That leaves the case of interrupt handlers interrupting timer bottom + * halves. This is handled simply by disabling the interrupt when the + * bottom half calls reset(). + * + * XEmac_PhyRead and XEmac_PhyWrite are not thread safe. + * These functions are called from get_phy_status(), xenet_ioctl() and + * probe(). probe() is only called from xenet_init() so it is not an + * issue (nothing is really up and running yet). get_phy_status() is + * called from both poll_mii() (a timer bottom half) and xenet_open(). + * These shouldn't interfere with each other because xenet_open() is + * what starts the poll_mii() timer. xenet_open() and xenet_ioctl() + * should be safe as well because they will be sequential. That leaves + * the interaction between poll_mii() and xenet_ioctl(). While the + * timer bottom half is executing, a new ioctl won't come in so that is + * taken care of. That leaves the one case of the poll_mii timer + * popping while handling an ioctl. To take care of that case, the + * timer is deleted when the ioctl comes in and then added back in after + * the ioctl is finished. + */ + +/* + * Helper function to reset the underlying hardware. This is called + * when we get into such deep trouble that we don't know how to handle + * otherwise. + */ +typedef enum DUPLEX { UNKNOWN_DUPLEX, HALF_DUPLEX, FULL_DUPLEX } DUPLEX; +static void +reset(struct net_device *dev, DUPLEX duplex) +{ + struct net_local *lp = (struct net_local *) dev->priv; + struct sk_buff *tskb; + u32 Options; + u8 IfgPart1; + u8 IfgPart2; + u8 SendThreshold; + u32 SendWaitBound; + u8 RecvThreshold; + u32 RecvWaitBound; + + /* Shouldn't really be necessary, but shouldn't hurt. */ + netif_stop_queue(dev); + + /* + * XEmac_Reset puts the device back to the default state. We need + * to save all the settings we don't already know, reset, restore + * the settings, and then restart the emac. + */ + XEmac_GetInterframeGap(&lp->Emac, &IfgPart1, &IfgPart2); + Options = XEmac_GetOptions(&lp->Emac); + switch (duplex) { + case HALF_DUPLEX: + Options &= ~XEM_FDUPLEX_OPTION; + break; + case FULL_DUPLEX: + Options |= XEM_FDUPLEX_OPTION; + break; + case UNKNOWN_DUPLEX: + break; + } + + if (XEmac_mIsSgDma(&lp->Emac)) { + /* + * The following four functions will return an error if we are + * not doing scatter-gather DMA. We just checked that so we + * can safely ignore the return values. We cast them to void + * to make that explicit. + */ + (void) XEmac_GetPktThreshold(&lp->Emac, XEM_SEND, + &SendThreshold); + (void) XEmac_GetPktWaitBound(&lp->Emac, XEM_SEND, + &SendWaitBound); + (void) XEmac_GetPktThreshold(&lp->Emac, XEM_RECV, + &RecvThreshold); + (void) XEmac_GetPktWaitBound(&lp->Emac, XEM_RECV, + &RecvWaitBound); + } + + XEmac_Reset(&lp->Emac); + + /* + * The following three functions will return an error if the + * EMAC is already started. We just stopped it by calling + * XEmac_Reset() so we can safely ignore the return values. + * We cast them to void to make that explicit. + */ + (void) XEmac_SetMacAddress(&lp->Emac, dev->dev_addr); + (void) XEmac_SetInterframeGap(&lp->Emac, IfgPart1, IfgPart2); + (void) XEmac_SetOptions(&lp->Emac, Options); + if (XEmac_mIsSgDma(&lp->Emac)) { + /* + * The following four functions will return an error if + * we are not doing scatter-gather DMA or if the EMAC is + * already started. We just checked that we are indeed + * doing scatter-gather and we just stopped the EMAC so + * we can safely ignore the return values. We cast them + * to void to make that explicit. + */ + (void) XEmac_SetPktThreshold(&lp->Emac, XEM_SEND, + SendThreshold); + (void) XEmac_SetPktWaitBound(&lp->Emac, XEM_SEND, + SendWaitBound); + (void) XEmac_SetPktThreshold(&lp->Emac, XEM_RECV, + RecvThreshold); + (void) XEmac_SetPktWaitBound(&lp->Emac, XEM_RECV, + RecvWaitBound); + } + + /* + * XEmac_Start returns an error when: it is already started, the send + * and receive handlers are not set, or a scatter-gather DMA list is + * missing. None of these can happen at this point, so we cast the + * return to void to make that explicit. + */ + (void) XEmac_Start(&lp->Emac); + + /* Make sure that the send handler and we don't both free the skb. */ + spin_lock_irq(&lp->skb_lock); + tskb = lp->saved_skb; + lp->saved_skb = NULL; + spin_unlock_irq(&lp->skb_lock); + if (tskb) + dev_kfree_skb(tskb); + + /* We're all ready to go. Start the queue in case it was stopped. */ + netif_wake_queue(dev); +} + +static int +get_phy_status(struct net_device *dev, DUPLEX * duplex, int *linkup) +{ + struct net_local *lp = (struct net_local *) dev->priv; + u16 reg; + XStatus xs; + + xs = XEmac_PhyRead(&lp->Emac, lp->mii_addr, MII_BMCR, ®); + if (xs != XST_SUCCESS) { + printk(KERN_ERR + "%s: Could not read PHY control register; error %d\n", + dev->name, xs); + return -1; + } + + if (!(reg & BMCR_ANENABLE)) { + /* + * Auto-negotiation is disabled so the full duplex bit in + * the control register tells us if the PHY is running + * half or full duplex. + */ + *duplex = (reg & BMCR_FULLDPLX) ? FULL_DUPLEX : HALF_DUPLEX; + } else { + /* + * Auto-negotiation is enabled. Figure out what was + * negotiated by looking for the best mode in the union + * of what we and our partner advertise. + */ + u16 advertise, partner, negotiated; + + xs = XEmac_PhyRead(&lp->Emac, lp->mii_addr, + MII_ADVERTISE, &advertise); + if (xs != XST_SUCCESS) { + printk(KERN_ERR + "%s: Could not read PHY advertisement; error %d\n", + dev->name, xs); + return -1; + } + xs = XEmac_PhyRead(&lp->Emac, lp->mii_addr, MII_LPA, &partner); + if (xs != XST_SUCCESS) { + printk(KERN_ERR + "%s: Could not read PHY LPA; error %d\n", + dev->name, xs); + return -1; + } + + negotiated = advertise & partner & ADVERTISE_ALL; + if (negotiated & ADVERTISE_100FULL) + *duplex = FULL_DUPLEX; + else if (negotiated & ADVERTISE_100HALF) + *duplex = HALF_DUPLEX; + else if (negotiated & ADVERTISE_10FULL) + *duplex = FULL_DUPLEX; + else + *duplex = HALF_DUPLEX; + } + + xs = XEmac_PhyRead(&lp->Emac, lp->mii_addr, MII_BMSR, ®); + if (xs != XST_SUCCESS) { + printk(KERN_ERR + "%s: Could not read PHY status register; error %d\n", + dev->name, xs); + return -1; + } + + *linkup = (reg & BMSR_LSTATUS) != 0; + + return 0; +} + +/* + * This routine is used for two purposes. The first is to keep the + * EMAC's duplex setting in sync with the PHY's. The second is to keep + * the system apprised of the state of the link. Note that this driver + * does not configure the PHY. Either the PHY should be configured for + * auto-negotiation or it should be handled by something like mii-tool. + */ +static void +poll_mii(unsigned long data) +{ + struct net_device *dev = (struct net_device *) data; + struct net_local *lp = (struct net_local *) dev->priv; + u32 Options; + DUPLEX phy_duplex, mac_duplex; + int phy_carrier, netif_carrier; + + /* First, find out what's going on with the PHY. */ + if (get_phy_status(dev, &phy_duplex, &phy_carrier)) { + printk(KERN_ERR "%s: Terminating link monitoring.\n", + dev->name); + return; + } + + /* Second, figure out if we have the EMAC in half or full duplex. */ + Options = XEmac_GetOptions(&lp->Emac); + mac_duplex = (Options & XEM_FDUPLEX_OPTION) ? FULL_DUPLEX : HALF_DUPLEX; + + /* Now see if there is a mismatch. */ + if (mac_duplex != phy_duplex) { + /* + * Make sure that no interrupts come in that could cause + * reentrancy problems in reset. + */ + disable_irq(dev->irq); + reset(dev, phy_duplex); + enable_irq(dev->irq); + } + + netif_carrier = netif_carrier_ok(dev) != 0; + + if (phy_carrier != netif_carrier) { + if (phy_carrier) { + printk(KERN_INFO "%s: Link carrier restored.\n", + dev->name); + netif_carrier_on(dev); + } else { + printk(KERN_INFO "%s: Link carrier lost.\n", dev->name); + netif_carrier_off(dev); + } + } + + /* Set up the timer so we'll get called again in 2 seconds. */ + lp->phy_timer.expires = jiffies + 2 * HZ; + add_timer(&lp->phy_timer); +} + +/* + * This routine is registered with the OS as the function to call when + * the EMAC interrupts. It in turn, calls the Xilinx OS independent + * interrupt function. There are different interrupt functions for FIFO + * and scatter-gather so we just set a pointer (Isr) into our private + * data so we don't have to figure it out here. The Xilinx OS + * independent interrupt function will in turn call any callbacks that + * we have registered for various conditions. + */ +static void +xenet_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct net_local *lp = (struct net_local *) dev->priv; + + /* Call it. */ + (*(lp->Isr)) (&lp->Emac); +} + +static int +xenet_open(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *) dev->priv; + u32 Options; + DUPLEX phy_duplex, mac_duplex; + int phy_carrier; + + /* + * Just to be safe, stop the device first. If the device is already + * stopped, an error will be returned. In this case, we don't really + * care, so cast it to void to make it explicit. + */ + (void) XEmac_Stop(&lp->Emac); + + /* Set the MAC address each time opened. */ + if (XEmac_SetMacAddress(&lp->Emac, dev->dev_addr) != XST_SUCCESS) { + printk(KERN_ERR "%s: Could not set MAC address.\n", dev->name); + return -EIO; + } + + /* + * If the device is not configured for polled mode, connect to the + * interrupt controller and enable interrupts. Currently, there + * isn't any code to set polled mode, so this check is probably + * superfluous. + */ + Options = XEmac_GetOptions(&lp->Emac); + if ((Options & XEM_POLLED_OPTION) == 0) { + int retval; + /* Grab the IRQ */ + retval = + request_irq(dev->irq, &xenet_interrupt, 0, dev->name, dev); + if (retval) { + printk(KERN_ERR + "%s: Could not allocate interrupt %d.\n", + dev->name, dev->irq); + return retval; + } + } + + /* Set the EMAC's duplex setting based upon what the PHY says. */ + if (!get_phy_status(dev, &phy_duplex, &phy_carrier)) { + /* We successfully got the PHY status. */ + mac_duplex = ((Options & XEM_FDUPLEX_OPTION) + ? FULL_DUPLEX : HALF_DUPLEX); + if (mac_duplex != phy_duplex) { + switch (phy_duplex) { + case HALF_DUPLEX: Options &= ~XEM_FDUPLEX_OPTION; break; + case FULL_DUPLEX: Options |= XEM_FDUPLEX_OPTION; break; + case UNKNOWN_DUPLEX: break; + } + /* + * The following function will return an error + * if the EMAC is already started. We know it + * isn't started so we can safely ignore the + * return value. We cast it to void to make + * that explicit. + */ + (void) XEmac_SetOptions(&lp->Emac, Options); + } + } + + if (XEmac_Start(&lp->Emac) != XST_SUCCESS) { + printk(KERN_ERR "%s: Could not start device.\n", dev->name); + free_irq(dev->irq, dev); + return -EBUSY; + } + + /* We're ready to go. */ + MOD_INC_USE_COUNT; + netif_start_queue(dev); + + /* Set up the PHY monitoring timer. */ + lp->phy_timer.expires = jiffies + 2*HZ; + lp->phy_timer.data = (unsigned long)dev; + lp->phy_timer.function = &poll_mii; + add_timer(&lp->phy_timer); + + return 0; +} +static int +xenet_close(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *) dev->priv; + + /* Shut down the PHY monitoring timer. */ + del_timer_sync(&lp->phy_timer); + + netif_stop_queue(dev); + + /* + * If not in polled mode, free the interrupt. Currently, there + * isn't any code to set polled mode, so this check is probably + * superfluous. + */ + if ((XEmac_GetOptions(&lp->Emac) & XEM_POLLED_OPTION) == 0) + free_irq(dev->irq, dev); + + if (XEmac_Stop(&lp->Emac) != XST_SUCCESS) { + printk(KERN_ERR "%s: Could not stop device.\n", dev->name); + return -EBUSY; + } + + MOD_DEC_USE_COUNT; + return 0; +} +static struct net_device_stats * +xenet_get_stats(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *) dev->priv; + return &lp->stats; +} + +/* Helper function to determine if a given XEmac error warrants a reset. */ +extern inline int +status_requires_reset(XStatus s) +{ + return (s == XST_DMA_ERROR || s == XST_FIFO_ERROR + || s == XST_RESET_ERROR); +} + +static int +xenet_FifoSend(struct sk_buff *orig_skb, struct net_device *dev) +{ + struct net_local *lp = (struct net_local *) dev->priv; + struct sk_buff *new_skb; + unsigned int len, align; + + /* + * The FIFO takes a single request at a time. Stop the queue to + * accomplish this. We'll wake the queue in FifoSendHandler once + * the skb has been sent or in xenet_tx_timeout if something goes + * horribly wrong. + */ + netif_stop_queue(dev); + + len = orig_skb->len; + /* + * The packet FIFO requires the buffers to be 32 bit aligned. + * The sk_buff data is not 32 bit aligned, so we have to do this + * copy. As you probably well know, this is not optimal. + */ + if (!(new_skb = dev_alloc_skb(len + 4))) { + /* We couldn't get another skb. */ + dev_kfree_skb(orig_skb); + lp->stats.tx_dropped++; + printk(KERN_ERR "%s: Could not allocate transmit buffer.\n", + dev->name); + netif_wake_queue(dev); + return -EBUSY; + } + + /* + * A new skb should have the data word aligned, but this code is + * here just in case that isn't true... Calculate how many + * bytes we should reserve to get the data to start on a word + * boundary. */ + align = 4 - ((unsigned long) new_skb->data & 3); + if (align != 4) + skb_reserve(new_skb, align); + + /* Copy the data from the original skb to the new one. */ + skb_put(new_skb, len); + memcpy(new_skb->data, orig_skb->data, len); + + /* Get rid of the original skb. */ + dev_kfree_skb(orig_skb); + + lp->saved_skb = new_skb; + if (XEmac_FifoSend(&lp->Emac, (u8 *) new_skb->data, len) != XST_SUCCESS) { + /* + * I don't think that we will be fighting FifoSendHandler or + * xenet_tx_timeout, but it's cheap to guarantee it won't be a + * problem. + */ + spin_lock_irq(&lp->skb_lock); + new_skb = lp->saved_skb; + lp->saved_skb = NULL; + spin_unlock_irq(&lp->skb_lock); + + dev_kfree_skb(new_skb); + lp->stats.tx_errors++; + printk(KERN_ERR "%s: Could not transmit buffer.\n", dev->name); + netif_wake_queue(dev); + return -EIO; + } + return 0; +} + +/* The callback function for completed frames sent in FIFO mode. */ +static void +FifoSendHandler(void *CallbackRef) +{ + struct net_device *dev = (struct net_device *) CallbackRef; + struct net_local *lp = (struct net_local *) dev->priv; + struct sk_buff *tskb; + + lp->stats.tx_bytes += lp->saved_skb->len; + lp->stats.tx_packets++; + + /* Make sure that the timeout handler and we don't both free the skb. */ + spin_lock_irq(&lp->skb_lock); + tskb = lp->saved_skb; + lp->saved_skb = NULL; + spin_unlock_irq(&lp->skb_lock); + if (tskb) + dev_kfree_skb(tskb); + + /* Start the queue back up to allow next request. */ + netif_wake_queue(dev); +} +static void +xenet_tx_timeout(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *) dev->priv; + printk("%s: Exceeded transmit timeout of %lu ms. Resetting emac.\n", + dev->name, TX_TIMEOUT * 1000UL / HZ); + + lp->stats.tx_errors++; + + /* + * Make sure that no interrupts come in that could cause reentrancy + * problems in reset. + */ + disable_irq(dev->irq); + reset(dev, UNKNOWN_DUPLEX); + enable_irq(dev->irq); +} + +/* The callback function for frames received when in FIFO mode. */ +static void +FifoRecvHandler(void *CallbackRef) +{ + struct net_device *dev = (struct net_device *) CallbackRef; + struct net_local *lp = (struct net_local *) dev->priv; + struct sk_buff *skb; + unsigned int align; + u32 len; + XStatus Result; + + /* + * The OS independent Xilinx EMAC code does not provide a + * function to get the length of an incoming packet and a + * separate call to actually get the packet data. It does this + * because they didn't add any code to keep the hardware's + * receive length and data FIFOs in sync. Instead, they require + * that you send a maximal length buffer so that they can read + * the length and data FIFOs in a single chunk of code so that + * they can't get out of sync. So, we need to allocate an skb + * that can hold a maximal sized packet. The OS independent + * code needs to see the data 32-bit aligned, so we tack on an + * extra four just in case we need to do an skb_reserve to get + * it that way. + */ + len = XEM_MAX_FRAME_SIZE; + if (!(skb = dev_alloc_skb(len + 4))) { + /* Couldn't get memory. */ + lp->stats.rx_dropped++; + printk(KERN_ERR "%s: Could not allocate receive buffer.\n", + dev->name); + return; + } + + /* + * A new skb should have the data word aligned, but this code is + * here just in case that isn't true... Calculate how many + * bytes we should reserve to get the data to start on a word + * boundary. */ + align = 4 - ((unsigned long) skb->data & 3); + if (align != 4) + skb_reserve(skb, align); + + Result = XEmac_FifoRecv(&lp->Emac, (u8 *) skb->data, &len); + if (Result != XST_SUCCESS) { + int need_reset = status_requires_reset(Result); + + lp->stats.rx_errors++; + dev_kfree_skb(skb); + + printk(KERN_ERR "%s: Could not receive buffer, error=%d%s.\n", + dev->name, Result, + need_reset ? ", resetting device." : ""); + if (need_reset) + reset(dev, UNKNOWN_DUPLEX); + return; + } + + skb_put(skb, len); /* Tell the skb how much data we got. */ + skb->dev = dev; /* Fill out required meta-data. */ + skb->protocol = eth_type_trans(skb, dev); + + lp->stats.rx_packets++; + lp->stats.rx_bytes += len; + + netif_rx(skb); /* Send the packet upstream. */ +} + +/* The callback function for errors. */ +static void +ErrorHandler(void *CallbackRef, XStatus Code) +{ + struct net_device *dev = (struct net_device *) CallbackRef; + int need_reset; + need_reset = status_requires_reset(Code); + + printk(KERN_ERR "%s: device error %d%s\n", + dev->name, Code, need_reset ? ", resetting device." : ""); + if (need_reset) + reset(dev, UNKNOWN_DUPLEX); +} + +static void +xenet_set_multicast_list(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *) dev->priv; + u32 Options; + + /* + * XEmac_Start, XEmac_Stop and XEmac_SetOptions are supposed to + * be protected by a semaphore. This Linux adapter doesn't have + * it as bad as the VxWorks adapter because the sequence of + * requests to us is much more sequential. However, we do have + * one area in which this is a problem. + * + * xenet_set_multicast_list() is called while the link is up and + * interrupts are enabled, so at any point in time we could get + * an error that causes our reset() to be called. reset() calls + * the aforementioned functions, and we need to call them from + * here as well. + * + * The solution is to make sure that we don't get interrupts or + * timers popping while we are in this function. + */ + disable_irq(dev->irq); + local_bh_disable(); + + /* + * The dev's set_multicast_list function is only called when + * the device is up. So, without checking, we know we need to + * Stop and Start the XEmac because it has already been + * started. XEmac_Stop() will return an error if it is already + * stopped, but in this case we don't care so cast it to void + * to make it explicit + */ + (void) XEmac_Stop(&lp->Emac); + + Options = XEmac_GetOptions(&lp->Emac); + + /* Clear out the bits we may set. */ + Options &= ~(XEM_PROMISC_OPTION | XEM_MULTICAST_OPTION); + + if (dev->flags & IFF_PROMISC) + Options |= XEM_PROMISC_OPTION; +#if 0 + else { + /* + * SAATODO: Xilinx is going to add multicast support to their + * VxWorks adapter and OS independent layer. After that is + * done, this skeleton code should be fleshed out. Note that + * IFF_MULTICAST is being masked out from dev->flags in probe, + * so that will need to be removed to actually do multidrop. + */ + if ((dev->flags & IFF_ALLMULTI) + || dev->mc_count > MAX_MULTICAST ? ? ?) { + xemac_get_all_multicast ? ? ? (); + Options |= XEM_MULTICAST_OPTION; + } else if (dev->mc_count != 0) { + struct dev_mc_list *mc; + + XEmac_MulticastClear(&lp->Emac); + for (mc = dev->mc_list; mc; mc = mc->next) + XEmac_MulticastAdd(&lp->Emac, mc->dmi_addr); + Options |= XEM_MULTICAST_OPTION; + } + } +#endif + + /* + * The following function will return an error if the EMAC is already + * started. We know it isn't started so we can safely ignore the + * return value. We cast it to void to make that explicit. + */ + (void) XEmac_SetOptions(&lp->Emac, Options); + + /* + * XEmac_Start returns an error when: it is already started, the send + * and receive handlers are not set, or a scatter-gather DMA list is + * missing. None of these can happen at this point, so we cast the + * return to void to make that explicit. + */ + (void) XEmac_Start(&lp->Emac); + + /* All done, get those interrupts and timers going again. */ + local_bh_enable(); + enable_irq(dev->irq); +} + +static int +xenet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct net_local *lp = (struct net_local *) dev->priv; + /* mii_ioctl_data has 4 u16 fields: phy_id, reg_num, val_in & val_out */ + struct mii_ioctl_data *data = (struct mii_ioctl_data *) &rq->ifr_data; + + XStatus Result; + + switch (cmd) { + case SIOCGMIIPHY: /* Get address of MII PHY in use. */ + case SIOCDEVPRIVATE: /* for binary compat, remove in 2.5 */ + data->phy_id = lp->mii_addr; + /* Fall Through */ + + case SIOCGMIIREG: /* Read MII PHY register. */ + case SIOCDEVPRIVATE + 1: /* for binary compat, remove in 2.5 */ + if (data->phy_id > 31 || data->reg_num > 31) + return -ENXIO; + + /* Stop the PHY timer to prevent reentrancy. */ + del_timer_sync(&lp->phy_timer); + Result = XEmac_PhyRead(&lp->Emac, data->phy_id, + data->reg_num, &data->val_out); + /* Start the PHY timer up again. */ + lp->phy_timer.expires = jiffies + 2*HZ; + add_timer(&lp->phy_timer); + + if (Result != XST_SUCCESS) { + printk(KERN_ERR + "%s: Could not read from PHY, error=%d.\n", + dev->name, Result); + return (Result == XST_EMAC_MII_BUSY) ? -EBUSY : -EIO; + } + return 0; + + case SIOCSMIIREG: /* Write MII PHY register. */ + case SIOCDEVPRIVATE + 2: /* for binary compat, remove in 2.5 */ + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + + if (data->phy_id > 31 || data->reg_num > 31) + return -ENXIO; + + /* Stop the PHY timer to prevent reentrancy. */ + del_timer_sync(&lp->phy_timer); + Result = XEmac_PhyWrite(&lp->Emac, data->phy_id, + data->reg_num, data->val_in); + /* Start the PHY timer up again. */ + lp->phy_timer.expires = jiffies + 2*HZ; + add_timer(&lp->phy_timer); + + if (Result != XST_SUCCESS) { + printk(KERN_ERR + "%s: Could not write to PHY, error=%d.\n", + dev->name, Result); + return (Result == XST_EMAC_MII_BUSY) ? -EBUSY : -EIO; + } + return 0; + + default: + return -EOPNOTSUPP; + } +} + +static void +remove_head_dev(void) +{ + struct net_local *lp; + struct net_device *dev; + XEmac_Config *cfg; + + /* Pull the head off of dev_list. */ + spin_lock(&dev_lock); + dev = dev_list; + lp = (struct net_local *) dev->priv; + dev_list = lp->next_dev; + spin_unlock(&dev_lock); + + /* Put the base address back to the physical address. */ + cfg = XEmac_GetConfig(lp->index); + iounmap((void *) cfg->BaseAddress); + cfg->BaseAddress = lp->save_BaseAddress; + + /* Free up the memory. */ + if (lp->saved_skb) + dev_kfree_skb(lp->saved_skb); + kfree(lp); + + unregister_netdev(dev); + kfree(dev); +} + +static int __init +probe(int index) +{ + static const unsigned long remap_size + = XPAR_EMAC_0_HIGHADDR - XPAR_EMAC_0_BASEADDR + 1; + struct net_device *dev; + struct net_local *lp; + XEmac_Config *cfg; + unsigned int irq; + u32 maddr; + + switch (index) { +#if defined(XPAR_INTC_0_EMAC_0_VEC_ID) + case 0: + irq = 31 - XPAR_INTC_0_EMAC_0_VEC_ID; + break; +#if defined(XPAR_INTC_0_EMAC_1_VEC_ID) + case 1: + irq = 31 - XPAR_INTC_0_EMAC_1_VEC_ID; + break; +#if defined(XPAR_INTC_0_EMAC_2_VEC_ID) + case 2: + irq = 31 - XPAR_INTC_0_EMAC_2_VEC_ID; + break; +#if defined(XPAR_INTC_0_EMAC_3_VEC_ID) +#error Edit this file to add more devices. +#endif /* 3 */ +#endif /* 2 */ +#endif /* 1 */ +#endif /* 0 */ + default: + return -ENODEV; + } + + /* Find the config for our device. */ + cfg = XEmac_GetConfig(index); + if (!cfg) + return -ENODEV; + + dev = init_etherdev(0, sizeof (struct net_local)); + if (!dev) { + printk(KERN_ERR "Could not allocate Xilinx enet device %d.\n", + index); + return -ENOMEM; + } + SET_MODULE_OWNER(dev); + + ether_setup(dev); + dev->irq = irq; + + /* Initialize our private data. */ + lp = (struct net_local *) dev->priv; + memset(lp, 0, sizeof (struct net_local)); + lp->index = index; + spin_lock_init(&lp->skb_lock); + + /* Make it the head of dev_list. */ + spin_lock(&dev_lock); + lp->next_dev = dev_list; + dev_list = dev; + spin_unlock(&dev_lock); + + /* Change the addresses to be virtual; save the old ones to restore. */ + lp->save_BaseAddress = cfg->BaseAddress; + cfg->BaseAddress = (u32) ioremap(lp->save_BaseAddress, remap_size); + + if (XEmac_Initialize(&lp->Emac, cfg->DeviceId) != XST_SUCCESS) { + printk(KERN_ERR "%s: Could not initialize device.\n", + dev->name); + remove_head_dev(); + return -ENODEV; + } + + memcpy(dev->dev_addr, ((bd_t *) __res)->bi_enetaddr, 6); + if (XEmac_SetMacAddress(&lp->Emac, dev->dev_addr) != XST_SUCCESS) { + /* should not fail right after an initialize */ + printk(KERN_ERR "%s: Could not set MAC address.\n", dev->name); + remove_head_dev(); + return -EIO; + } + + if (XEmac_mIsSgDma(&lp->Emac)) { + /* + * SAATODO: Currently scatter-gather DMA does not work. At + * some point Xilinx is going to get that working in their + * code and then this driver should be enhanced in a similar + * fashion. + */ + printk(KERN_ERR "%s: Scatter gather not supported yet.\n", + dev->name); + remove_head_dev(); + return -EIO; + /* lp->Isr = XEmac_IntrHandlerDma; */ + } else { + XEmac_SetFifoRecvHandler(&lp->Emac, dev, FifoRecvHandler); + XEmac_SetFifoSendHandler(&lp->Emac, dev, FifoSendHandler); + dev->hard_start_xmit = xenet_FifoSend; + lp->Isr = XEmac_IntrHandlerFifo; + } + XEmac_SetErrorHandler(&lp->Emac, dev, ErrorHandler); + + + /* Scan to find the PHY. */ + lp->mii_addr = 0xFF; + for (maddr = 0; maddr < 31; maddr++) { + XStatus Result; + u16 reg; + + Result = XEmac_PhyRead(&lp->Emac, maddr, MII_BMCR, ®); + /* + * XEmac_PhyRead is currently returning XST_SUCCESS even + * when reading from non-existent addresses. Work + * around this by doing a primitive validation on the + * control word we get back. + */ + if (Result == XST_SUCCESS && (reg & BMCR_RESV) == 0) { + lp->mii_addr = maddr; + break; + } + } + if (lp->mii_addr == 0xFF) { + lp->mii_addr = 0; + printk(KERN_WARNING + "%s: No PHY detected. Assuming a PHY at address %d.\n", + dev->name, lp->mii_addr); + } + + dev->open = xenet_open; + dev->stop = xenet_close; + dev->get_stats = xenet_get_stats; + dev->flags &= ~IFF_MULTICAST; + dev->set_multicast_list = xenet_set_multicast_list; + dev->do_ioctl = xenet_ioctl; + dev->tx_timeout = xenet_tx_timeout; + dev->watchdog_timeo = TX_TIMEOUT; + + printk(KERN_INFO + "%s: Xilinx EMAC #%d at 0x%08X mapped to 0x%08X, irq=%d\n", + dev->name, index, + lp->save_BaseAddress, cfg->BaseAddress, dev->irq); + return 0; +} +static int __init +xenet_init(void) +{ + int index = 0; + + while (probe(index++) == 0) ; + /* If we found at least one, report success. */ + return (index > 1) ? 0 : -ENODEV; +} + +static void __exit +xenet_cleanup(void) +{ + while (dev_list) + remove_head_dev(); +} + +EXPORT_NO_SYMBOLS; + +module_init(xenet_init); +module_exit(xenet_cleanup); diff -urN -X bkexcl linux-2.4.22/drivers/net/xilinx_enet/xemac.c linuxppc-2.4/drivers/net/xilinx_enet/xemac.c --- linux-2.4.22/drivers/net/xilinx_enet/xemac.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/net/xilinx_enet/xemac.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,845 @@ +/* $Id: xemac.c,v 1.4 2003/05/13 19:32:35 robertm Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xemac.c +* +* The XEmac driver. Functions in this file are the minimum required functions +* for this driver. See xemac.h for a detailed description of the driver. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -------------------------------------------------------
+* 1.00a rpm  07/31/01 First release
+* 1.00b rpm  02/20/02 Repartitioned files and functions
+* 1.00b rpm  07/23/02 Removed the PHY reset from Initialize()
+* 1.00b rmm  09/23/02 Removed commented code in Initialize(). Recycled as
+*                     XEmac_mPhyReset macro in xemac_l.h.
+* 1.00c rpm  12/05/02 New version includes support for simple DMA
+* 1.00c rpm  12/12/02 Changed location of IsStarted assignment in XEmac_Start
+*                     to be sure the flag is set before the device and
+*                     interrupts are enabled.
+* 1.00c rpm  02/03/03 SelfTest was not clearing polled mode. Take driver out
+*                     of polled mode in XEmac_Reset() to fix this problem.
+* 1.00c rmm  05/13/03 Fixed diab compiler warnings relating to asserts.
+* 
+******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xemac_i.h" +#include "xio.h" +#include "xipif_v1_23_b.h" /* Uses v1.23b of the IPIF */ + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +static XStatus ConfigureDma(XEmac * InstancePtr); +static XStatus ConfigureFifo(XEmac * InstancePtr); +static void StubFifoHandler(void *CallBackRef); +static void StubErrorHandler(void *CallBackRef, XStatus ErrorCode); +static void StubSgHandler(void *CallBackRef, XBufDescriptor * BdPtr, + u32 NumBds); + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** +* +* Initialize a specific XEmac instance/driver. The initialization entails: +* - Initialize fields of the XEmac structure +* - Clear the Ethernet statistics for this device +* - Initialize the IPIF component with its register base address +* - Configure the FIFO components with their register base addresses. +* - If the device is configured with DMA, configure the DMA channel components +* with their register base addresses. At some later time, memory pools for +* the scatter-gather descriptor lists may be passed to the driver. +* - Reset the Ethernet MAC +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param DeviceId is the unique id of the device controlled by this XEmac +* instance. Passing in a device id associates the generic XEmac +* instance to a specific device, as chosen by the caller or application +* developer. +* +* @return +* +* - XST_SUCCESS if initialization was successful +* - XST_DEVICE_IS_STARTED if the device has already been started +* - XST_DEVICE_NOT_FOUND if device configuration information was not found for +* a device with the supplied device ID. +* +* @note +* +* None. +* +******************************************************************************/ +XStatus +XEmac_Initialize(XEmac * InstancePtr, u16 DeviceId) +{ + XStatus Result; + XEmac_Config *ConfigPtr; /* configuration information */ + + XASSERT_NONVOID(InstancePtr != NULL); + + /* + * If the device is started, disallow the initialize and return a status + * indicating it is started. This allows the user to stop the device + * and reinitialize, but prevents a user from inadvertently initializing + */ + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + return XST_DEVICE_IS_STARTED; + } + + /* + * Lookup the device configuration in the temporary CROM table. Use this + * configuration info down below when initializing this component. + */ + ConfigPtr = XEmac_LookupConfig(DeviceId); + if (ConfigPtr == NULL) { + return XST_DEVICE_NOT_FOUND; + } + + /* + * Set some default values + */ + InstancePtr->IsReady = 0; + InstancePtr->IsStarted = 0; + InstancePtr->IpIfDmaConfig = ConfigPtr->IpIfDmaConfig; + InstancePtr->HasMii = ConfigPtr->HasMii; + InstancePtr->HasMulticastHash = FALSE; + + /* Always default polled to false, let user configure this mode */ + InstancePtr->IsPolled = FALSE; + InstancePtr->FifoRecvHandler = StubFifoHandler; + InstancePtr->FifoSendHandler = StubFifoHandler; + InstancePtr->ErrorHandler = StubErrorHandler; + InstancePtr->SgRecvHandler = StubSgHandler; + InstancePtr->SgSendHandler = StubSgHandler; + + /* + * Clear the statistics for this driver + */ + XEmac_mClearStruct((u8 *) & InstancePtr->Stats, sizeof (XEmac_Stats)); + + /* + * Initialize the device register base addresses + */ + InstancePtr->BaseAddress = ConfigPtr->BaseAddress; + + /* + * Configure the send and receive FIFOs in the MAC + */ + Result = ConfigureFifo(InstancePtr); + if (Result != XST_SUCCESS) { + return Result; + } + + /* + * If the device is configured for DMA, configure the send and receive DMA + * channels in the MAC. + */ + if (XEmac_mIsDma(InstancePtr)) { + Result = ConfigureDma(InstancePtr); + if (Result != XST_SUCCESS) { + return Result; + } + } + + /* + * Indicate the component is now ready to use. Note that this is done before + * we reset the device and the PHY below, which may seem a bit odd. The + * choice was made to move it here rather than remove the asserts in various + * functions (e.g., Reset() and all functions that it calls). Applications + * that use multiple threads, one to initialize the XEmac driver and one + * waiting on the IsReady condition could have a problem with this sequence. + */ + InstancePtr->IsReady = XCOMPONENT_IS_READY; + + /* + * Reset the MAC to get it into its initial state. It is expected that + * device configuration by the user will take place after this + * initialization is done, but before the device is started. + */ + XEmac_Reset(InstancePtr); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Start the Ethernet controller as follows: +* - If not in polled mode +* - Set the internal interrupt enable registers appropriately +* - Enable interrupts within the device itself. Note that connection of +* the driver's interrupt handler to the interrupt source (typically +* done using the interrupt controller component) is done by the higher +* layer software. +* - If the device is configured with scatter-gather DMA, start the DMA +* channels if the descriptor lists are not empty +* - Enable the transmitter +* - Enable the receiver +* +* The PHY is enabled after driver initialization. We assume the upper layer +* software has configured it and the EMAC appropriately before this function +* is called. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* - XST_SUCCESS if the device was started successfully +* - XST_NO_CALLBACK if a callback function has not yet been registered using +* the SetxxxHandler function. This is required if in interrupt mode. +* - XST_DEVICE_IS_STARTED if the device is already started +* - XST_DMA_SG_NO_LIST if configured for scatter-gather DMA and a descriptor +* list has not yet been created for the send or receive channel. +* +* @note +* +* The driver tries to match the hardware configuration. So if the hardware +* is configured with scatter-gather DMA, the driver expects to start the +* scatter-gather channels and expects that the user has set up the buffer +* descriptor lists already. If the user expects to use the driver in a mode +* different than how the hardware is configured, the user should modify the +* configuration table to reflect the mode to be used. Modifying the config +* table is a workaround for now until we get some experience with how users +* are intending to use the hardware in its different configurations. For +* example, if the hardware is built with scatter-gather DMA but the user is +* intending to use only simple DMA, the user either needs to modify the config +* table as a workaround or rebuild the hardware with only simple DMA. +* +* This function makes use of internal resources that are shared between the +* Start, Stop, and SetOptions functions. So if one task might be setting device +* options while another is trying to start the device, the user is required to +* provide protection of this shared data (typically using a semaphore). +* +******************************************************************************/ +XStatus +XEmac_Start(XEmac * InstancePtr) +{ + u32 ControlReg; + XStatus Result; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * If it is already started, return a status indicating so + */ + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + return XST_DEVICE_IS_STARTED; + } + + /* + * If not polled, enable interrupts + */ + if (!InstancePtr->IsPolled) { + /* + * Verify that the callbacks have been registered, then enable + * interrupts + */ + if (XEmac_mIsSgDma(InstancePtr)) { + if ((InstancePtr->SgRecvHandler == StubSgHandler) || + (InstancePtr->SgSendHandler == StubSgHandler)) { + return XST_NO_CALLBACK; + } + + /* Enable IPIF interrupts */ + XIIF_V123B_WRITE_DIER(InstancePtr->BaseAddress, + XEM_IPIF_DMA_DFT_MASK | + XIIF_V123B_ERROR_MASK); + XIIF_V123B_WRITE_IIER(InstancePtr->BaseAddress, + XEM_EIR_DFT_SG_MASK); + + /* Enable scatter-gather DMA interrupts */ + XDmaChannel_SetIntrEnable(&InstancePtr->RecvChannel, + XEM_DMA_SG_INTR_MASK); + XDmaChannel_SetIntrEnable(&InstancePtr->SendChannel, + XEM_DMA_SG_INTR_MASK); + } else { + if ((InstancePtr->FifoRecvHandler == StubFifoHandler) || + (InstancePtr->FifoSendHandler == StubFifoHandler)) { + return XST_NO_CALLBACK; + } + + /* Enable IPIF interrupts (used by simple DMA also) */ + XIIF_V123B_WRITE_DIER(InstancePtr->BaseAddress, + XEM_IPIF_FIFO_DFT_MASK | + XIIF_V123B_ERROR_MASK); + XIIF_V123B_WRITE_IIER(InstancePtr->BaseAddress, + XEM_EIR_DFT_FIFO_MASK); + } + + /* Enable the global IPIF interrupt output */ + XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); + } + + /* + * Indicate that the device is started before we enable the transmitter + * or receiver. This needs to be done before because as soon as the + * receiver is enabled we may get an interrupt, and there are functions + * in the interrupt handling path that rely on the IsStarted flag. + */ + InstancePtr->IsStarted = XCOMPONENT_IS_STARTED; + + /* + * Enable the transmitter, and receiver (do a read/modify/write to preserve + * current settings). There is no critical section here since this register + * is not modified during interrupt context. + */ + ControlReg = XIo_In32(InstancePtr->BaseAddress + XEM_ECR_OFFSET); + ControlReg &= ~(XEM_ECR_XMIT_RESET_MASK | XEM_ECR_RECV_RESET_MASK); + ControlReg |= (XEM_ECR_XMIT_ENABLE_MASK | XEM_ECR_RECV_ENABLE_MASK); + + XIo_Out32(InstancePtr->BaseAddress + XEM_ECR_OFFSET, ControlReg); + + /* + * If configured with scatter-gather DMA and not polled, restart the + * DMA channels in case there are buffers ready to be sent or received into. + * The DMA SgStart function uses data that can be modified during interrupt + * context, so a critical section is required here. + */ + if ((XEmac_mIsSgDma(InstancePtr)) && (!InstancePtr->IsPolled)) { + XIIF_V123B_GINTR_DISABLE(InstancePtr->BaseAddress); + + /* + * The only error we care about is if the list has not yet been + * created, or on receive, if no buffer descriptors have been + * added yet (the list is empty). Other errors are benign at this point. + */ + Result = XDmaChannel_SgStart(&InstancePtr->RecvChannel); + if ((Result == XST_DMA_SG_NO_LIST) + || (Result == XST_DMA_SG_LIST_EMPTY)) { + XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); + return Result; + } + + Result = XDmaChannel_SgStart(&InstancePtr->SendChannel); + if (Result == XST_DMA_SG_NO_LIST) { + XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); + return Result; + } + + XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Stop the Ethernet MAC as follows: +* - If the device is configured with scatter-gather DMA, stop the DMA +* channels (wait for acknowledgment of stop) +* - Disable the transmitter and receiver +* - Disable interrupts if not in polled mode (the higher layer software is +* responsible for disabling interrupts at the interrupt controller) +* +* The PHY is left enabled after a Stop is called. +* +* If the device is configured for scatter-gather DMA, the DMA engine stops at +* the next buffer descriptor in its list. The remaining descriptors in the list +* are not removed, so anything in the list will be transmitted or received when +* the device is restarted. The side effect of doing this is that the last +* buffer descriptor processed by the DMA engine before stopping may not be the +* last descriptor in the Ethernet frame. So when the device is restarted, a +* partial frame (i.e., a bad frame) may be transmitted/received. This is only a +* concern if a frame can span multiple buffer descriptors, which is dependent +* on the size of the network buffers. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* - XST_SUCCESS if the device was stopped successfully +* - XST_DEVICE_IS_STOPPED if the device is already stopped +* +* @note +* +* This function makes use of internal resources that are shared between the +* Start, Stop, and SetOptions functions. So if one task might be setting device +* options while another is trying to start the device, the user is required to +* provide protection of this shared data (typically using a semaphore). +* +******************************************************************************/ +XStatus +XEmac_Stop(XEmac * InstancePtr) +{ + u32 ControlReg; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * If the device is already stopped, do nothing but return a status + * indicating so + */ + if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED) { + return XST_DEVICE_IS_STOPPED; + } + + /* + * If configured for scatter-gather DMA, stop the DMA channels. Ignore + * the XST_DMA_SG_IS_STOPPED return code. There is a critical section + * here between SgStart and SgStop, and SgStart can be called in interrupt + * context, so disable interrupts while calling SgStop. + */ + if (XEmac_mIsSgDma(InstancePtr)) { + XBufDescriptor *BdTemp; /* temporary descriptor pointer */ + + XIIF_V123B_GINTR_DISABLE(InstancePtr->BaseAddress); + + (void) XDmaChannel_SgStop(&InstancePtr->SendChannel, &BdTemp); + (void) XDmaChannel_SgStop(&InstancePtr->RecvChannel, &BdTemp); + + XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); + } + + /* + * Disable the transmitter and receiver. There is no critical section + * here since this register is not modified during interrupt context. + */ + ControlReg = XIo_In32(InstancePtr->BaseAddress + XEM_ECR_OFFSET); + ControlReg &= ~(XEM_ECR_XMIT_ENABLE_MASK | XEM_ECR_RECV_ENABLE_MASK); + XIo_Out32(InstancePtr->BaseAddress + XEM_ECR_OFFSET, ControlReg); + + /* + * If not in polled mode, disable interrupts for IPIF (includes MAC and + * DMAs) + */ + if (!InstancePtr->IsPolled) { + XIIF_V123B_GINTR_DISABLE(InstancePtr->BaseAddress); + } + + InstancePtr->IsStarted = 0; + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Reset the Ethernet MAC. This is a graceful reset in that the device is stopped +* first. Resets the DMA channels, the FIFOs, the transmitter, and the receiver. +* The PHY is not reset. Any frames in the scatter-gather descriptor lists will +* remain in the lists. The side effect of doing this is that after a reset and +* following a restart of the device, frames that were in the list before the +* reset may be transmitted or received. Reset must only be called after the +* driver has been initialized. +* +* The driver is also taken out of polled mode if polled mode was set. The user +* is responsbile for re-configuring the driver into polled mode after the +* reset if desired. +* +* The configuration after this reset is as follows: +* - Half duplex +* - Disabled transmitter and receiver +* - Enabled PHY (the PHY is not reset) +* - MAC transmitter does pad insertion, FCS insertion, and source address +* overwrite. +* - MAC receiver does not strip padding or FCS +* - Interframe Gap as recommended by IEEE Std. 802.3 (96 bit times) +* - Unicast addressing enabled +* - Broadcast addressing enabled +* - Multicast addressing disabled (addresses are preserved) +* - Promiscuous addressing disabled +* - Default packet threshold and packet wait bound register values for +* scatter-gather DMA operation +* - MAC address of all zeros +* - Non-polled mode +* +* The upper layer software is responsible for re-configuring (if necessary) +* and restarting the MAC after the reset. Note that the PHY is not reset. PHY +* control is left to the upper layer software. Note also that driver statistics +* are not cleared on reset. It is up to the upper layer software to clear the +* statistics if needed. +* +* When a reset is required due to an internal error, the driver notifies the +* upper layer software of this need through the ErrorHandler callback and +* specific status codes. The upper layer software is responsible for calling +* this Reset function and then re-configuring the device. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +* @internal +* +* The reset is accomplished by setting the IPIF reset register. This takes +* care of resetting all hardware blocks, including the MAC. +* +******************************************************************************/ +void +XEmac_Reset(XEmac * InstancePtr) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Stop the device first + */ + (void) XEmac_Stop(InstancePtr); + + /* + * Take the driver out of polled mode + */ + InstancePtr->IsPolled = FALSE; + + /* + * Reset the entire IPIF at once. If we choose someday to reset each + * hardware block separately, the reset should occur in the direction of + * data flow. For example, for the send direction the reset order is DMA + * first, then FIFO, then the MAC transmitter. + */ + XIIF_V123B_RESET(InstancePtr->BaseAddress); + + if (XEmac_mIsSgDma(InstancePtr)) { + /* + * After reset, configure the scatter-gather DMA packet threshold and + * packet wait bound registers to default values. Ignore the return + * values of these functions since they only return error if the device + * is not stopped. + */ + (void) XEmac_SetPktThreshold(InstancePtr, XEM_SEND, + XEM_SGDMA_DFT_THRESHOLD); + (void) XEmac_SetPktThreshold(InstancePtr, XEM_RECV, + XEM_SGDMA_DFT_THRESHOLD); + (void) XEmac_SetPktWaitBound(InstancePtr, XEM_SEND, + XEM_SGDMA_DFT_WAITBOUND); + (void) XEmac_SetPktWaitBound(InstancePtr, XEM_RECV, + XEM_SGDMA_DFT_WAITBOUND); + } +} + +/*****************************************************************************/ +/** +* +* Set the MAC address for this driver/device. The address is a 48-bit value. +* The device must be stopped before calling this function. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param AddressPtr is a pointer to a 6-byte MAC address. +* +* @return +* +* - XST_SUCCESS if the MAC address was set successfully +* - XST_DEVICE_IS_STARTED if the device has not yet been stopped +* +* @note +* +* None. +* +******************************************************************************/ +XStatus +XEmac_SetMacAddress(XEmac * InstancePtr, u8 * AddressPtr) +{ + u32 MacAddr = 0; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(AddressPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * The device must be stopped before setting the MAC address + */ + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + return XST_DEVICE_IS_STARTED; + } + + /* + * Set the device station address high and low registers + */ + MacAddr = (AddressPtr[0] << 8) | AddressPtr[1]; + XIo_Out32(InstancePtr->BaseAddress + XEM_SAH_OFFSET, MacAddr); + + MacAddr = (AddressPtr[2] << 24) | (AddressPtr[3] << 16) | + (AddressPtr[4] << 8) | AddressPtr[5]; + + XIo_Out32(InstancePtr->BaseAddress + XEM_SAL_OFFSET, MacAddr); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Get the MAC address for this driver/device. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param BufferPtr is an output parameter, and is a pointer to a buffer into +* which the current MAC address will be copied. The buffer must be at +* least 6 bytes. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XEmac_GetMacAddress(XEmac * InstancePtr, u8 * BufferPtr) +{ + u32 MacAddrHi; + u32 MacAddrLo; + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(BufferPtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + MacAddrHi = XIo_In32(InstancePtr->BaseAddress + XEM_SAH_OFFSET); + MacAddrLo = XIo_In32(InstancePtr->BaseAddress + XEM_SAL_OFFSET); + + BufferPtr[0] = (u8) (MacAddrHi >> 8); + BufferPtr[1] = (u8) MacAddrHi; + BufferPtr[2] = (u8) (MacAddrLo >> 24); + BufferPtr[3] = (u8) (MacAddrLo >> 16); + BufferPtr[4] = (u8) (MacAddrLo >> 8); + BufferPtr[5] = (u8) MacAddrLo; +} + +/******************************************************************************/ +/** +* +* Configure DMA capabilities. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* - XST_SUCCESS if successful initialization of DMA +* +* @note +* +* None. +* +******************************************************************************/ +static XStatus +ConfigureDma(XEmac * InstancePtr) +{ + XStatus Result; + + /* + * Initialize the DMA channels with their base addresses. We assume + * scatter-gather DMA is the only possible configuration. Descriptor space + * will need to be set later by the upper layer. + */ + Result = XDmaChannel_Initialize(&InstancePtr->RecvChannel, + InstancePtr->BaseAddress + + XEM_DMA_RECV_OFFSET); + if (Result != XST_SUCCESS) { + return Result; + } + + Result = XDmaChannel_Initialize(&InstancePtr->SendChannel, + InstancePtr->BaseAddress + + XEM_DMA_SEND_OFFSET); + + return Result; +} + +/******************************************************************************/ +/** +* +* Configure the send and receive FIFO components with their base addresses +* and interrupt masks. Currently the base addresses are defined constants. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* XST_SUCCESS if successful initialization of the packet FIFOs +* +* @note +* +* None. +* +******************************************************************************/ +static XStatus +ConfigureFifo(XEmac * InstancePtr) +{ + XStatus Result; + + /* + * Return status from the packet FIFOs initialization is ignored since + * they always return success. + */ + Result = XPacketFifoV100b_Initialize(&InstancePtr->RecvFifo, + InstancePtr->BaseAddress + + XEM_PFIFO_RXREG_OFFSET, + InstancePtr->BaseAddress + + XEM_PFIFO_RXDATA_OFFSET); + if (Result != XST_SUCCESS) { + return Result; + } + + Result = XPacketFifoV100b_Initialize(&InstancePtr->SendFifo, + InstancePtr->BaseAddress + + XEM_PFIFO_TXREG_OFFSET, + InstancePtr->BaseAddress + + XEM_PFIFO_TXDATA_OFFSET); + return Result; +} + +/******************************************************************************/ +/** +* +* This is a stub for the scatter-gather send and recv callbacks. The stub +* is here in case the upper layers forget to set the handlers. +* +* @param CallBackRef is a pointer to the upper layer callback reference +* @param BdPtr is a pointer to the first buffer descriptor in a list +* @param NumBds is the number of descriptors in the list. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +StubSgHandler(void *CallBackRef, XBufDescriptor * BdPtr, u32 NumBds) +{ + XASSERT_VOID_ALWAYS(); +} + +/******************************************************************************/ +/** +* +* This is a stub for the non-DMA send and recv callbacks. The stub is here in +* case the upper layers forget to set the handlers. +* +* @param CallBackRef is a pointer to the upper layer callback reference +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +StubFifoHandler(void *CallBackRef) +{ + XASSERT_VOID_ALWAYS(); +} + +/******************************************************************************/ +/** +* +* This is a stub for the asynchronous error callback. The stub is here in +* case the upper layers forget to set the handler. +* +* @param CallBackRef is a pointer to the upper layer callback reference +* @param ErrorCode is the Xilinx error code, indicating the cause of the error +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +StubErrorHandler(void *CallBackRef, XStatus ErrorCode) +{ + XASSERT_VOID_ALWAYS(); +} + +/*****************************************************************************/ +/** +* +* Lookup the device configuration based on the unique device ID. The table +* EmacConfigTable contains the configuration info for each device in the system. +* +* @param DeviceId is the unique device ID of the device being looked up. +* +* @return +* +* A pointer to the configuration table entry corresponding to the given +* device ID, or NULL if no match is found. +* +* @note +* +* None. +* +******************************************************************************/ +XEmac_Config * +XEmac_LookupConfig(u16 DeviceId) +{ + XEmac_Config *CfgPtr = NULL; + int i; + + for (i = 0; i < XPAR_XEMAC_NUM_INSTANCES; i++) { + if (XEmac_ConfigTable[i].DeviceId == DeviceId) { + CfgPtr = &XEmac_ConfigTable[i]; + break; + } + } + + return CfgPtr; +} diff -urN -X bkexcl linux-2.4.22/drivers/net/xilinx_enet/xemac.h linuxppc-2.4/drivers/net/xilinx_enet/xemac.h --- linux-2.4.22/drivers/net/xilinx_enet/xemac.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/net/xilinx_enet/xemac.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,674 @@ +/* $Id: xemac.h,v 1.3 2003/02/03 19:49:38 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xemac.h +* +* The Xilinx Ethernet driver component. This component supports the Xilinx +* Ethernet 10/100 MAC (EMAC). +* +* The Xilinx Ethernet 10/100 MAC supports the following features: +* - Simple and scatter-gather DMA operations, as well as simple memory +* mapped direct I/O interface (FIFOs). +* - Media Independent Interface (MII) for connection to external +* 10/100 Mbps PHY transceivers. +* - MII management control reads and writes with MII PHYs +* - Independent internal transmit and receive FIFOs +* - CSMA/CD compliant operations for half-duplex modes +* - Programmable PHY reset signal +* - Unicast, broadcast, and promiscuous address filtering (no multicast yet) +* - Internal loopback +* - Automatic source address insertion or overwrite (programmable) +* - Automatic FCS insertion and stripping (programmable) +* - Automatic pad insertion and stripping (programmable) +* - Pause frame (flow control) detection in full-duplex mode +* - Programmable interframe gap +* - VLAN frame support. +* - Pause frame support +* +* The device driver supports all the features listed above. +* +* Driver Description +* +* The device driver enables higher layer software (e.g., an application) to +* communicate to the EMAC. The driver handles transmission and reception of +* Ethernet frames, as well as configuration of the controller. It does not +* handle protocol stack functionality such as Link Layer Control (LLC) or the +* Address Resolution Protocol (ARP). The protocol stack that makes use of the +* driver handles this functionality. This implies that the driver is simply a +* pass-through mechanism between a protocol stack and the EMAC. A single device +* driver can support multiple EMACs. +* +* The driver is designed for a zero-copy buffer scheme. That is, the driver will +* not copy buffers. This avoids potential throughput bottlenecks within the +* driver. +* +* Since the driver is a simple pass-through mechanism between a protocol stack +* and the EMAC, no assembly or disassembly of Ethernet frames is done at the +* driver-level. This assumes that the protocol stack passes a correctly +* formatted Ethernet frame to the driver for transmission, and that the driver +* does not validate the contents of an incoming frame +* +* PHY Communication +* +* The driver provides rudimentary read and write functions to allow the higher +* layer software to access the PHY. The EMAC provides MII registers for the +* driver to access. This management interface can be parameterized away in the +* FPGA implementation process. If this is the case, the PHY read and write +* functions of the driver return XST_NO_FEATURE. +* +* External loopback is usually supported at the PHY. It is up to the user to +* turn external loopback on or off at the PHY. The driver simply provides pass- +* through functions for configuring the PHY. The driver does not read, write, +* or reset the PHY on its own. All control of the PHY must be done by the user. +* +* Asynchronous Callbacks +* +* The driver services interrupts and passes Ethernet frames to the higher layer +* software through asynchronous callback functions. When using the driver +* directly (i.e., not with the RTOS protocol stack), the higher layer +* software must register its callback functions during initialization. The +* driver requires callback functions for received frames, for confirmation of +* transmitted frames, and for asynchronous errors. +* +* Interrupts +* +* The driver has no dependencies on the interrupt controller. The driver +* provides two interrupt handlers. XEmac_IntrHandlerDma() handles interrupts +* when the EMAC is configured with scatter-gather DMA. XEmac_IntrHandlerFifo() +* handles interrupts when the EMAC is configured for direct FIFO I/O or simple +* DMA. Either of these routines can be connected to the system interrupt +* controller by the user. +* +* Interrupt Frequency +* +* When the EMAC is configured with scatter-gather DMA, the frequency of +* interrupts can be controlled with the interrupt coalescing features of the +* scatter-gather DMA engine. The frequency of interrupts can be adjusted using +* the driver API functions for setting the packet count threshold and the packet +* wait bound values. +* +* The scatter-gather DMA engine only interrupts when the packet count threshold +* is reached, instead of interrupting for each packet. A packet is a generic +* term used by the scatter-gather DMA engine, and is equivalent to an Ethernet +* frame in our case. +* +* The packet wait bound is a timer value used during interrupt coalescing to +* trigger an interrupt when not enough packets have been received to reach the +* packet count threshold. +* +* These values can be tuned by the user to meet their needs. If there appear to +* be interrupt latency problems or delays in packet arrival that are longer than +* might be expected, the user should verify that the packet count threshold is +* set low enough to receive interrupts before the wait bound timer goes off. +* +* Device Reset +* +* Some errors that can occur in the device require a device reset. These errors +* are listed in the XEmac_SetErrorHandler() function header. The user's error +* handler is responsible for resetting the device and re-configuring it based on +* its needs (the driver does not save the current configuration). When +* integrating into an RTOS, these reset and re-configure obligations are +* taken care of by the Xilinx adapter software if it exists for that RTOS. +* +* Device Configuration +* +* The device can be configured in various ways during the FPGA implementation +* process. Configuration parameters are stored in the xemac_g.c files. +* A table is defined where each entry contains configuration information +* for an EMAC device. This information includes such things as the base address +* of the memory-mapped device, the base addresses of IPIF, DMA, and FIFO modules +* within the device, and whether the device has DMA, counter registers, +* multicast support, MII support, and flow control. +* +* The driver tries to use the features built into the device. So if, for +* example, the hardware is configured with scatter-gather DMA, the driver +* expects to start the scatter-gather channels and expects that the user has set +* up the buffer descriptor lists already. If the user expects to use the driver +* in a mode different than how the hardware is configured, the user should +* modify the configuration table to reflect the mode to be used. Modifying the +* configuration table is a workaround for now until we get some experience with +* how users are intending to use the hardware in its different configurations. +* For example, if the hardware is built with scatter-gather DMA but the user is +* intending to use only simple DMA, the user either needs to modify the config +* table as a workaround or rebuild the hardware with only simple DMA. The +* recommendation at this point is to build the hardware with the features you +* intend to use. If you're inclined to modify the table, do so before the call +* to XEmac_Initialize(). Here is a snippet of code that changes a device to +* simple DMA (the hardware needs to have DMA for this to work of course): +*
+*        XEmac_Config *ConfigPtr;
+*
+*        ConfigPtr = XEmac_LookupConfig(DeviceId);
+*        ConfigPtr->IpIfDmaConfig = XEM_CFG_SIMPLE_DMA;
+* 
+* +* Simple DMA +* +* Simple DMA is supported through the FIFO functions, FifoSend and FifoRecv, of +* the driver (i.e., there is no separate interface for it). The driver makes use +* of the DMA engine for a simple DMA transfer if the device is configured with +* DMA, otherwise it uses the FIFOs directly. While the simple DMA interface is +* therefore transparent to the user, the caching of network buffers is not. +* If the device is configured with DMA and the FIFO interface is used, the user +* must ensure that the network buffers are not cached or are cache coherent, +* since DMA will be used to transfer to and from the Emac device. If the device +* is configured with DMA and the user really wants to use the FIFOs directly, +* the user should rebuild the hardware without DMA. If unable to do this, there +* is a workaround (described above in Device Configuration) to modify the +* configuration table of the driver to fake the driver into thinking the device +* has no DMA. A code snippet follows: +*
+*        XEmac_Config *ConfigPtr;
+*
+*        ConfigPtr = XEmac_LookupConfig(DeviceId);
+*        ConfigPtr->IpIfDmaConfig = XEM_CFG_NO_DMA;
+* 
+* +* Asserts +* +* Asserts are used within all Xilinx drivers to enforce constraints on argument +* values. Asserts can be turned off on a system-wide basis by defining, at +* compile time, the NDEBUG identifier. By default, asserts are turned on and it +* is recommended that users leave asserts on during development. +* +* Building the driver +* +* The XEmac driver is composed of several source files. Why so many? This +* allows the user to build and link only those parts of the driver that are +* necessary. Since the EMAC hardware can be configured in various ways (e.g., +* with or without DMA), the driver too can be built with varying features. +* For the most part, this means that besides always linking in xemac.c, you +* link in only the driver functionality you want. Some of the choices you have +* are polled vs. interrupt, interrupt with FIFOs only vs. interrupt with DMA, +* self-test diagnostics, and driver statistics. Note that currently the DMA code +* must be linked in, even if you don't have DMA in the device. +* +* @note +* +* Xilinx drivers are typically composed of two components, one is the driver +* and the other is the adapter. The driver is independent of OS and processor +* and is intended to be highly portable. The adapter is OS-specific and +* facilitates communication between the driver and an OS. +*

+* This driver is intended to be RTOS and processor independent. It works +* with physical addresses only. Any needs for dynamic memory management, +* threads or thread mutual exclusion, virtual memory, or cache control must +* be satisfied by the layer above this driver. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -------------------------------------------------------
+* 1.00a rpm  07/31/01 First release
+* 1.00b rpm  02/20/02 Repartitioned files and functions
+* 1.00b rpm  10/08/02 Replaced HasSgDma boolean with IpifDmaConfig enumerated
+*                     configuration parameter
+* 1.00c rpm  12/05/02 New version includes support for simple DMA and the delay
+*                     argument to SgSend
+* 1.00c rpm  02/03/03 The XST_DMA_SG_COUNT_EXCEEDED return code was removed
+*                     from SetPktThreshold in the internal DMA driver. Also
+*                     avoided compiler warnings by initializing Result in the
+*                     DMA interrupt service routines.
+* 
+* +******************************************************************************/ + +#ifndef XEMAC_H /* prevent circular inclusions */ +#define XEMAC_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xstatus.h" +#include "xparameters.h" +#include "xpacket_fifo_v1_00_b.h" /* Uses v1.00b of Packet Fifo */ +#include "xdma_channel.h" + +/************************** Constant Definitions *****************************/ + +/* + * Device information + */ +#define XEM_DEVICE_NAME "xemac" +#define XEM_DEVICE_DESC "Xilinx Ethernet 10/100 MAC" + +/** @name Configuration options + * + * Device configuration options (see the XEmac_SetOptions() and + * XEmac_GetOptions() for information on how to use these options) + * @{ + */ +/** + *
+ *   XEM_BROADCAST_OPTION        Broadcast addressing on or off (default is on)
+ *   XEM_UNICAST_OPTION          Unicast addressing on or off (default is on)
+ *   XEM_PROMISC_OPTION          Promiscuous addressing on or off (default is off)
+ *   XEM_FDUPLEX_OPTION          Full duplex on or off (default is off)
+ *   XEM_POLLED_OPTION           Polled mode on or off (default is off)
+ *   XEM_LOOPBACK_OPTION         Internal loopback on or off (default is off)
+ *   XEM_FLOW_CONTROL_OPTION     Interpret pause frames in full duplex mode
+ *                               (default is off)
+ *   XEM_INSERT_PAD_OPTION       Pad short frames on transmit (default is on)
+ *   XEM_INSERT_FCS_OPTION       Insert FCS (CRC) on transmit (default is on)
+ *   XEM_INSERT_ADDR_OPTION      Insert source address on transmit (default is on)
+ *   XEM_OVWRT_ADDR_OPTION       Overwrite source address on transmit. This is
+ *                               only used if source address insertion is on.
+ *                               (default is on)
+ *   XEM_STRIP_PAD_FCS_OPTION    Strip FCS and padding from received frames
+ *                               (default is off)
+  * 
+ */ +#define XEM_UNICAST_OPTION 0x00000001UL +#define XEM_BROADCAST_OPTION 0x00000002UL +#define XEM_PROMISC_OPTION 0x00000004UL +#define XEM_FDUPLEX_OPTION 0x00000008UL +#define XEM_POLLED_OPTION 0x00000010UL +#define XEM_LOOPBACK_OPTION 0x00000020UL +#define XEM_FLOW_CONTROL_OPTION 0x00000080UL +#define XEM_INSERT_PAD_OPTION 0x00000100UL +#define XEM_INSERT_FCS_OPTION 0x00000200UL +#define XEM_INSERT_ADDR_OPTION 0x00000400UL +#define XEM_OVWRT_ADDR_OPTION 0x00000800UL +#define XEM_STRIP_PAD_FCS_OPTION 0x00002000UL +/*@}*/ +/* + * Not supported yet: + * XEM_MULTICAST_OPTION Multicast addressing on or off (default is off) + */ +/* NOT SUPPORTED YET... */ +#define XEM_MULTICAST_OPTION 0x00000040UL + +/* + * Some default values for interrupt coalescing within the scatter-gather + * DMA engine. + */ +#define XEM_SGDMA_DFT_THRESHOLD 1 /* Default pkt threshold */ +#define XEM_SGDMA_MAX_THRESHOLD 255 /* Maximum pkt theshold */ +#define XEM_SGDMA_DFT_WAITBOUND 5 /* Default pkt wait bound (msec) */ +#define XEM_SGDMA_MAX_WAITBOUND 1023 /* Maximum pkt wait bound (msec) */ + +/* + * Direction identifiers. These are used for setting values like packet + * thresholds and wait bound for specific channels + */ +#define XEM_SEND 1 +#define XEM_RECV 2 + +/* + * Arguments to SgSend function to indicate whether to hold off starting + * the scatter-gather engine. + */ +#define XEM_SGDMA_NODELAY 0 /* start SG DMA immediately */ +#define XEM_SGDMA_DELAY 1 /* do not start SG DMA */ + +/* + * Constants to determine the configuration of the hardware device. They are + * used to allow the driver to verify it can operate with the hardware. + */ +#define XEM_CFG_NO_IPIF 0 /* Not supported by the driver */ +#define XEM_CFG_NO_DMA 1 /* No DMA */ +#define XEM_CFG_SIMPLE_DMA 2 /* Simple DMA */ +#define XEM_CFG_DMA_SG 3 /* DMA scatter gather */ + +/* + * The next few constants help upper layers determine the size of memory + * pools used for Ethernet buffers and descriptor lists. + */ +#define XEM_MAC_ADDR_SIZE 6 /* six-byte MAC address */ +#define XEM_MTU 1500 /* max size of Ethernet frame */ +#define XEM_HDR_SIZE 14 /* size of Ethernet header */ +#define XEM_HDR_VLAN_SIZE 18 /* size of Ethernet header with VLAN */ +#define XEM_TRL_SIZE 4 /* size of Ethernet trailer (FCS) */ +#define XEM_MAX_FRAME_SIZE (XEM_MTU + XEM_HDR_SIZE + XEM_TRL_SIZE) +#define XEM_MAX_VLAN_FRAME_SIZE (XEM_MTU + XEM_HDR_VLAN_SIZE + XEM_TRL_SIZE) + +/* + * Define a default number of send and receive buffers + */ +#define XEM_MIN_RECV_BUFS 32 /* minimum # of recv buffers */ +#define XEM_DFT_RECV_BUFS 64 /* default # of recv buffers */ + +#define XEM_MIN_SEND_BUFS 16 /* minimum # of send buffers */ +#define XEM_DFT_SEND_BUFS 32 /* default # of send buffers */ + +#define XEM_MIN_BUFFERS (XEM_MIN_RECV_BUFS + XEM_MIN_SEND_BUFS) +#define XEM_DFT_BUFFERS (XEM_DFT_RECV_BUFS + XEM_DFT_SEND_BUFS) + +/* + * Define the number of send and receive buffer descriptors, used for + * scatter-gather DMA + */ +#define XEM_MIN_RECV_DESC 16 /* minimum # of recv descriptors */ +#define XEM_DFT_RECV_DESC 32 /* default # of recv descriptors */ + +#define XEM_MIN_SEND_DESC 8 /* minimum # of send descriptors */ +#define XEM_DFT_SEND_DESC 16 /* default # of send descriptors */ + +/**************************** Type Definitions *******************************/ + +/** + * Ethernet statistics (see XEmac_GetStats() and XEmac_ClearStats()) + */ +typedef struct { + u32 XmitFrames; /**< Number of frames transmitted */ + u32 XmitBytes; /**< Number of bytes transmitted */ + u32 XmitLateCollisionErrors; + /**< Number of transmission failures + due to late collisions */ + u32 XmitExcessDeferral; /**< Number of transmission failures + due o excess collision deferrals */ + u32 XmitOverrunErrors; /**< Number of transmit overrun errors */ + u32 XmitUnderrunErrors; /**< Number of transmit underrun errors */ + u32 RecvFrames; /**< Number of frames received */ + u32 RecvBytes; /**< Number of bytes received */ + u32 RecvFcsErrors; /**< Number of frames discarded due + to FCS errors */ + u32 RecvAlignmentErrors; /**< Number of frames received with + alignment errors */ + u32 RecvOverrunErrors; /**< Number of frames discarded due + to overrun errors */ + u32 RecvUnderrunErrors; /**< Number of recv underrun errors */ + u32 RecvMissedFrameErrors; + /**< Number of frames missed by MAC */ + u32 RecvCollisionErrors; /**< Number of frames discarded due + to collisions */ + u32 RecvLengthFieldErrors; + /**< Number of frames discarded with + invalid length field */ + u32 RecvShortErrors; /**< Number of short frames discarded */ + u32 RecvLongErrors; /**< Number of long frames discarded */ + u32 DmaErrors; /**< Number of DMA errors since init */ + u32 FifoErrors; /**< Number of FIFO errors since init */ + u32 RecvInterrupts; /**< Number of receive interrupts */ + u32 XmitInterrupts; /**< Number of transmit interrupts */ + u32 EmacInterrupts; /**< Number of MAC (device) interrupts */ + u32 TotalIntrs; /**< Total interrupts */ +} XEmac_Stats; + +/** + * This typedef contains configuration information for a device. + */ +typedef struct { + u16 DeviceId; /**< Unique ID of device */ + u32 BaseAddress; /**< Register base address */ + u32 HasCounters; /**< Does device have counters? */ + u8 IpIfDmaConfig; /**< IPIF/DMA hardware configuration */ + u32 HasMii; /**< Does device support MII? */ + +} XEmac_Config; + +/** @name Typedefs for callbacks + * Callback functions. + * @{ + */ +/** + * Callback when data is sent or received with scatter-gather DMA. + * + * @param CallBackRef is a callback reference passed in by the upper layer + * when setting the callback functions, and passed back to the upper + * layer when the callback is invoked. + * @param BdPtr is a pointer to the first buffer descriptor in a list of + * buffer descriptors. + * @param NumBds is the number of buffer descriptors in the list pointed + * to by BdPtr. + */ +typedef void (*XEmac_SgHandler) (void *CallBackRef, XBufDescriptor * BdPtr, + u32 NumBds); + +/** + * Callback when data is sent or received with direct FIFO communication or + * simple DMA. The user typically defines two callacks, one for send and one + * for receive. + * + * @param CallBackRef is a callback reference passed in by the upper layer + * when setting the callback functions, and passed back to the upper + * layer when the callback is invoked. + */ +typedef void (*XEmac_FifoHandler) (void *CallBackRef); + +/** + * Callback when an asynchronous error occurs. + * + * @param CallBackRef is a callback reference passed in by the upper layer + * when setting the callback functions, and passed back to the upper + * layer when the callback is invoked. + * @param ErrorCode is a Xilinx error code defined in xstatus.h. Also see + * XEmac_SetErrorHandler() for a description of possible errors. + */ +typedef void (*XEmac_ErrorHandler) (void *CallBackRef, XStatus ErrorCode); +/*@}*/ + +/** + * The XEmac driver instance data. The user is required to allocate a + * variable of this type for every EMAC device in the system. A pointer + * to a variable of this type is then passed to the driver API functions. + */ +typedef struct { + u32 BaseAddress; /* Base address (of IPIF) */ + u32 IsStarted; /* Device is currently started */ + u32 IsReady; /* Device is initialized and ready */ + u32 IsPolled; /* Device is in polled mode */ + u8 IpIfDmaConfig; /* IPIF/DMA hardware configuration */ + u32 HasMii; /* Does device support MII? */ + u32 HasMulticastHash; /* Does device support multicast hash table? */ + + XEmac_Stats Stats; + XPacketFifoV100b RecvFifo; /* FIFO used to receive frames */ + XPacketFifoV100b SendFifo; /* FIFO used to send frames */ + + /* + * Callbacks + */ + XEmac_FifoHandler FifoRecvHandler; /* for non-DMA/simple DMA interrupts */ + void *FifoRecvRef; + XEmac_FifoHandler FifoSendHandler; /* for non-DMA/simple DMA interrupts */ + void *FifoSendRef; + XEmac_ErrorHandler ErrorHandler; /* for asynchronous errors */ + void *ErrorRef; + + XDmaChannel RecvChannel; /* DMA receive channel driver */ + XDmaChannel SendChannel; /* DMA send channel driver */ + + XEmac_SgHandler SgRecvHandler; /* callback for scatter-gather DMA */ + void *SgRecvRef; + XEmac_SgHandler SgSendHandler; /* callback for scatter-gather DMA */ + void *SgSendRef; +} XEmac; + +/***************** Macros (Inline Functions) Definitions *********************/ + +/*****************************************************************************/ +/** +* +* This macro determines if the device is currently configured for +* scatter-gather DMA. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* Boolean TRUE if the device is configured for scatter-gather DMA, or FALSE +* if it is not. +* +* @note +* +* Signature: u32 XEmac_mIsSgDma(XEmac *InstancePtr) +* +******************************************************************************/ +#define XEmac_mIsSgDma(InstancePtr) \ + ((InstancePtr)->IpIfDmaConfig == XEM_CFG_DMA_SG) + +/*****************************************************************************/ +/** +* +* This macro determines if the device is currently configured for simple DMA. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* Boolean TRUE if the device is configured for simple DMA, or FALSE otherwise +* +* @note +* +* Signature: u32 XEmac_mIsSimpleDma(XEmac *InstancePtr) +* +******************************************************************************/ +#define XEmac_mIsSimpleDma(InstancePtr) \ + ((InstancePtr)->IpIfDmaConfig == XEM_CFG_SIMPLE_DMA) + +/*****************************************************************************/ +/** +* +* This macro determines if the device is currently configured with DMA (either +* simple DMA or scatter-gather DMA) +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* Boolean TRUE if the device is configured with DMA, or FALSE otherwise +* +* @note +* +* Signature: u32 XEmac_mIsDma(XEmac *InstancePtr) +* +******************************************************************************/ +#define XEmac_mIsDma(InstancePtr) \ + (XEmac_mIsSimpleDma(InstancePtr) || XEmac_mIsSgDma(InstancePtr)) + +/************************** Function Prototypes ******************************/ + +/* + * Initialization functions in xemac.c + */ +XStatus XEmac_Initialize(XEmac * InstancePtr, u16 DeviceId); +XStatus XEmac_Start(XEmac * InstancePtr); +XStatus XEmac_Stop(XEmac * InstancePtr); +void XEmac_Reset(XEmac * InstancePtr); +XEmac_Config *XEmac_LookupConfig(u16 DeviceId); + +/* + * Diagnostic functions in xemac_selftest.c + */ +XStatus XEmac_SelfTest(XEmac * InstancePtr); + +/* + * Polled functions in xemac_polled.c + */ +XStatus XEmac_PollSend(XEmac * InstancePtr, u8 * BufPtr, u32 ByteCount); +XStatus XEmac_PollRecv(XEmac * InstancePtr, u8 * BufPtr, u32 * ByteCountPtr); + +/* + * Interrupts with scatter-gather DMA functions in xemac_intr_dma.c + */ +XStatus XEmac_SgSend(XEmac * InstancePtr, XBufDescriptor * BdPtr, int Delay); +XStatus XEmac_SgRecv(XEmac * InstancePtr, XBufDescriptor * BdPtr); +XStatus XEmac_SetPktThreshold(XEmac * InstancePtr, u32 Direction, u8 Threshold); +XStatus XEmac_GetPktThreshold(XEmac * InstancePtr, u32 Direction, + u8 * ThreshPtr); +XStatus XEmac_SetPktWaitBound(XEmac * InstancePtr, u32 Direction, + u32 TimerValue); +XStatus XEmac_GetPktWaitBound(XEmac * InstancePtr, u32 Direction, + u32 * WaitPtr); +XStatus XEmac_SetSgRecvSpace(XEmac * InstancePtr, u32 * MemoryPtr, + u32 ByteCount); +XStatus XEmac_SetSgSendSpace(XEmac * InstancePtr, u32 * MemoryPtr, + u32 ByteCount); +void XEmac_SetSgRecvHandler(XEmac * InstancePtr, void *CallBackRef, + XEmac_SgHandler FuncPtr); +void XEmac_SetSgSendHandler(XEmac * InstancePtr, void *CallBackRef, + XEmac_SgHandler FuncPtr); + +void XEmac_IntrHandlerDma(void *InstancePtr); /* interrupt handler */ + +/* + * Interrupts with direct FIFO functions in xemac_intr_fifo.c. Also used + * for simple DMA. + */ +XStatus XEmac_FifoSend(XEmac * InstancePtr, u8 * BufPtr, u32 ByteCount); +XStatus XEmac_FifoRecv(XEmac * InstancePtr, u8 * BufPtr, u32 * ByteCountPtr); +void XEmac_SetFifoRecvHandler(XEmac * InstancePtr, void *CallBackRef, + XEmac_FifoHandler FuncPtr); +void XEmac_SetFifoSendHandler(XEmac * InstancePtr, void *CallBackRef, + XEmac_FifoHandler FuncPtr); + +void XEmac_IntrHandlerFifo(void *InstancePtr); /* interrupt handler */ + +/* + * General interrupt-related functions in xemac_intr.c + */ +void XEmac_SetErrorHandler(XEmac * InstancePtr, void *CallBackRef, + XEmac_ErrorHandler FuncPtr); + +/* + * MAC configuration in xemac_options.c + */ +XStatus XEmac_SetOptions(XEmac * InstancePtr, u32 OptionFlag); +u32 XEmac_GetOptions(XEmac * InstancePtr); +XStatus XEmac_SetMacAddress(XEmac * InstancePtr, u8 * AddressPtr); +void XEmac_GetMacAddress(XEmac * InstancePtr, u8 * BufferPtr); +XStatus XEmac_SetInterframeGap(XEmac * InstancePtr, u8 Part1, u8 Part2); +void XEmac_GetInterframeGap(XEmac * InstancePtr, u8 * Part1Ptr, u8 * Part2Ptr); + +/* + * Multicast functions in xemac_multicast.c (not supported by EMAC yet) + */ +XStatus XEmac_MulticastAdd(XEmac * InstancePtr, u8 * AddressPtr); +XStatus XEmac_MulticastClear(XEmac * InstancePtr); + +/* + * PHY configuration in xemac_phy.c + */ +XStatus XEmac_PhyRead(XEmac * InstancePtr, u32 PhyAddress, + u32 RegisterNum, u16 * PhyDataPtr); +XStatus XEmac_PhyWrite(XEmac * InstancePtr, u32 PhyAddress, + u32 RegisterNum, u16 PhyData); + +/* + * Statistics in xemac_stats.c + */ +void XEmac_GetStats(XEmac * InstancePtr, XEmac_Stats * StatsPtr); +void XEmac_ClearStats(XEmac * InstancePtr); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/drivers/net/xilinx_enet/xemac_g.c linuxppc-2.4/drivers/net/xilinx_enet/xemac_g.c --- linux-2.4.22/drivers/net/xilinx_enet/xemac_g.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/net/xilinx_enet/xemac_g.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,61 @@ + +/******************************************************************* +* +* CAUTION: This file is automatically generated by libgen. +* Version: Xilinx EDK 3.2.2 EDK_Cm.22 +* DO NOT EDIT. +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +* Description: Driver configuration +* +*******************************************************************/ + +#include "xparameters.h" +#include "xemac.h" + +/* +* The configuration table for devices +*/ + +XEmac_Config XEmac_ConfigTable[] = { + { + XPAR_OPB_ETHERNET_0_DEVICE_ID, + XPAR_OPB_ETHERNET_0_BASEADDR, + XPAR_OPB_ETHERNET_0_ERR_COUNT_EXIST, + XPAR_OPB_ETHERNET_0_DMA_PRESENT, + XPAR_OPB_ETHERNET_0_MII_EXIST} +}; diff -urN -X bkexcl linux-2.4.22/drivers/net/xilinx_enet/xemac_i.h linuxppc-2.4/drivers/net/xilinx_enet/xemac_i.h --- linux-2.4.22/drivers/net/xilinx_enet/xemac_i.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/net/xilinx_enet/xemac_i.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,208 @@ +/* $Id: xemac_i.h,v 1.1 2002/12/09 17:42:43 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xemac_i.h +* +* This header file contains internal identifiers, which are those shared +* between XEmac components. The identifiers in this file are not intended for +* use external to the driver. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rpm  07/31/01 First release
+* 1.00b rpm  02/20/02 Repartitioned files and functions
+* 1.00b rpm  04/29/02 Moved register definitions to xemac_l.h
+* 1.00c rpm  12/05/02 New version includes support for simple DMA
+* 
+* +******************************************************************************/ + +#ifndef XEMAC_I_H /* prevent circular inclusions */ +#define XEMAC_I_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xemac.h" +#include "xemac_l.h" + +/************************** Constant Definitions *****************************/ + +/* + * Default buffer descriptor control word masks. The default send BD control + * is set for incrementing the source address by one for each byte transferred, + * and specify that the destination address (FIFO) is local to the device. The + * default receive BD control is set for incrementing the destination address + * by one for each byte transferred, and specify that the source address is + * local to the device. + */ +#define XEM_DFT_SEND_BD_MASK (XDC_DMACR_SOURCE_INCR_MASK | \ + XDC_DMACR_DEST_LOCAL_MASK) +#define XEM_DFT_RECV_BD_MASK (XDC_DMACR_DEST_INCR_MASK | \ + XDC_DMACR_SOURCE_LOCAL_MASK) + +/* + * Masks for the IPIF Device Interrupt enable and status registers. + */ +#define XEM_IPIF_EMAC_MASK 0x00000004UL /* MAC interrupt */ +#define XEM_IPIF_SEND_DMA_MASK 0x00000008UL /* Send DMA interrupt */ +#define XEM_IPIF_RECV_DMA_MASK 0x00000010UL /* Receive DMA interrupt */ +#define XEM_IPIF_RECV_FIFO_MASK 0x00000020UL /* Receive FIFO interrupt */ +#define XEM_IPIF_SEND_FIFO_MASK 0x00000040UL /* Send FIFO interrupt */ + +/* + * Default IPIF Device Interrupt mask when configured for DMA + */ +#define XEM_IPIF_DMA_DFT_MASK (XEM_IPIF_SEND_DMA_MASK | \ + XEM_IPIF_RECV_DMA_MASK | \ + XEM_IPIF_EMAC_MASK | \ + XEM_IPIF_SEND_FIFO_MASK | \ + XEM_IPIF_RECV_FIFO_MASK) + +/* + * Default IPIF Device Interrupt mask when configured without DMA + */ +#define XEM_IPIF_FIFO_DFT_MASK (XEM_IPIF_EMAC_MASK | \ + XEM_IPIF_SEND_FIFO_MASK | \ + XEM_IPIF_RECV_FIFO_MASK) + +#define XEM_IPIF_DMA_DEV_INTR_COUNT 7 /* Number of interrupt sources */ +#define XEM_IPIF_FIFO_DEV_INTR_COUNT 5 /* Number of interrupt sources */ +#define XEM_IPIF_DEVICE_INTR_COUNT 7 /* Number of interrupt sources */ +#define XEM_IPIF_IP_INTR_COUNT 22 /* Number of MAC interrupts */ + +/* a mask for all transmit interrupts, used in polled mode */ +#define XEM_EIR_XMIT_ALL_MASK (XEM_EIR_XMIT_DONE_MASK | \ + XEM_EIR_XMIT_ERROR_MASK | \ + XEM_EIR_XMIT_SFIFO_EMPTY_MASK | \ + XEM_EIR_XMIT_LFIFO_FULL_MASK) + +/* a mask for all receive interrupts, used in polled mode */ +#define XEM_EIR_RECV_ALL_MASK (XEM_EIR_RECV_DONE_MASK | \ + XEM_EIR_RECV_ERROR_MASK | \ + XEM_EIR_RECV_LFIFO_EMPTY_MASK | \ + XEM_EIR_RECV_LFIFO_OVER_MASK | \ + XEM_EIR_RECV_LFIFO_UNDER_MASK | \ + XEM_EIR_RECV_DFIFO_OVER_MASK | \ + XEM_EIR_RECV_MISSED_FRAME_MASK | \ + XEM_EIR_RECV_COLLISION_MASK | \ + XEM_EIR_RECV_FCS_ERROR_MASK | \ + XEM_EIR_RECV_LEN_ERROR_MASK | \ + XEM_EIR_RECV_SHORT_ERROR_MASK | \ + XEM_EIR_RECV_LONG_ERROR_MASK | \ + XEM_EIR_RECV_ALIGN_ERROR_MASK) + +/* a default interrupt mask for scatter-gather DMA operation */ +#define XEM_EIR_DFT_SG_MASK (XEM_EIR_RECV_ERROR_MASK | \ + XEM_EIR_RECV_LFIFO_OVER_MASK | \ + XEM_EIR_RECV_LFIFO_UNDER_MASK | \ + XEM_EIR_XMIT_SFIFO_OVER_MASK | \ + XEM_EIR_XMIT_SFIFO_UNDER_MASK | \ + XEM_EIR_XMIT_LFIFO_OVER_MASK | \ + XEM_EIR_XMIT_LFIFO_UNDER_MASK | \ + XEM_EIR_RECV_DFIFO_OVER_MASK | \ + XEM_EIR_RECV_MISSED_FRAME_MASK | \ + XEM_EIR_RECV_COLLISION_MASK | \ + XEM_EIR_RECV_FCS_ERROR_MASK | \ + XEM_EIR_RECV_LEN_ERROR_MASK | \ + XEM_EIR_RECV_SHORT_ERROR_MASK | \ + XEM_EIR_RECV_LONG_ERROR_MASK | \ + XEM_EIR_RECV_ALIGN_ERROR_MASK) + +/* a default interrupt mask for non-DMA operation (direct FIFOs) */ +#define XEM_EIR_DFT_FIFO_MASK (XEM_EIR_XMIT_DONE_MASK | \ + XEM_EIR_RECV_DONE_MASK | \ + XEM_EIR_DFT_SG_MASK) + +/* + * Mask for the DMA interrupt enable and status registers when configured + * for scatter-gather DMA. + */ +#define XEM_DMA_SG_INTR_MASK (XDC_IXR_DMA_ERROR_MASK | \ + XDC_IXR_PKT_THRESHOLD_MASK | \ + XDC_IXR_PKT_WAIT_BOUND_MASK | \ + XDC_IXR_SG_END_MASK) + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/*****************************************************************************/ +/* +* +* Clears a structure of given size, in bytes, by setting each byte to 0. +* +* @param StructPtr is a pointer to the structure to be cleared. +* @param NumBytes is the number of bytes in the structure. +* +* @return +* +* None. +* +* @note +* +* Signature: void XEmac_mClearStruct(u8 *StructPtr, unsigned int NumBytes) +* +******************************************************************************/ +#define XEmac_mClearStruct(StructPtr, NumBytes) \ +{ \ + int i; \ + u8 *BytePtr = (u8 *)(StructPtr); \ + for (i=0; i < (unsigned int)(NumBytes); i++) \ + { \ + *BytePtr++ = 0; \ + } \ +} + +/************************** Variable Definitions *****************************/ + +extern XEmac_Config XEmac_ConfigTable[]; + +/************************** Function Prototypes ******************************/ + +void XEmac_CheckEmacError(XEmac * InstancePtr, u32 IntrStatus); +void XEmac_CheckFifoRecvError(XEmac * InstancePtr); +void XEmac_CheckFifoSendError(XEmac * InstancePtr); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/drivers/net/xilinx_enet/xemac_intr.c linuxppc-2.4/drivers/net/xilinx_enet/xemac_intr.c --- linux-2.4.22/drivers/net/xilinx_enet/xemac_intr.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/net/xilinx_enet/xemac_intr.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,403 @@ +/* $Id: xemac_intr.c,v 1.2 2003/03/31 17:19:32 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xemac_intr.c +* +* This file contains general interrupt-related functions of the XEmac driver. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rpm  07/31/01 First release
+* 1.00b rpm  02/20/02 Repartitioned files and functions
+* 1.00c rpm  12/05/02 New version includes support for simple DMA
+* 1.00c rpm  03/31/03 Added comment to indicate that no Receive Length FIFO
+*                     overrun interrupts occur in v1.00l and later of the EMAC
+*                     device. This avoids the need to reset the device on
+*                     receive overruns.
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xemac_i.h" +#include "xio.h" +#include "xipif_v1_23_b.h" /* Uses v1.23b of the IPIF */ + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + +/*****************************************************************************/ +/** +* +* Set the callback function for handling asynchronous errors. The upper layer +* software should call this function during initialization. +* +* The error callback is invoked by the driver within interrupt context, so it +* needs to do its job quickly. If there are potentially slow operations within +* the callback, these should be done at task-level. +* +* The Xilinx errors that must be handled by the callback are: +* - XST_DMA_ERROR indicates an unrecoverable DMA error occurred. This is +* typically a bus error or bus timeout. The handler must reset and +* re-configure the device. +* - XST_FIFO_ERROR indicates an unrecoverable FIFO error occurred. This is a +* deadlock condition in the packet FIFO. The handler must reset and +* re-configure the device. +* - XST_RESET_ERROR indicates an unrecoverable MAC error occurred, usually an +* overrun or underrun. The handler must reset and re-configure the device. +* - XST_DMA_SG_NO_LIST indicates an attempt was made to access a scatter-gather +* DMA list that has not yet been created. +* - XST_DMA_SG_LIST_EMPTY indicates the driver tried to get a descriptor from +* the receive descriptor list, but the list was empty. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param CallBackRef is a reference pointer to be passed back to the adapter in +* the callback. This helps the adapter correlate the callback to a +* particular driver. +* @param FuncPtr is the pointer to the callback function. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XEmac_SetErrorHandler(XEmac * InstancePtr, void *CallBackRef, + XEmac_ErrorHandler FuncPtr) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(FuncPtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + InstancePtr->ErrorHandler = FuncPtr; + InstancePtr->ErrorRef = CallBackRef; +} + +/****************************************************************************/ +/* +* +* Check the interrupt status bits of the Ethernet MAC for errors. Errors +* currently handled are: +* - Receive length FIFO overrun. Indicates data was lost due to the receive +* length FIFO becoming full during the reception of a packet. Only a device +* reset clears this condition. +* - Receive length FIFO underrun. An attempt to read an empty FIFO. Only a +* device reset clears this condition. +* - Transmit status FIFO overrun. Indicates data was lost due to the transmit +* status FIFO becoming full following the transmission of a packet. Only a +* device reset clears this condition. +* - Transmit status FIFO underrun. An attempt to read an empty FIFO. Only a +* device reset clears this condition. +* - Transmit length FIFO overrun. Indicates data was lost due to the transmit +* length FIFO becoming full following the transmission of a packet. Only a +* device reset clears this condition. +* - Transmit length FIFO underrun. An attempt to read an empty FIFO. Only a +* device reset clears this condition. +* - Receive data FIFO overrun. Indicates data was lost due to the receive data +* FIFO becoming full during the reception of a packet. +* - Receive data errors: +* - Receive missed frame error. Valid data was lost by the MAC. +* - Receive collision error. Data was lost by the MAC due to a collision. +* - Receive FCS error. Data was dicarded by the MAC due to FCS error. +* - Receive length field error. Data was dicarded by the MAC due to an invalid +* length field in the packet. +* - Receive short error. Data was dicarded by the MAC because a packet was +* shorter than allowed. +* - Receive long error. Data was dicarded by the MAC because a packet was +* longer than allowed. +* - Receive alignment error. Data was truncated by the MAC because its length +* was not byte-aligned. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param IntrStatus is the contents of the interrupt status register to be checked +* +* @return +* +* None. +* +* @note +* +* This function is intended for internal use only. +* +******************************************************************************/ +void +XEmac_CheckEmacError(XEmac * InstancePtr, u32 IntrStatus) +{ + u32 ResetError = FALSE; + + /* + * First check for receive fifo overrun/underrun errors. Most require a + * reset by the user to clear, but the data FIFO overrun error does not. + */ + if (IntrStatus & XEM_EIR_RECV_DFIFO_OVER_MASK) { + InstancePtr->Stats.RecvOverrunErrors++; + InstancePtr->Stats.FifoErrors++; + } + + if (IntrStatus & XEM_EIR_RECV_LFIFO_OVER_MASK) { + /* + * Receive Length FIFO overrun interrupts no longer occur in v1.00l + * and later of the EMAC device. Frames are just dropped by the EMAC + * if the length FIFO is full. The user would notice the Receive Missed + * Frame count incrementing without any other errors being reported. + * This code is left here for backward compatibility with v1.00k and + * older EMAC devices. + */ + InstancePtr->Stats.RecvOverrunErrors++; + InstancePtr->Stats.FifoErrors++; + ResetError = TRUE; /* requires a reset */ + } + + if (IntrStatus & XEM_EIR_RECV_LFIFO_UNDER_MASK) { + InstancePtr->Stats.RecvUnderrunErrors++; + InstancePtr->Stats.FifoErrors++; + ResetError = TRUE; /* requires a reset */ + } + + /* + * Now check for general receive errors. Get the latest count where + * available, otherwise just bump the statistic so we know the interrupt + * occurred. + */ + if (IntrStatus & XEM_EIR_RECV_ERROR_MASK) { + if (IntrStatus & XEM_EIR_RECV_MISSED_FRAME_MASK) { + /* + * Caused by length FIFO or data FIFO overruns on receive side + */ + InstancePtr->Stats.RecvMissedFrameErrors = + XIo_In32(InstancePtr->BaseAddress + + XEM_RMFC_OFFSET); + } + + if (IntrStatus & XEM_EIR_RECV_COLLISION_MASK) { + InstancePtr->Stats.RecvCollisionErrors = + XIo_In32(InstancePtr->BaseAddress + XEM_RCC_OFFSET); + } + + if (IntrStatus & XEM_EIR_RECV_FCS_ERROR_MASK) { + InstancePtr->Stats.RecvFcsErrors = + XIo_In32(InstancePtr->BaseAddress + + XEM_RFCSEC_OFFSET); + } + + if (IntrStatus & XEM_EIR_RECV_LEN_ERROR_MASK) { + InstancePtr->Stats.RecvLengthFieldErrors++; + } + + if (IntrStatus & XEM_EIR_RECV_SHORT_ERROR_MASK) { + InstancePtr->Stats.RecvShortErrors++; + } + + if (IntrStatus & XEM_EIR_RECV_LONG_ERROR_MASK) { + InstancePtr->Stats.RecvLongErrors++; + } + + if (IntrStatus & XEM_EIR_RECV_ALIGN_ERROR_MASK) { + InstancePtr->Stats.RecvAlignmentErrors = + XIo_In32(InstancePtr->BaseAddress + + XEM_RAEC_OFFSET); + } + + /* + * Bump recv interrupts stats only if not scatter-gather DMA (this + * stat gets bumped elsewhere in that case) + */ + if (!XEmac_mIsSgDma(InstancePtr)) { + InstancePtr->Stats.RecvInterrupts++; /* TODO: double bump? */ + } + + } + + /* + * Check for transmit errors. These apply to both DMA and non-DMA modes + * of operation. The entire device should be reset after overruns or + * underruns. + */ + if (IntrStatus & (XEM_EIR_XMIT_SFIFO_OVER_MASK | + XEM_EIR_XMIT_LFIFO_OVER_MASK)) { + InstancePtr->Stats.XmitOverrunErrors++; + InstancePtr->Stats.FifoErrors++; + ResetError = TRUE; + } + + if (IntrStatus & (XEM_EIR_XMIT_SFIFO_UNDER_MASK | + XEM_EIR_XMIT_LFIFO_UNDER_MASK)) { + InstancePtr->Stats.XmitUnderrunErrors++; + InstancePtr->Stats.FifoErrors++; + ResetError = TRUE; + } + + if (ResetError) { + /* + * If a reset error occurred, disable the EMAC interrupts since the + * reset-causing interrupt(s) is latched in the EMAC - meaning it will + * keep occurring until the device is reset. In order to give the higher + * layer software time to reset the device, we have to disable the + * overrun/underrun interrupts until that happens. We trust that the + * higher layer resets the device. We are able to get away with disabling + * all EMAC interrupts since the only interrupts it generates are for + * error conditions, and we don't care about any more errors right now. + */ + XIIF_V123B_WRITE_IIER(InstancePtr->BaseAddress, 0); + + /* + * Invoke the error handler callback, which should result in a reset + * of the device by the upper layer software. + */ + InstancePtr->ErrorHandler(InstancePtr->ErrorRef, + XST_RESET_ERROR); + } +} + +/*****************************************************************************/ +/* +* +* Check the receive packet FIFO for errors. FIFO error interrupts are: +* - Deadlock. See the XPacketFifo component for a description of deadlock on a +* FIFO. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* Although the function returns void, it can return an asynchronous error to the +* application through the error handler. It can return XST_FIFO_ERROR if a FIFO +* error occurred. +* +* @note +* +* This function is intended for internal use only. +* +******************************************************************************/ +void +XEmac_CheckFifoRecvError(XEmac * InstancePtr) +{ + /* + * Although the deadlock is currently the only interrupt from a packet + * FIFO, make sure it is deadlocked before taking action. There is no + * need to clear this interrupt since it requires a reset of the device. + */ + if (XPF_V100B_IS_DEADLOCKED(&InstancePtr->RecvFifo)) { + u32 IntrEnable; + + InstancePtr->Stats.FifoErrors++; + + /* + * Invoke the error callback function, which should result in a reset + * of the device by the upper layer software. We first need to disable + * the FIFO interrupt, since otherwise the upper layer thread that + * handles the reset may never run because this interrupt condition + * doesn't go away until a reset occurs (there is no way to ack it). + */ + IntrEnable = XIIF_V123B_READ_DIER(InstancePtr->BaseAddress); + XIIF_V123B_WRITE_DIER(InstancePtr->BaseAddress, + IntrEnable & ~XEM_IPIF_RECV_FIFO_MASK); + + InstancePtr->ErrorHandler(InstancePtr->ErrorRef, + XST_FIFO_ERROR); + } +} + +/*****************************************************************************/ +/* +* +* Check the send packet FIFO for errors. FIFO error interrupts are: +* - Deadlock. See the XPacketFifo component for a description of deadlock on a +* FIFO. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* Although the function returns void, it can return an asynchronous error to the +* application through the error handler. It can return XST_FIFO_ERROR if a FIFO +* error occurred. +* +* @note +* +* This function is intended for internal use only. +* +******************************************************************************/ +void +XEmac_CheckFifoSendError(XEmac * InstancePtr) +{ + /* + * Although the deadlock is currently the only interrupt from a packet + * FIFO, make sure it is deadlocked before taking action. There is no + * need to clear this interrupt since it requires a reset of the device. + */ + if (XPF_V100B_IS_DEADLOCKED(&InstancePtr->SendFifo)) { + u32 IntrEnable; + + InstancePtr->Stats.FifoErrors++; + + /* + * Invoke the error callback function, which should result in a reset + * of the device by the upper layer software. We first need to disable + * the FIFO interrupt, since otherwise the upper layer thread that + * handles the reset may never run because this interrupt condition + * doesn't go away until a reset occurs (there is no way to ack it). + */ + IntrEnable = XIIF_V123B_READ_DIER(InstancePtr->BaseAddress); + XIIF_V123B_WRITE_DIER(InstancePtr->BaseAddress, + IntrEnable & ~XEM_IPIF_SEND_FIFO_MASK); + + InstancePtr->ErrorHandler(InstancePtr->ErrorRef, + XST_FIFO_ERROR); + } +} diff -urN -X bkexcl linux-2.4.22/drivers/net/xilinx_enet/xemac_intr_dma.c linuxppc-2.4/drivers/net/xilinx_enet/xemac_intr_dma.c --- linux-2.4.22/drivers/net/xilinx_enet/xemac_intr_dma.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/net/xilinx_enet/xemac_intr_dma.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,1345 @@ +/* $Id: xemac_intr_dma.c,v 1.3 2003/03/26 17:29:24 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xemac_intr_dma.c +* +* Contains functions used in interrupt mode when configured with scatter-gather +* DMA. +* +* The interrupt handler, XEmac_IntrHandlerDma(), must be connected by the user +* to the interrupt controller. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- ---------------------------------------------------------
+* 1.00a rpm  07/31/01 First release
+* 1.00b rpm  02/20/02 Repartitioned files and functions
+* 1.00c rpm  12/05/02 New version includes support for simple DMA and the delay
+*                     argument to SgSend
+* 1.00c rpm  02/03/03 The XST_DMA_SG_COUNT_EXCEEDED return code was removed
+*                     from SetPktThreshold in the internal DMA driver. Also
+*                     avoided compiler warnings by initializing Result in the
+*                     interrupt service routines.
+* 1.00c rpm  03/26/03 Fixed a problem in the interrupt service routines where
+*                     the interrupt status was toggled clear after a call to
+*                     ErrorHandler, but if ErrorHandler reset the device the
+*                     toggle actually asserted the interrupt because the
+*                     reset had cleared it.
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xemac_i.h" +#include "xio.h" +#include "xbuf_descriptor.h" +#include "xdma_channel.h" +#include "xipif_v1_23_b.h" /* Uses v1.23b of the IPIF */ + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + +static void HandleDmaRecvIntr(XEmac * InstancePtr); +static void HandleDmaSendIntr(XEmac * InstancePtr); +static void HandleEmacDmaIntr(XEmac * InstancePtr); + +/*****************************************************************************/ +/** +* +* Send an Ethernet frame using scatter-gather DMA. The caller attaches the +* frame to one or more buffer descriptors, then calls this function once for +* each descriptor. The caller is responsible for allocating and setting up the +* descriptor. An entire Ethernet frame may or may not be contained within one +* descriptor. This function simply inserts the descriptor into the scatter- +* gather engine's transmit list. The caller is responsible for providing mutual +* exclusion to guarantee that a frame is contiguous in the transmit list. The +* buffer attached to the descriptor must be word-aligned. +* +* The driver updates the descriptor with the device control register before +* being inserted into the transmit list. If this is the last descriptor in +* the frame, the inserts are committed, which means the descriptors for this +* frame are now available for transmission. +* +* It is assumed that the upper layer software supplies a correctly formatted +* Ethernet frame, including the destination and source addresses, the +* type/length field, and the data field. It is also assumed that upper layer +* software does not append FCS at the end of the frame. +* +* The buffer attached to the descriptor must be word-aligned on the front end. +* +* This call is non-blocking. Notification of error or successful transmission +* is done asynchronously through the send or error callback function. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param BdPtr is the address of a descriptor to be inserted into the transmit +* ring. +* @param Delay indicates whether to start the scatter-gather DMA channel +* immediately, or whether to wait. This allows the user to build up a +* list of more than one descriptor before starting the transmission of +* the packets, which allows the application to keep up with DMA and have +* a constant stream of frames being transmitted. Use XEM_SGDMA_NODELAY or +* XEM_SGDMA_DELAY, defined in xemac.h, as the value of this argument. If +* the user chooses to delay and build a list, the user must call this +* function with the XEM_SGDMA_NODELAY option or call XEmac_Start() to +* kick off the tranmissions. +* +* @return +* +* - XST_SUCCESS if the buffer was successfull sent +* - XST_DEVICE_IS_STOPPED if the Ethernet MAC has not been started yet +* - XST_NOT_SGDMA if the device is not in scatter-gather DMA mode +* - XST_DMA_SG_LIST_FULL if the descriptor list for the DMA channel is full +* - XST_DMA_SG_BD_LOCKED if the DMA channel cannot insert the descriptor into +* the list because a locked descriptor exists at the insert point +* - XST_DMA_SG_NOTHING_TO_COMMIT if even after inserting a descriptor into the +* list, the DMA channel believes there are no new descriptors to commit. If +* this is ever encountered, there is likely a thread mutual exclusion problem +* on transmit. +* +* @note +* +* This function is not thread-safe. The user must provide mutually exclusive +* access to this function if there are to be multiple threads that can call it. +* +* @internal +* +* A status that should never be returned from this function, although +* the code is set up to handle it, is XST_DMA_SG_NO_LIST. Starting the device +* requires a list to be created, and this function requires the device to be +* started. +* +******************************************************************************/ +XStatus +XEmac_SgSend(XEmac * InstancePtr, XBufDescriptor * BdPtr, int Delay) +{ + XStatus Result; + u32 BdControl; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(BdPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Be sure the device is configured for scatter-gather DMA, then be sure + * it is started. + */ + if (!XEmac_mIsSgDma(InstancePtr)) { + return XST_NOT_SGDMA; + } + + /* + * Set some descriptor control word defaults (source address increment + * and local destination address) and the destination address + * (the FIFO). These are the same for every transmit descriptor. + */ + BdControl = XBufDescriptor_GetControl(BdPtr); + XBufDescriptor_SetControl(BdPtr, BdControl | XEM_DFT_SEND_BD_MASK); + + XBufDescriptor_SetDestAddress(BdPtr, + InstancePtr->BaseAddress + + XEM_PFIFO_TXDATA_OFFSET); + + /* + * Put the descriptor in the send list. The DMA component accesses data + * here that can also be modified in interrupt context, so a critical + * section is required. + */ + XIIF_V123B_GINTR_DISABLE(InstancePtr->BaseAddress); + + Result = XDmaChannel_PutDescriptor(&InstancePtr->SendChannel, BdPtr); + if (Result != XST_SUCCESS) { + XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); + return Result; + } + + /* + * If this is the last buffer in the frame, commit the inserts and start + * the DMA engine if necessary + */ + if (XBufDescriptor_IsLastControl(BdPtr)) { + Result = XDmaChannel_CommitPuts(&InstancePtr->SendChannel); + if (Result != XST_SUCCESS) { + XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); + return Result; + } + + if (Delay == XEM_SGDMA_NODELAY) { + /* + * Start the DMA channel. Ignore the return status since we know the + * list exists and has at least one entry and we don't care if the + * channel is already started. The DMA component accesses data here + * that can be modified at interrupt or task levels, so a critical + * section is required. + */ + (void) XDmaChannel_SgStart(&InstancePtr->SendChannel); + } + } + + XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Add a descriptor, with an attached empty buffer, into the receive descriptor +* list. The buffer attached to the descriptor must be word-aligned. This is +* used by the upper layer software during initialization when first setting up +* the receive descriptors, and also during reception of frames to replace +* filled buffers with empty buffers. This function can be called when the +* device is started or stopped. Note that it does start the scatter-gather DMA +* engine. Although this is not necessary during initialization, it is not a +* problem during initialization because the MAC receiver is not yet started. +* +* The buffer attached to the descriptor must be word-aligned on both the front +* end and the back end. +* +* Notification of received frames are done asynchronously through the receive +* callback function. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param BdPtr is a pointer to the buffer descriptor that will be added to the +* descriptor list. +* +* @return +* +* - XST_SUCCESS if a descriptor was successfully returned to the driver +* - XST_NOT_SGDMA if the device is not in scatter-gather DMA mode +* - XST_DMA_SG_LIST_FULL if the receive descriptor list is full +* - XST_DMA_SG_BD_LOCKED if the DMA channel cannot insert the descriptor into +* the list because a locked descriptor exists at the insert point. +* - XST_DMA_SG_NOTHING_TO_COMMIT if even after inserting a descriptor into the +* list, the DMA channel believes there are no new descriptors to commit. +* +* @internal +* +* A status that should never be returned from this function, although +* the code is set up to handle it, is XST_DMA_SG_NO_LIST. Starting the device +* requires a list to be created, and this function requires the device to be +* started. +* +******************************************************************************/ +XStatus +XEmac_SgRecv(XEmac * InstancePtr, XBufDescriptor * BdPtr) +{ + XStatus Result; + u32 BdControl; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(BdPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Be sure the device is configured for scatter-gather DMA + */ + if (!XEmac_mIsSgDma(InstancePtr)) { + return XST_NOT_SGDMA; + } + + /* + * Set some descriptor control word defaults (destination address increment + * and local source address) and the source address (the FIFO). These are + * the same for every receive descriptor. + */ + BdControl = XBufDescriptor_GetControl(BdPtr); + XBufDescriptor_SetControl(BdPtr, BdControl | XEM_DFT_RECV_BD_MASK); + XBufDescriptor_SetSrcAddress(BdPtr, + InstancePtr->BaseAddress + + XEM_PFIFO_RXDATA_OFFSET); + + /* + * Put the descriptor into the channel's descriptor list and commit. + * Although this function is likely called within interrupt context, there + * is the possibility that the upper layer software queues it to a task. + * In this case, a critical section is needed here to protect shared data + * in the DMA component. + */ + XIIF_V123B_GINTR_DISABLE(InstancePtr->BaseAddress); + + Result = XDmaChannel_PutDescriptor(&InstancePtr->RecvChannel, BdPtr); + if (Result != XST_SUCCESS) { + XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); + return Result; + } + + Result = XDmaChannel_CommitPuts(&InstancePtr->RecvChannel); + if (Result != XST_SUCCESS) { + XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); + return Result; + } + + /* + * Start the DMA channel. Ignore the return status since we know the list + * exists and has at least one entry and we don't care if the channel is + * already started. The DMA component accesses data here that can be + * modified at interrupt or task levels, so a critical section is required. + */ + (void) XDmaChannel_SgStart(&InstancePtr->RecvChannel); + + XIIF_V123B_GINTR_ENABLE(InstancePtr->BaseAddress); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* The interrupt handler for the Ethernet driver when configured with scatter- +* gather DMA. +* +* Get the interrupt status from the IpIf to determine the source of the +* interrupt. The source can be: MAC, Recv Packet FIFO, Send Packet FIFO, Recv +* DMA channel, or Send DMA channel. The packet FIFOs only interrupt during +* "deadlock" conditions. +* +* @param InstancePtr is a pointer to the XEmac instance that just interrupted. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XEmac_IntrHandlerDma(void *InstancePtr) +{ + u32 IntrStatus; + XEmac *EmacPtr = (XEmac *) InstancePtr; + + EmacPtr->Stats.TotalIntrs++; + + /* + * Get the interrupt status from the IPIF. There is no clearing of + * interrupts in the IPIF. Interrupts must be cleared at the source. + */ + IntrStatus = XIIF_V123B_READ_DIPR(EmacPtr->BaseAddress); + + /* + * See which type of interrupt is being requested, and service it + */ + if (IntrStatus & XEM_IPIF_RECV_DMA_MASK) { /* Receive DMA interrupt */ + EmacPtr->Stats.RecvInterrupts++; + HandleDmaRecvIntr(EmacPtr); + } + + if (IntrStatus & XEM_IPIF_SEND_DMA_MASK) { /* Send DMA interrupt */ + EmacPtr->Stats.XmitInterrupts++; + HandleDmaSendIntr(EmacPtr); + } + + if (IntrStatus & XEM_IPIF_EMAC_MASK) { /* MAC interrupt */ + EmacPtr->Stats.EmacInterrupts++; + HandleEmacDmaIntr(EmacPtr); + } + + if (IntrStatus & XEM_IPIF_RECV_FIFO_MASK) { /* Receive FIFO interrupt */ + EmacPtr->Stats.RecvInterrupts++; + XEmac_CheckFifoRecvError(EmacPtr); + } + + if (IntrStatus & XEM_IPIF_SEND_FIFO_MASK) { /* Send FIFO interrupt */ + EmacPtr->Stats.XmitInterrupts++; + XEmac_CheckFifoSendError(EmacPtr); + } + + if (IntrStatus & XIIF_V123B_ERROR_MASK) { + /* + * An error occurred internal to the IPIF. This is more of a debug and + * integration issue rather than a production error. Don't do anything + * other than clear it, which provides a spot for software to trap + * on the interrupt and begin debugging. + */ + XIIF_V123B_WRITE_DISR(EmacPtr->BaseAddress, + XIIF_V123B_ERROR_MASK); + } +} + +/*****************************************************************************/ +/** +* +* Set the packet count threshold for this device. The device must be stopped +* before setting the threshold. The packet count threshold is used for interrupt +* coalescing, which reduces the frequency of interrupts from the device to the +* processor. In this case, the scatter-gather DMA engine only interrupts when +* the packet count threshold is reached, instead of interrupting for each packet. +* A packet is a generic term used by the scatter-gather DMA engine, and is +* equivalent to an Ethernet frame in our case. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param Direction indicates the channel, send or receive, from which the +* threshold register is read. +* @param Threshold is the value of the packet threshold count used during +* interrupt coalescing. A value of 0 disables the use of packet threshold +* by the hardware. +* +* @return +* +* - XST_SUCCESS if the threshold was successfully set +* - XST_NOT_SGDMA if the MAC is not configured for scatter-gather DMA +* - XST_DEVICE_IS_STARTED if the device has not been stopped +* - XST_INVALID_PARAM if the Direction parameter is invalid. Turning on +* asserts would also catch this error. +* +* @note +* +* The packet threshold could be set to larger than the number of descriptors +* allocated to the DMA channel. In this case, the wait bound will take over +* and always indicate data arrival. There was a check in this function that +* returned an error if the treshold was larger than the number of descriptors, +* but that was removed because users would then have to set the threshold +* only after they set descriptor space, which is an order dependency that +* caused confustion. +* +******************************************************************************/ +XStatus +XEmac_SetPktThreshold(XEmac * InstancePtr, u32 Direction, u8 Threshold) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(Direction == XEM_SEND || Direction == XEM_RECV); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Be sure device is configured for scatter-gather DMA and has been stopped + */ + if (!XEmac_mIsSgDma(InstancePtr)) { + return XST_NOT_SGDMA; + } + + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + return XST_DEVICE_IS_STARTED; + } + + /* + * Based on the direction, set the packet threshold in the + * corresponding DMA channel component. Default to the receive + * channel threshold register (if an invalid Direction is passed). + */ + switch (Direction) { + case XEM_SEND: + return XDmaChannel_SetPktThreshold(&InstancePtr->SendChannel, + Threshold); + + case XEM_RECV: + return XDmaChannel_SetPktThreshold(&InstancePtr->RecvChannel, + Threshold); + + default: + return XST_INVALID_PARAM; + } +} + +/*****************************************************************************/ +/** +* +* Get the value of the packet count threshold for this driver/device. The packet +* count threshold is used for interrupt coalescing, which reduces the frequency +* of interrupts from the device to the processor. In this case, the +* scatter-gather DMA engine only interrupts when the packet count threshold is +* reached, instead of interrupting for each packet. A packet is a generic term +* used by the scatter-gather DMA engine, and is equivalent to an Ethernet frame +* in our case. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param Direction indicates the channel, send or receive, from which the +* threshold register is read. +* @param ThreshPtr is a pointer to the byte into which the current value of the +* packet threshold register will be copied. An output parameter. A value +* of 0 indicates the use of packet threshold by the hardware is disabled. +* +* @return +* +* - XST_SUCCESS if the packet threshold was retrieved successfully +* - XST_NOT_SGDMA if the MAC is not configured for scatter-gather DMA +* - XST_INVALID_PARAM if the Direction parameter is invalid. Turning on +* asserts would also catch this error. +* +* @note +* +* None. +* +******************************************************************************/ +XStatus +XEmac_GetPktThreshold(XEmac * InstancePtr, u32 Direction, u8 * ThreshPtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(Direction == XEM_SEND || Direction == XEM_RECV); + XASSERT_NONVOID(ThreshPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + if (!XEmac_mIsSgDma(InstancePtr)) { + return XST_NOT_SGDMA; + } + + /* + * Based on the direction, return the packet threshold set in the + * corresponding DMA channel component. Default to the value in + * the receive channel threshold register (if an invalid Direction + * is passed). + */ + switch (Direction) { + case XEM_SEND: + *ThreshPtr = + XDmaChannel_GetPktThreshold(&InstancePtr->SendChannel); + break; + + case XEM_RECV: + *ThreshPtr = + XDmaChannel_GetPktThreshold(&InstancePtr->RecvChannel); + break; + + default: + return XST_INVALID_PARAM; + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Set the packet wait bound timer for this driver/device. The device must be +* stopped before setting the timer value. The packet wait bound is used during +* interrupt coalescing to trigger an interrupt when not enough packets have been +* received to reach the packet count threshold. A packet is a generic term used +* by the scatter-gather DMA engine, and is equivalent to an Ethernet frame in +* our case. The timer is in milliseconds. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param Direction indicates the channel, send or receive, from which the +* threshold register is read. +* @param TimerValue is the value of the packet wait bound used during interrupt +* coalescing. It is in milliseconds in the range 0 - 1023. A value of 0 +* disables the packet wait bound timer. +* +* @return +* +* - XST_SUCCESS if the packet wait bound was set successfully +* - XST_NOT_SGDMA if the MAC is not configured for scatter-gather DMA +* - XST_DEVICE_IS_STARTED if the device has not been stopped +* - XST_INVALID_PARAM if the Direction parameter is invalid. Turning on +* asserts would also catch this error. +* +* @note +* +* None. +* +******************************************************************************/ +XStatus +XEmac_SetPktWaitBound(XEmac * InstancePtr, u32 Direction, u32 TimerValue) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(Direction == XEM_SEND || Direction == XEM_RECV); + XASSERT_NONVOID(TimerValue <= XEM_SGDMA_MAX_WAITBOUND); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Be sure device is configured for scatter-gather DMA and has been stopped + */ + if (!XEmac_mIsSgDma(InstancePtr)) { + return XST_NOT_SGDMA; + } + + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + return XST_DEVICE_IS_STARTED; + } + + /* + * Based on the direction, set the packet wait bound in the + * corresponding DMA channel component. Default to the receive + * channel wait bound register (if an invalid Direction is passed). + */ + switch (Direction) { + case XEM_SEND: + XDmaChannel_SetPktWaitBound(&InstancePtr->SendChannel, + TimerValue); + break; + + case XEM_RECV: + XDmaChannel_SetPktWaitBound(&InstancePtr->RecvChannel, + TimerValue); + break; + + default: + return XST_INVALID_PARAM; + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Get the packet wait bound timer for this driver/device. The packet wait bound +* is used during interrupt coalescing to trigger an interrupt when not enough +* packets have been received to reach the packet count threshold. A packet is a +* generic term used by the scatter-gather DMA engine, and is equivalent to an +* Ethernet frame in our case. The timer is in milliseconds. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param Direction indicates the channel, send or receive, from which the +* threshold register is read. +* @param WaitPtr is a pointer to the byte into which the current value of the +* packet wait bound register will be copied. An output parameter. Units +* are in milliseconds in the range 0 - 1023. A value of 0 indicates the +* packet wait bound timer is disabled. +* +* @return +* +* - XST_SUCCESS if the packet wait bound was retrieved successfully +* - XST_NOT_SGDMA if the MAC is not configured for scatter-gather DMA +* - XST_INVALID_PARAM if the Direction parameter is invalid. Turning on +* asserts would also catch this error. +* +* @note +* +* None. +* +******************************************************************************/ +XStatus +XEmac_GetPktWaitBound(XEmac * InstancePtr, u32 Direction, u32 * WaitPtr) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(Direction == XEM_SEND || Direction == XEM_RECV); + XASSERT_NONVOID(WaitPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + if (!XEmac_mIsSgDma(InstancePtr)) { + return XST_NOT_SGDMA; + } + + /* + * Based on the direction, return the packet wait bound set in the + * corresponding DMA channel component. Default to the value in + * the receive channel wait bound register (if an invalid Direction + * is passed). + */ + switch (Direction) { + case XEM_SEND: + *WaitPtr = + XDmaChannel_GetPktWaitBound(&InstancePtr->SendChannel); + break; + + case XEM_RECV: + *WaitPtr = + XDmaChannel_GetPktWaitBound(&InstancePtr->RecvChannel); + break; + + default: + return XST_INVALID_PARAM; + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Give the driver the memory space to be used for the scatter-gather DMA +* receive descriptor list. This function should only be called once, during +* initialization of the Ethernet driver. The memory space must be big enough +* to hold some number of descriptors, depending on the needs of the system. +* The xemac.h file defines minimum and default numbers of descriptors +* which can be used to allocate this memory space. +* +* The memory space must be word-aligned. An assert will occur if asserts are +* turned on and the memory is not word-aligned. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param MemoryPtr is a pointer to the word-aligned memory. +* @param ByteCount is the length, in bytes, of the memory space. +* +* @return +* +* - XST_SUCCESS if the space was initialized successfully +* - XST_NOT_SGDMA if the MAC is not configured for scatter-gather DMA +* - XST_DMA_SG_LIST_EXISTS if this list space has already been created +* +* @note +* +* If the device is configured for scatter-gather DMA, this function must be +* called AFTER the XEmac_Initialize() function because the DMA channel +* components must be initialized before the memory space is set. +* +******************************************************************************/ +XStatus +XEmac_SetSgRecvSpace(XEmac * InstancePtr, u32 * MemoryPtr, u32 ByteCount) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(MemoryPtr != NULL); + XASSERT_NONVOID(ByteCount != 0); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + if (!XEmac_mIsSgDma(InstancePtr)) { + return XST_NOT_SGDMA; + } + + return XDmaChannel_CreateSgList(&InstancePtr->RecvChannel, MemoryPtr, + ByteCount); +} + +/*****************************************************************************/ +/** +* +* Give the driver the memory space to be used for the scatter-gather DMA +* transmit descriptor list. This function should only be called once, during +* initialization of the Ethernet driver. The memory space must be big enough +* to hold some number of descriptors, depending on the needs of the system. +* The xemac.h file defines minimum and default numbers of descriptors +* which can be used to allocate this memory space. +* +* The memory space must be word-aligned. An assert will occur if asserts are +* turned on and the memory is not word-aligned. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param MemoryPtr is a pointer to the word-aligned memory. +* @param ByteCount is the length, in bytes, of the memory space. +* +* @return +* +* - XST_SUCCESS if the space was initialized successfully +* - XST_NOT_SGDMA if the MAC is not configured for scatter-gather DMA +* - XST_DMA_SG_LIST_EXISTS if this list space has already been created +* +* @note +* +* If the device is configured for scatter-gather DMA, this function must be +* called AFTER the XEmac_Initialize() function because the DMA channel +* components must be initialized before the memory space is set. +* +******************************************************************************/ +XStatus +XEmac_SetSgSendSpace(XEmac * InstancePtr, u32 * MemoryPtr, u32 ByteCount) +{ + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(MemoryPtr != NULL); + XASSERT_NONVOID(ByteCount != 0); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + if (!XEmac_mIsSgDma(InstancePtr)) { + return XST_NOT_SGDMA; + } + + return XDmaChannel_CreateSgList(&InstancePtr->SendChannel, MemoryPtr, + ByteCount); +} + +/*****************************************************************************/ +/** +* +* Set the callback function for handling received frames in scatter-gather DMA +* mode. The upper layer software should call this function during +* initialization. The callback is called once per frame received. The head of +* a descriptor list is passed in along with the number of descriptors in the +* list. Before leaving the callback, the upper layer software should attach a +* new buffer to each descriptor in the list. +* +* The callback is invoked by the driver within interrupt context, so it needs +* to do its job quickly. Sending the received frame up the protocol stack +* should be done at task-level. If there are other potentially slow operations +* within the callback, these too should be done at task-level. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param CallBackRef is a reference pointer to be passed back to the adapter in +* the callback. This helps the adapter correlate the callback to a +* particular driver. +* @param FuncPtr is the pointer to the callback function. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XEmac_SetSgRecvHandler(XEmac * InstancePtr, void *CallBackRef, + XEmac_SgHandler FuncPtr) +{ + /* + * Asserted IsDmaSg here instead of run-time check because there is really + * no ill-effects of setting these when not configured for scatter-gather. + */ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(FuncPtr != NULL); + XASSERT_VOID(XEmac_mIsSgDma(InstancePtr)); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + InstancePtr->SgRecvHandler = FuncPtr; + InstancePtr->SgRecvRef = CallBackRef; +} + +/*****************************************************************************/ +/** +* +* Set the callback function for handling confirmation of transmitted frames in +* scatter-gather DMA mode. The upper layer software should call this function +* during initialization. The callback is called once per frame sent. The head +* of a descriptor list is passed in along with the number of descriptors in +* the list. The callback is responsible for freeing buffers attached to these +* descriptors. +* +* The callback is invoked by the driver within interrupt context, so it needs +* to do its job quickly. If there are potentially slow operations within the +* callback, these should be done at task-level. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param CallBackRef is a reference pointer to be passed back to the adapter in +* the callback. This helps the adapter correlate the callback to a +* particular driver. +* @param FuncPtr is the pointer to the callback function. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XEmac_SetSgSendHandler(XEmac * InstancePtr, void *CallBackRef, + XEmac_SgHandler FuncPtr) +{ + /* + * Asserted IsDmaSg here instead of run-time check because there is really + * no ill-effects of setting these when not configured for scatter-gather. + */ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(FuncPtr != NULL); + XASSERT_VOID(XEmac_mIsSgDma(InstancePtr)); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + InstancePtr->SgSendHandler = FuncPtr; + InstancePtr->SgSendRef = CallBackRef; +} + +/*****************************************************************************/ +/* +* +* Handle an interrupt from the DMA receive channel. DMA interrupts are: +* +* - DMA error. DMA encountered a bus error or timeout. This is a fatal error +* that requires reset of the channel. The driver calls the error handler +* of the upper layer software with an error code indicating the device should +* be reset. +* - Packet count threshold reached. For scatter-gather operations, indicates +* the threshold for the number of packets not serviced by software has been +* reached. The driver behaves as follows: +* - Get the value of the packet counter, which tells us how many packets +* are ready to be serviced +* - For each packet +* - For each descriptor, remove it from the scatter-gather list +* - Check for the last descriptor in the frame, and if set +* - Bump frame statistics +* - Call the scatter-gather receive callback function +* - Decrement the packet counter by one +* Note that there are no receive errors reported in the status word of +* the buffer descriptor. If receive errors occur, the MAC drops the +* packet, and we only find out about the errors through various error +* count registers. +* - Packet wait bound reached. For scatter-gather, indicates the time to wait +* for the next packet has expired. The driver follows the same logic as when +* the packet count threshold interrupt is received. +* - Scatter-gather end acknowledge. Hardware has reached the end of the +* descriptor list. The driver follows the same logic as when the packet count +* threshold interrupt is received. In addition, the driver restarts the DMA +* scatter-gather channel in case there are newly inserted descriptors. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* Although the function returns void, there are asynchronous errors that can +* be generated (by calling the ErrorHandler) from this function. These are: +* - XST_DMA_SG_LIST_EMPTY indicates we tried to get a buffer descriptor from the +* DMA channel, but there was not one ready for software. +* - XST_DMA_ERROR indicates a DMA bus error or timeout occurred. This is a fatal +* error that requires reset. +* +* @note +* +* None. +* +******************************************************************************/ +static void +HandleDmaRecvIntr(XEmac * InstancePtr) +{ + u32 IntrStatus; + + /* + * Read the interrupt status + */ + IntrStatus = XDmaChannel_GetIntrStatus(&InstancePtr->RecvChannel); + + /* + * For packet threshold or wait bound interrupts, process desciptors. Also + * process descriptors on a SG end acknowledgement, which means the end of + * the descriptor list has been reached by the hardware. For receive, this + * is potentially trouble since it means the descriptor list is full, + * unless software can process enough packets quickly enough so the + * hardware has room to put new packets. + */ + if (IntrStatus & (XDC_IXR_PKT_THRESHOLD_MASK | + XDC_IXR_PKT_WAIT_BOUND_MASK | XDC_IXR_SG_END_MASK)) { + XStatus Result = XST_SUCCESS; + u32 NumFrames; + u32 NumProcessed; + u32 NumBuffers; + u32 NumBytes; + u32 IsLast; + XBufDescriptor *FirstBdPtr; + XBufDescriptor *BdPtr; + + /* + * Get the number of unserviced packets + */ + NumFrames = XDmaChannel_GetPktCount(&InstancePtr->RecvChannel); + + for (NumProcessed = 0; NumProcessed < NumFrames; NumProcessed++) { + IsLast = FALSE; + FirstBdPtr = NULL; + NumBuffers = 0; + NumBytes = 0; + + /* + * For each packet, get the descriptor from the list. On the + * last one in the frame, make the callback to the upper layer. + */ + while (!IsLast) { + Result = + XDmaChannel_GetDescriptor(&InstancePtr-> + RecvChannel, + &BdPtr); + if (Result != XST_SUCCESS) { + /* + * An error getting a buffer descriptor from the list. + * This should not happen, but if it does, report it to + * the error callback and break out of the loops to service + * other interrupts. + */ + InstancePtr->ErrorHandler(InstancePtr-> + ErrorRef, + Result); + break; + } + + /* + * Keep a pointer to the first descriptor in the list, as it + * will be passed to the upper layers in a bit. By the fact + * that we received this packet means no errors occurred, so + * no need to check the device status word for errors. + */ + if (FirstBdPtr == NULL) { + FirstBdPtr = BdPtr; + } + + NumBytes += XBufDescriptor_GetLength(BdPtr); + + /* + * Check to see if this is the last descriptor in the frame, + * and if so, set the IsLast flag to get out of the loop. + */ + if (XBufDescriptor_IsLastStatus(BdPtr)) { + IsLast = TRUE; + } + + /* + * Bump the number of buffers in this packet + */ + NumBuffers++; + + } /* end while loop */ + + /* + * Check for error that occurred inside the while loop, and break + * out of the for loop if there was one so other interrupts can + * be serviced. + */ + if (Result != XST_SUCCESS) { + break; + } + + InstancePtr->Stats.RecvFrames++; + InstancePtr->Stats.RecvBytes += NumBytes; + + /* + * Make the callback to the upper layers, passing it the first + * descriptor in the packet and the number of descriptors in the + * packet. + */ + InstancePtr->SgRecvHandler(InstancePtr->SgRecvRef, + FirstBdPtr, NumBuffers); + + /* + * Decrement the packet count register to reflect the fact we + * just processed a packet + */ + XDmaChannel_DecrementPktCount(&InstancePtr-> + RecvChannel); + + } /* end for loop */ + + /* + * If the interrupt was an end-ack, check the descriptor list again to + * see if it is empty. If not, go ahead and restart the scatter-gather + * channel. This is to fix a possible race condition where, on receive, + * the driver attempted to start a scatter-gather channel that was + * already started, which resulted in no action from the XDmaChannel + * component. But, just after the XDmaChannel component saw that the + * hardware was already started, the hardware stopped because it + * reached the end of the list. In that case, this interrupt is + * generated and we can restart the hardware here. + */ + if (IntrStatus & XDC_IXR_SG_END_MASK) { + /* + * Ignore the return status since we know the list exists and we + * don't care if the list is empty or the channel is already started. + */ + (void) XDmaChannel_SgStart(&InstancePtr->RecvChannel); + } + } + + /* + * All interrupts are handled (except the error below) so acknowledge + * (clear) the interrupts by writing the value read above back to the status + * register. The packet count interrupt must be acknowledged after the + * decrement, otherwise it will come right back. We clear the interrupts + * before we handle the error interrupt because the ErrorHandler should + * result in a reset, which clears the interrupt status register. So we + * don't want to toggle the interrupt back on by writing the interrupt + * status register with an old value after a reset. + */ + XDmaChannel_SetIntrStatus(&InstancePtr->RecvChannel, IntrStatus); + + /* + * Check for DMA errors and call the error callback function if an error + * occurred (DMA bus or timeout error), which should result in a reset of + * the device by the upper layer software. + */ + if (IntrStatus & XDC_IXR_DMA_ERROR_MASK) { + InstancePtr->Stats.DmaErrors++; + InstancePtr->ErrorHandler(InstancePtr->ErrorRef, XST_DMA_ERROR); + } +} + +/*****************************************************************************/ +/* +* +* Handle an interrupt from the DMA send channel. DMA interrupts are: +* +* - DMA error. DMA encountered a bus error or timeout. This is a fatal error +* that requires reset of the channel. The driver calls the error handler +* of the upper layer software with an error code indicating the device should +* be reset. +* - Packet count threshold reached. For scatter-gather operations, indicates +* the threshold for the number of packets not serviced by software has been +* reached. The driver behaves as follows: +* - Get the value of the packet counter, which tells us how many packets +* are ready to be serviced +* - For each packet +* - For each descriptor, remove it from the scatter-gather list +* - Check for the last descriptor in the frame, and if set +* - Bump frame statistics +* - Call the scatter-gather receive callback function +* - Decrement the packet counter by one +* Note that there are no receive errors reported in the status word of +* the buffer descriptor. If receive errors occur, the MAC drops the +* packet, and we only find out about the errors through various error +* count registers. +* - Packet wait bound reached. For scatter-gather, indicates the time to wait +* for the next packet has expired. The driver follows the same logic as when +* the packet count threshold interrupt is received. +* - Scatter-gather end acknowledge. Hardware has reached the end of the +* descriptor list. The driver follows the same logic as when the packet count +* threshold interrupt is received. In addition, the driver restarts the DMA +* scatter-gather channel in case there are newly inserted descriptors. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* Although the function returns void, there are asynchronous errors +* that can be generated from this function. These are: +* - XST_DMA_SG_LIST_EMPTY indicates we tried to get a buffer descriptor from +* the DMA channel, but there was not one ready for software. +* - XST_DMA_ERROR indicates a DMA bus error or timeout occurred. This is a +* fatal error that requires reset. +* +* @note +* +* None. +* +******************************************************************************/ +static void +HandleDmaSendIntr(XEmac * InstancePtr) +{ + u32 IntrStatus; + + /* + * Read the interrupt status + */ + IntrStatus = XDmaChannel_GetIntrStatus(&InstancePtr->SendChannel); + + /* + * For packet threshold or wait bound interrupt, process descriptors. Also + * process descriptors on a SG end acknowledgement, which means the end of + * the descriptor list has been reached by the hardware. For transmit, + * this is a normal condition during times of light traffic. In fact, the + * wait bound interrupt may be masked for transmit since the end-ack would + * always occur before the wait bound expires. + */ + if (IntrStatus & (XDC_IXR_PKT_THRESHOLD_MASK | + XDC_IXR_PKT_WAIT_BOUND_MASK | XDC_IXR_SG_END_MASK)) { + XStatus Result = XST_SUCCESS; + u32 NumFrames; + u32 NumProcessed; + u32 NumBuffers; + u32 NumBytes; + u32 IsLast; + XBufDescriptor *FirstBdPtr; + XBufDescriptor *BdPtr; + + /* + * Get the number of unserviced packets + */ + NumFrames = XDmaChannel_GetPktCount(&InstancePtr->SendChannel); + + for (NumProcessed = 0; NumProcessed < NumFrames; NumProcessed++) { + IsLast = FALSE; + FirstBdPtr = NULL; + NumBuffers = 0; + NumBytes = 0; + + /* + * For each frame, traverse the descriptor list and look for + * errors. On the last one in the frame, make the callback. + */ + while (!IsLast) { + Result = + XDmaChannel_GetDescriptor(&InstancePtr-> + SendChannel, + &BdPtr); + if (Result != XST_SUCCESS) { + /* + * An error getting a buffer descriptor from the list. + * This should not happen, but if it does, report it to + * the error callback and break out of the loops to service + * other interrupts + */ + InstancePtr->ErrorHandler(InstancePtr-> + ErrorRef, + Result); + break; + } + + /* + * Keep a pointer to the first descriptor in the list and + * check the device status for errors. The device status is + * only available in the first descriptor of a packet. + */ + if (FirstBdPtr == NULL) { + u32 XmitStatus; + + FirstBdPtr = BdPtr; + + XmitStatus = + XBufDescriptor_GetDeviceStatus + (BdPtr); + if (XmitStatus & + XEM_TSR_EXCESS_DEFERRAL_MASK) { + InstancePtr->Stats. + XmitExcessDeferral++; + } + + if (XmitStatus & + XEM_TSR_LATE_COLLISION_MASK) { + InstancePtr->Stats. + XmitLateCollisionErrors++; + } + } + + NumBytes += XBufDescriptor_GetLength(BdPtr); + + /* + * Check to see if this is the last descriptor in the frame, + * and if so, set the IsLast flag to get out of the loop. The + * transmit channel must check the last bit in the control + * word, not the status word (the DMA engine does not update + * the last bit in the status word for the transmit direction). + */ + if (XBufDescriptor_IsLastControl(BdPtr)) { + IsLast = TRUE; + } + + /* + * Bump the number of buffers in this packet + */ + NumBuffers++; + + } /* end while loop */ + + /* + * Check for error that occurred inside the while loop, and break + * out of the for loop if there was one so other interrupts can + * be serviced. + */ + if (Result != XST_SUCCESS) { + break; + } + + InstancePtr->Stats.XmitFrames++; + InstancePtr->Stats.XmitBytes += NumBytes; + + /* + * Make the callback to the upper layers, passing it the first + * descriptor in the packet and the number of descriptors in the + * packet. + */ + InstancePtr->SgSendHandler(InstancePtr->SgSendRef, + FirstBdPtr, NumBuffers); + + /* + * Decrement the packet count register to reflect the fact we + * just processed a packet + */ + XDmaChannel_DecrementPktCount(&InstancePtr-> + SendChannel); + + } /* end for loop */ + + /* + * If the interrupt was an end-ack, check the descriptor list again to + * see if it is empty. If not, go ahead and restart the scatter-gather + * channel. This is to fix a possible race condition where, on transmit, + * the driver attempted to start a scatter-gather channel that was + * already started, which resulted in no action from the XDmaChannel + * component. But, just after the XDmaChannel component saw that the + * hardware was already started, the hardware stopped because it + * reached the end of the list. In that case, this interrupt is + * generated and we can restart the hardware here. + */ + if (IntrStatus & XDC_IXR_SG_END_MASK) { + /* + * Ignore the return status since we know the list exists and we + * don't care if the list is empty or the channel is already started. + */ + (void) XDmaChannel_SgStart(&InstancePtr->SendChannel); + } + } + + /* + * All interrupts are handled (except the error below) so acknowledge + * (clear) the interrupts by writing the value read above back to the status + * register. The packet count interrupt must be acknowledged after the + * decrement, otherwise it will come right back. We clear the interrupts + * before we handle the error interrupt because the ErrorHandler should + * result in a reset, which clears the interrupt status register. So we + * don't want to toggle the interrupt back on by writing the interrupt + * status register with an old value after a reset. + */ + XDmaChannel_SetIntrStatus(&InstancePtr->SendChannel, IntrStatus); + + /* + * Check for DMA errors and call the error callback function if an error + * occurred (DMA bus or timeout error), which should result in a reset of + * the device by the upper layer software. + */ + if (IntrStatus & XDC_IXR_DMA_ERROR_MASK) { + InstancePtr->Stats.DmaErrors++; + InstancePtr->ErrorHandler(InstancePtr->ErrorRef, XST_DMA_ERROR); + } +} + +/*****************************************************************************/ +/* +* +* Handle an interrupt from the Ethernet MAC when configured with scatter-gather +* DMA. The only interrupts handled in this case are errors. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +HandleEmacDmaIntr(XEmac * InstancePtr) +{ + u32 IntrStatus; + + /* + * When configured with DMA, the EMAC generates interrupts only when errors + * occur. We clear the interrupts immediately so that any latched status + * interrupt bits will reflect the true status of the device, and so any + * pulsed interrupts (non-status) generated during the Isr will not be lost. + */ + IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress); + XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress, IntrStatus); + + /* + * Check the MAC for errors + */ + XEmac_CheckEmacError(InstancePtr, IntrStatus); +} diff -urN -X bkexcl linux-2.4.22/drivers/net/xilinx_enet/xemac_intr_fifo.c linuxppc-2.4/drivers/net/xilinx_enet/xemac_intr_fifo.c --- linux-2.4.22/drivers/net/xilinx_enet/xemac_intr_fifo.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/net/xilinx_enet/xemac_intr_fifo.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,681 @@ +/* $Id: xemac_intr_fifo.c,v 1.3 2003/04/01 21:06:43 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xemac_intr_fifo.c +* +* Contains functions related to interrupt mode using direct FIFO I/O or simple +* DMA. The driver uses simple DMA if the device is configured with DMA, +* otherwise it uses direct FIFO access. +* +* The interrupt handler, XEmac_IntrHandlerFifo(), must be connected by the user +* to the interrupt controller. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rpm  07/31/01 First release
+* 1.00b rpm  02/20/02 Repartitioned files and functions
+* 1.00c rpm  12/05/02 New version includes support for simple DMA
+* 1.00c rpm  04/01/03 Added check in FifoSend for room in the data FIFO
+*                     before starting a simple DMA transfer.
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xemac_i.h" +#include "xio.h" +#include "xipif_v1_23_b.h" /* Uses v1.23b of the IPIF */ + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Variable Definitions *****************************/ + +/************************** Function Prototypes ******************************/ + +static void HandleEmacFifoIntr(XEmac * InstancePtr); + +/*****************************************************************************/ +/** +* +* Send an Ethernet frame using direct FIFO I/O or simple DMA with interrupts. +* The caller provides a contiguous-memory buffer and its length. The buffer +* must be word-aligned. The callback function set by using SetFifoSendHandler +* is invoked when the transmission is complete. +* +* It is assumed that the upper layer software supplies a correctly formatted +* Ethernet frame, including the destination and source addresses, the +* type/length field, and the data field. +* +* If the device is configured with DMA, simple DMA will be used to transfer +* the buffer from memory to the Emac. This means that this buffer should not +* be cached. See the comment section "Simple DMA" in xemac.h for more +* information. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param BufPtr is a pointer to a word-aligned buffer containing the Ethernet +* frame to be sent. +* @param ByteCount is the size of the Ethernet frame. +* +* @return +* +* - XST_SUCCESS if the frame was successfully sent. An interrupt is generated +* when the EMAC transmits the frame and the driver calls the callback set +* with XEmac_SetFifoSendHandler() +* - XST_DEVICE_IS_STOPPED if the device has not yet been started +* - XST_NOT_INTERRUPT if the device is not in interrupt mode +* - XST_FIFO_NO_ROOM if there is no room in the FIFO for this frame +* - XST_DEVICE_BUSY if configured for simple DMA and the DMA engine is busy +* - XST_DMA_ERROR if an error occurred during the DMA transfer (simple DMA). +* The user should treat this as a fatal error that requires a reset of the +* EMAC device. +* +* @note +* +* This function is not thread-safe. The user must provide mutually exclusive +* access to this function if there are to be multiple threads that can call it. +* +* @internal +* +* The Ethernet MAC uses FIFOs behind its length and status registers. For this +* reason, it is important to keep the length, status, and data FIFOs in sync +* when reading or writing to them. +* +******************************************************************************/ +XStatus +XEmac_FifoSend(XEmac * InstancePtr, u8 * BufPtr, u32 ByteCount) +{ + XStatus Result; + volatile u32 StatusReg; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(BufPtr != NULL); + XASSERT_NONVOID(ByteCount > XEM_HDR_SIZE); /* send at least 1 byte */ + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Be sure the device is configured for interrupt mode and it is started + */ + if (InstancePtr->IsPolled) { + return XST_NOT_INTERRUPT; + } + + if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED) { + return XST_DEVICE_IS_STOPPED; + } + + /* + * Before writing to the data FIFO, make sure the length FIFO is not + * full. The data FIFO might not be full yet even though the length FIFO + * is. This avoids an overrun condition on the length FIFO and keeps the + * FIFOs in sync. + */ + StatusReg = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress); + if (StatusReg & XEM_EIR_XMIT_LFIFO_FULL_MASK) { + return XST_FIFO_NO_ROOM; + } + + /* + * Send either by directly writing to the FIFOs or using the DMA engine + */ + if (!XEmac_mIsDma(InstancePtr)) { + /* + * This is a non-blocking write. The packet FIFO returns an error if there + * is not enough room in the FIFO for this frame. + */ + Result = + XPacketFifoV100b_Write(&InstancePtr->SendFifo, BufPtr, + ByteCount); + if (Result != XST_SUCCESS) { + return Result; + } + } else { + u32 Vacancy; + + /* + * Need to make sure there is room in the data FIFO for the packet + * before trying to DMA into it. Get the vacancy count (in words) + * and make sure the packet will fit. + */ + Vacancy = XPF_V100B_GET_COUNT(&InstancePtr->SendFifo); + if ((Vacancy * sizeof (u32)) < ByteCount) { + return XST_FIFO_NO_ROOM; + } + + /* + * Check the DMA engine to make sure it is not already busy + */ + if (XDmaChannel_GetStatus(&InstancePtr->SendChannel) & + XDC_DMASR_BUSY_MASK) { + return XST_DEVICE_BUSY; + } + + /* + * Set the DMA control register up properly + */ + XDmaChannel_SetControl(&InstancePtr->SendChannel, + XDC_DMACR_SOURCE_INCR_MASK | + XDC_DMACR_DEST_LOCAL_MASK | + XDC_DMACR_SG_DISABLE_MASK); + + /* + * Now transfer the data from the buffer to the FIFO + */ + XDmaChannel_Transfer(&InstancePtr->SendChannel, (u32 *) BufPtr, + (u32 *) (InstancePtr->BaseAddress + + XEM_PFIFO_TXDATA_OFFSET), + ByteCount); + + /* + * Poll here waiting for DMA to be not busy. We think this will + * typically be a single read since DMA should be ahead of the SW. + */ + do { + StatusReg = + XDmaChannel_GetStatus(&InstancePtr->SendChannel); + } + while (StatusReg & XDC_DMASR_BUSY_MASK); + + /* Return an error if there was a problem with DMA */ + if ((StatusReg & XDC_DMASR_BUS_ERROR_MASK) || + (StatusReg & XDC_DMASR_BUS_TIMEOUT_MASK)) { + InstancePtr->Stats.DmaErrors++; + return XST_DMA_ERROR; + } + } + + /* + * Set the MAC's transmit packet length register to tell it to transmit + */ + XIo_Out32(InstancePtr->BaseAddress + XEM_TPLR_OFFSET, ByteCount); + + /* + * Bump stats here instead of the Isr since we know the byte count + * here but would have to save it in the instance in order to know the + * byte count at interrupt time. + */ + InstancePtr->Stats.XmitFrames++; + InstancePtr->Stats.XmitBytes += ByteCount; + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Receive an Ethernet frame into the buffer passed as an argument. This +* function is called in response to the callback function for received frames +* being called by the driver. The callback function is set up using +* SetFifoRecvHandler, and is invoked when the driver receives an interrupt +* indicating a received frame. The driver expects the upper layer software to +* call this function, FifoRecv, to receive the frame. The buffer supplied +* should be large enough to hold a maximum-size Ethernet frame. +* +* The buffer into which the frame will be received must be word-aligned. +* +* If the device is configured with DMA, simple DMA will be used to transfer +* the buffer from the Emac to memory. This means that this buffer should not +* be cached. See the comment section "Simple DMA" in xemac.h for more +* information. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param BufPtr is a pointer to a word-aligned buffer into which the received +* Ethernet frame will be copied. +* @param ByteCountPtr is both an input and an output parameter. It is a pointer +* to a 32-bit word that contains the size of the buffer on entry into +* the function and the size the received frame on return from the +* function. +* +* @return +* +* - XST_SUCCESS if the frame was sent successfully +* - XST_DEVICE_IS_STOPPED if the device has not yet been started +* - XST_NOT_INTERRUPT if the device is not in interrupt mode +* - XST_NO_DATA if there is no frame to be received from the FIFO +* - XST_BUFFER_TOO_SMALL if the buffer to receive the frame is too small for +* the frame waiting in the FIFO. +* - XST_DEVICE_BUSY if configured for simple DMA and the DMA engine is busy +* - XST_DMA_ERROR if an error occurred during the DMA transfer (simple DMA). +* The user should treat this as a fatal error that requires a reset of the +* EMAC device. +* +* @note +* +* The input buffer must be big enough to hold the largest Ethernet frame. +* +* @internal +* +* The Ethernet MAC uses FIFOs behind its length and status registers. For this +* reason, it is important to keep the length, status, and data FIFOs in sync +* when reading or writing to them. +* +******************************************************************************/ +XStatus +XEmac_FifoRecv(XEmac * InstancePtr, u8 * BufPtr, u32 * ByteCountPtr) +{ + XStatus Result; + u32 PktLength; + u32 StatusReg; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(BufPtr != NULL); + XASSERT_NONVOID(ByteCountPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Be sure the device is not configured for polled mode and it is started + */ + if (InstancePtr->IsPolled) { + return XST_NOT_INTERRUPT; + } + + if (InstancePtr->IsStarted != XCOMPONENT_IS_STARTED) { + return XST_DEVICE_IS_STOPPED; + } + + /* + * Make sure the buffer is big enough to hold the maximum frame size. + * We need to do this because as soon as we read the MAC's packet length + * register, which is actually a FIFO, we remove that length from the + * FIFO. We do not want to read the length FIFO without also reading the + * data FIFO since this would get the FIFOs out of sync. So we have to + * make this restriction. + */ + if (*ByteCountPtr < XEM_MAX_FRAME_SIZE) { + return XST_BUFFER_TOO_SMALL; + } + + /* + * Before reading from the length FIFO, make sure the length FIFO is not + * empty. We could cause an underrun error if we try to read from an + * empty FIFO. + */ + StatusReg = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress); + if (StatusReg & XEM_EIR_RECV_LFIFO_EMPTY_MASK) { + /* + * Clear the empty status so the next time through the current status + * of the hardware is reflected (we have to do this because the status + * is level in the device but latched in the interrupt status register). + */ + XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress, + XEM_EIR_RECV_LFIFO_EMPTY_MASK); + return XST_NO_DATA; + } + + /* + * If configured with DMA, make sure the DMA engine is not busy + */ + if (XEmac_mIsDma(InstancePtr)) { + if (XDmaChannel_GetStatus(&InstancePtr->RecvChannel) & + XDC_DMASR_BUSY_MASK) { + return XST_DEVICE_BUSY; + } + } + + /* + * Determine, from the MAC, the length of the next packet available + * in the data FIFO (there should be a non-zero length here) + */ + PktLength = XIo_In32(InstancePtr->BaseAddress + XEM_RPLR_OFFSET); + if (PktLength == 0) { + return XST_NO_DATA; + } + + /* + * We assume that the MAC never has a length bigger than the largest + * Ethernet frame, so no need to make another check here. + * + * Receive either by directly reading the FIFO or using the DMA engine + */ + if (!XEmac_mIsDma(InstancePtr)) { + /* + * This is a non-blocking read. The FIFO returns an error if there is + * not at least the requested amount of data in the FIFO. + */ + Result = + XPacketFifoV100b_Read(&InstancePtr->RecvFifo, BufPtr, + PktLength); + if (Result != XST_SUCCESS) { + return Result; + } + } else { + /* + * Call on DMA to transfer from the FIFO to the buffer. First set up + * the DMA control register. + */ + XDmaChannel_SetControl(&InstancePtr->RecvChannel, + XDC_DMACR_DEST_INCR_MASK | + XDC_DMACR_SOURCE_LOCAL_MASK | + XDC_DMACR_SG_DISABLE_MASK); + + /* + * Now transfer the data + */ + XDmaChannel_Transfer(&InstancePtr->RecvChannel, + (u32 *) (InstancePtr->BaseAddress + + XEM_PFIFO_RXDATA_OFFSET), + (u32 *) BufPtr, PktLength); + + /* + * Poll here waiting for DMA to be not busy. We think this will + * typically be a single read since DMA should be ahead of the SW. + */ + do { + StatusReg = + XDmaChannel_GetStatus(&InstancePtr->RecvChannel); + } + while (StatusReg & XDC_DMASR_BUSY_MASK); + + /* Return an error if there was a problem with DMA */ + if ((StatusReg & XDC_DMASR_BUS_ERROR_MASK) || + (StatusReg & XDC_DMASR_BUS_TIMEOUT_MASK)) { + InstancePtr->Stats.DmaErrors++; + return XST_DMA_ERROR; + } + } + + *ByteCountPtr = PktLength; + + InstancePtr->Stats.RecvFrames++; + InstancePtr->Stats.RecvBytes += PktLength; + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* The interrupt handler for the Ethernet driver when configured for direct FIFO +* communication or simple DMA. +* +* Get the interrupt status from the IpIf to determine the source of the +* interrupt. The source can be: MAC, Recv Packet FIFO, or Send Packet FIFO. +* The packet FIFOs only interrupt during "deadlock" conditions. All other +* FIFO-related interrupts are generated by the MAC. +* +* @param InstancePtr is a pointer to the XEmac instance that just interrupted. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XEmac_IntrHandlerFifo(void *InstancePtr) +{ + u32 IntrStatus; + XEmac *EmacPtr = (XEmac *) InstancePtr; + + EmacPtr->Stats.TotalIntrs++; + + /* + * Get the interrupt status from the IPIF. There is no clearing of + * interrupts in the IPIF. Interrupts must be cleared at the source. + */ + IntrStatus = XIIF_V123B_READ_DIPR(EmacPtr->BaseAddress); + + if (IntrStatus & XEM_IPIF_EMAC_MASK) { /* MAC interrupt */ + EmacPtr->Stats.EmacInterrupts++; + HandleEmacFifoIntr(EmacPtr); + } + + if (IntrStatus & XEM_IPIF_RECV_FIFO_MASK) { /* Receive FIFO interrupt */ + EmacPtr->Stats.RecvInterrupts++; + XEmac_CheckFifoRecvError(EmacPtr); + } + + if (IntrStatus & XEM_IPIF_SEND_FIFO_MASK) { /* Send FIFO interrupt */ + EmacPtr->Stats.XmitInterrupts++; + XEmac_CheckFifoSendError(EmacPtr); + } + + if (IntrStatus & XIIF_V123B_ERROR_MASK) { + /* + * An error occurred internal to the IPIF. This is more of a debug and + * integration issue rather than a production error. Don't do anything + * other than clear it, which provides a spot for software to trap + * on the interrupt and begin debugging. + */ + XIIF_V123B_WRITE_DISR(EmacPtr->BaseAddress, + XIIF_V123B_ERROR_MASK); + } +} + +/*****************************************************************************/ +/** +* +* Set the callback function for handling confirmation of transmitted frames when +* configured for direct memory-mapped I/O using FIFOs. The upper layer software +* should call this function during initialization. The callback is called by the +* driver once per frame sent. The callback is responsible for freeing the +* transmitted buffer if necessary. +* +* The callback is invoked by the driver within interrupt context, so it needs +* to do its job quickly. If there are potentially slow operations within the +* callback, these should be done at task-level. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param CallBackRef is a reference pointer to be passed back to the adapter in +* the callback. This helps the adapter correlate the callback to a +* particular driver. +* @param FuncPtr is the pointer to the callback function. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XEmac_SetFifoRecvHandler(XEmac * InstancePtr, void *CallBackRef, + XEmac_FifoHandler FuncPtr) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(FuncPtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + InstancePtr->FifoRecvHandler = FuncPtr; + InstancePtr->FifoRecvRef = CallBackRef; +} + +/*****************************************************************************/ +/** +* +* Set the callback function for handling received frames when configured for +* direct memory-mapped I/O using FIFOs. The upper layer software should call +* this function during initialization. The callback is called once per frame +* received. During the callback, the upper layer software should call FifoRecv +* to retrieve the received frame. +* +* The callback is invoked by the driver within interrupt context, so it needs +* to do its job quickly. Sending the received frame up the protocol stack +* should be done at task-level. If there are other potentially slow operations +* within the callback, these too should be done at task-level. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param CallBackRef is a reference pointer to be passed back to the adapter in +* the callback. This helps the adapter correlate the callback to a +* particular driver. +* @param FuncPtr is the pointer to the callback function. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +void +XEmac_SetFifoSendHandler(XEmac * InstancePtr, void *CallBackRef, + XEmac_FifoHandler FuncPtr) +{ + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(FuncPtr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + InstancePtr->FifoSendHandler = FuncPtr; + InstancePtr->FifoSendRef = CallBackRef; +} + +/****************************************************************************** +* +* Handle an interrupt from the Ethernet MAC when configured for direct FIFO +* communication. The interrupts handled are: +* - Transmit done (transmit status FIFO is non-empty). Used to determine when +* a transmission has been completed. +* - Receive done (receive length FIFO is non-empty). Used to determine when a +* valid frame has been received. +* +* In addition, the interrupt status is checked for errors. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* None. +* +* @note +* +* None. +* +******************************************************************************/ +static void +HandleEmacFifoIntr(XEmac * InstancePtr) +{ + u32 IntrStatus; + + /* + * The EMAC generates interrupts for errors and generates the transmit + * and receive done interrupts for data. We clear the interrupts + * immediately so that any latched status interrupt bits will reflect the + * true status of the device, and so any pulsed interrupts (non-status) + * generated during the Isr will not be lost. + */ + IntrStatus = XIIF_V123B_READ_IISR(InstancePtr->BaseAddress); + XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress, IntrStatus); + + if (IntrStatus & XEM_EIR_RECV_DONE_MASK) { + /* + * Configured for direct memory-mapped I/O using FIFO with interrupts. + * This interrupt means the RPLR is non-empty, indicating a frame has + * arrived. + */ + InstancePtr->Stats.RecvInterrupts++; + + InstancePtr->FifoRecvHandler(InstancePtr->FifoRecvRef); + + /* + * The upper layer has removed as many frames as it wants to, so we + * need to clear the RECV_DONE bit before leaving the ISR so that it + * reflects the current state of the hardware (because it's a level + * interrupt that is latched in the IPIF interrupt status register). + * Note that if we've reached this point the bit is guaranteed to be + * set because it was cleared at the top of this ISR before any frames + * were serviced, so the bit was set again immediately by hardware + * because the RPLR was not yet emptied by software. + */ + XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress, + XEM_EIR_RECV_DONE_MASK); + } + + /* + * If configured for direct memory-mapped I/O using FIFO, the xmit status + * FIFO must be read and the callback invoked regardless of success or not. + */ + if (IntrStatus & XEM_EIR_XMIT_DONE_MASK) { + u32 XmitStatus; + + InstancePtr->Stats.XmitInterrupts++; + + XmitStatus = + XIo_In32(InstancePtr->BaseAddress + XEM_TSR_OFFSET); + + /* + * Collision errors are stored in the transmit status register + * instead of the interrupt status register + */ + if (XmitStatus & XEM_TSR_EXCESS_DEFERRAL_MASK) { + InstancePtr->Stats.XmitExcessDeferral++; + } + + if (XmitStatus & XEM_TSR_LATE_COLLISION_MASK) { + InstancePtr->Stats.XmitLateCollisionErrors++; + } + + InstancePtr->FifoSendHandler(InstancePtr->FifoSendRef); + + /* + * Only one status is retrieved per interrupt. We need to clear the + * XMIT_DONE bit before leaving the ISR so that it reflects the current + * state of the hardware (because it's a level interrupt that is latched + * in the IPIF interrupt status register). Note that if we've reached + * this point the bit is guaranteed to be set because it was cleared at + * the top of this ISR before any statuses were serviced, so the bit was + * set again immediately by hardware because the TSR was not yet emptied + * by software. + */ + XIIF_V123B_WRITE_IISR(InstancePtr->BaseAddress, + XEM_EIR_XMIT_DONE_MASK); + } + + /* + * Check the MAC for errors + */ + XEmac_CheckEmacError(InstancePtr, IntrStatus); +} diff -urN -X bkexcl linux-2.4.22/drivers/net/xilinx_enet/xemac_l.h linuxppc-2.4/drivers/net/xilinx_enet/xemac_l.h --- linux-2.4.22/drivers/net/xilinx_enet/xemac_l.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/net/xilinx_enet/xemac_l.h 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,463 @@ +/* $Id: xemac_l.h,v 1.1 2002/12/09 17:42:43 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xemac_l.h +* +* This header file contains identifiers and low-level driver functions (or +* macros) that can be used to access the device. High-level driver functions +* are defined in xemac.h. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00b rpm  04/26/02 First release
+* 1.00b rmm  09/23/02 Added XEmac_mPhyReset macro
+* 1.00c rpm  12/05/02 New version includes support for simple DMA
+* 
+* +******************************************************************************/ + +#ifndef XEMAC_L_H /* prevent circular inclusions */ +#define XEMAC_L_H /* by using protection macros */ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xio.h" + +/************************** Constant Definitions *****************************/ + +/* Offset of the MAC registers from the IPIF base address */ +#define XEM_REG_OFFSET 0x1100UL + +/* + * Register offsets for the Ethernet MAC. Each register is 32 bits. + */ +#define XEM_EMIR_OFFSET (XEM_REG_OFFSET + 0x0) /* EMAC Module ID */ +#define XEM_ECR_OFFSET (XEM_REG_OFFSET + 0x4) /* MAC Control */ +#define XEM_IFGP_OFFSET (XEM_REG_OFFSET + 0x8) /* Interframe Gap */ +#define XEM_SAH_OFFSET (XEM_REG_OFFSET + 0xC) /* Station addr, high */ +#define XEM_SAL_OFFSET (XEM_REG_OFFSET + 0x10) /* Station addr, low */ +#define XEM_MGTCR_OFFSET (XEM_REG_OFFSET + 0x14) /* MII mgmt control */ +#define XEM_MGTDR_OFFSET (XEM_REG_OFFSET + 0x18) /* MII mgmt data */ +#define XEM_RPLR_OFFSET (XEM_REG_OFFSET + 0x1C) /* Rx packet length */ +#define XEM_TPLR_OFFSET (XEM_REG_OFFSET + 0x20) /* Tx packet length */ +#define XEM_TSR_OFFSET (XEM_REG_OFFSET + 0x24) /* Tx status */ +#define XEM_RMFC_OFFSET (XEM_REG_OFFSET + 0x28) /* Rx missed frames */ +#define XEM_RCC_OFFSET (XEM_REG_OFFSET + 0x2C) /* Rx collisions */ +#define XEM_RFCSEC_OFFSET (XEM_REG_OFFSET + 0x30) /* Rx FCS errors */ +#define XEM_RAEC_OFFSET (XEM_REG_OFFSET + 0x34) /* Rx alignment errors */ +#define XEM_TEDC_OFFSET (XEM_REG_OFFSET + 0x38) /* Transmit excess + * deferral cnt */ + +/* + * Register offsets for the IPIF components + */ +#define XEM_ISR_OFFSET 0x20UL /* Interrupt status */ + +#define XEM_DMA_OFFSET 0x2300UL +#define XEM_DMA_SEND_OFFSET (XEM_DMA_OFFSET + 0x0) /* DMA send channel */ +#define XEM_DMA_RECV_OFFSET (XEM_DMA_OFFSET + 0x40) /* DMA recv channel */ + +#define XEM_PFIFO_OFFSET 0x2000UL +#define XEM_PFIFO_TXREG_OFFSET (XEM_PFIFO_OFFSET + 0x0) /* Tx registers */ +#define XEM_PFIFO_RXREG_OFFSET (XEM_PFIFO_OFFSET + 0x10) /* Rx registers */ +#define XEM_PFIFO_TXDATA_OFFSET (XEM_PFIFO_OFFSET + 0x100) /* Tx keyhole */ +#define XEM_PFIFO_RXDATA_OFFSET (XEM_PFIFO_OFFSET + 0x200) /* Rx keyhole */ + +/* + * EMAC Module Identification Register (EMIR) + */ +#define XEM_EMIR_VERSION_MASK 0xFFFF0000UL /* Device version */ +#define XEM_EMIR_TYPE_MASK 0x0000FF00UL /* Device type */ + +/* + * EMAC Control Register (ECR) + */ +#define XEM_ECR_FULL_DUPLEX_MASK 0x80000000UL /* Full duplex mode */ +#define XEM_ECR_XMIT_RESET_MASK 0x40000000UL /* Reset transmitter */ +#define XEM_ECR_XMIT_ENABLE_MASK 0x20000000UL /* Enable transmitter */ +#define XEM_ECR_RECV_RESET_MASK 0x10000000UL /* Reset receiver */ +#define XEM_ECR_RECV_ENABLE_MASK 0x08000000UL /* Enable receiver */ +#define XEM_ECR_PHY_ENABLE_MASK 0x04000000UL /* Enable PHY */ +#define XEM_ECR_XMIT_PAD_ENABLE_MASK 0x02000000UL /* Enable xmit pad insert */ +#define XEM_ECR_XMIT_FCS_ENABLE_MASK 0x01000000UL /* Enable xmit FCS insert */ +#define XEM_ECR_XMIT_ADDR_INSERT_MASK 0x00800000UL /* Enable xmit source addr + * insertion */ +#define XEM_ECR_XMIT_ERROR_INSERT_MASK 0x00400000UL /* Insert xmit error */ +#define XEM_ECR_XMIT_ADDR_OVWRT_MASK 0x00200000UL /* Enable xmit source addr + * overwrite */ +#define XEM_ECR_LOOPBACK_MASK 0x00100000UL /* Enable internal + * loopback */ +#define XEM_ECR_RECV_STRIP_ENABLE_MASK 0x00080000UL /* Enable recv pad/fcs strip */ +#define XEM_ECR_UNICAST_ENABLE_MASK 0x00020000UL /* Enable unicast addr */ +#define XEM_ECR_MULTI_ENABLE_MASK 0x00010000UL /* Enable multicast addr */ +#define XEM_ECR_BROAD_ENABLE_MASK 0x00008000UL /* Enable broadcast addr */ +#define XEM_ECR_PROMISC_ENABLE_MASK 0x00004000UL /* Enable promiscuous mode */ +#define XEM_ECR_RECV_ALL_MASK 0x00002000UL /* Receive all frames */ +#define XEM_ECR_RESERVED2_MASK 0x00001000UL /* Reserved */ +#define XEM_ECR_MULTI_HASH_ENABLE_MASK 0x00000800UL /* Enable multicast hash */ +#define XEM_ECR_PAUSE_FRAME_MASK 0x00000400UL /* Interpret pause frames */ +#define XEM_ECR_CLEAR_HASH_MASK 0x00000200UL /* Clear hash table */ +#define XEM_ECR_ADD_HASH_ADDR_MASK 0x00000100UL /* Add hash table address */ + +/* + * Interframe Gap Register (IFGR) + */ +#define XEM_IFGP_PART1_MASK 0xF8000000UL /* Interframe Gap Part1 */ +#define XEM_IFGP_PART1_SHIFT 27 +#define XEM_IFGP_PART2_MASK 0x07C00000UL /* Interframe Gap Part2 */ +#define XEM_IFGP_PART2_SHIFT 22 + +/* + * Station Address High Register (SAH) + */ +#define XEM_SAH_ADDR_MASK 0x0000FFFFUL /* Station address high bytes */ + +/* + * Station Address Low Register (SAL) + */ +#define XEM_SAL_ADDR_MASK 0xFFFFFFFFUL /* Station address low bytes */ + +/* + * MII Management Control Register (MGTCR) + */ +#define XEM_MGTCR_START_MASK 0x80000000UL /* Start/Busy */ +#define XEM_MGTCR_RW_NOT_MASK 0x40000000UL /* Read/Write Not (direction) */ +#define XEM_MGTCR_PHY_ADDR_MASK 0x3E000000UL /* PHY address */ +#define XEM_MGTCR_PHY_ADDR_SHIFT 25 /* PHY address shift */ +#define XEM_MGTCR_REG_ADDR_MASK 0x01F00000UL /* Register address */ +#define XEM_MGTCR_REG_ADDR_SHIFT 20 /* Register addr shift */ +#define XEM_MGTCR_MII_ENABLE_MASK 0x00080000UL /* Enable MII from EMAC */ +#define XEM_MGTCR_RD_ERROR_MASK 0x00040000UL /* MII mgmt read error */ + +/* + * MII Management Data Register (MGTDR) + */ +#define XEM_MGTDR_DATA_MASK 0x0000FFFFUL /* MII data */ + +/* + * Receive Packet Length Register (RPLR) + */ +#define XEM_RPLR_LENGTH_MASK 0x0000FFFFUL /* Receive packet length */ + +/* + * Transmit Packet Length Register (TPLR) + */ +#define XEM_TPLR_LENGTH_MASK 0x0000FFFFUL /* Transmit packet length */ + +/* + * Transmit Status Register (TSR) + */ +#define XEM_TSR_EXCESS_DEFERRAL_MASK 0x80000000UL /* Transmit excess deferral */ +#define XEM_TSR_FIFO_UNDERRUN_MASK 0x40000000UL /* Packet FIFO underrun */ +#define XEM_TSR_ATTEMPTS_MASK 0x3E000000UL /* Transmission attempts */ +#define XEM_TSR_LATE_COLLISION_MASK 0x01000000UL /* Transmit late collision */ + +/* + * Receive Missed Frame Count (RMFC) + */ +#define XEM_RMFC_DATA_MASK 0x0000FFFFUL + +/* + * Receive Collision Count (RCC) + */ +#define XEM_RCC_DATA_MASK 0x0000FFFFUL + +/* + * Receive FCS Error Count (RFCSEC) + */ +#define XEM_RFCSEC_DATA_MASK 0x0000FFFFUL + +/* + * Receive Alignment Error Count (RALN) + */ +#define XEM_RAEC_DATA_MASK 0x0000FFFFUL + +/* + * Transmit Excess Deferral Count (TEDC) + */ +#define XEM_TEDC_DATA_MASK 0x0000FFFFUL + +/* + * EMAC Interrupt Registers (Status and Enable) masks. These registers are + * part of the IPIF IP Interrupt registers + */ +#define XEM_EIR_XMIT_DONE_MASK 0x00000001UL /* Xmit complete */ +#define XEM_EIR_RECV_DONE_MASK 0x00000002UL /* Recv complete */ +#define XEM_EIR_XMIT_ERROR_MASK 0x00000004UL /* Xmit error */ +#define XEM_EIR_RECV_ERROR_MASK 0x00000008UL /* Recv error */ +#define XEM_EIR_XMIT_SFIFO_EMPTY_MASK 0x00000010UL /* Xmit status fifo empty */ +#define XEM_EIR_RECV_LFIFO_EMPTY_MASK 0x00000020UL /* Recv length fifo empty */ +#define XEM_EIR_XMIT_LFIFO_FULL_MASK 0x00000040UL /* Xmit length fifo full */ +#define XEM_EIR_RECV_LFIFO_OVER_MASK 0x00000080UL /* Recv length fifo + * overrun */ +#define XEM_EIR_RECV_LFIFO_UNDER_MASK 0x00000100UL /* Recv length fifo + * underrun */ +#define XEM_EIR_XMIT_SFIFO_OVER_MASK 0x00000200UL /* Xmit status fifo + * overrun */ +#define XEM_EIR_XMIT_SFIFO_UNDER_MASK 0x00000400UL /* Transmit status fifo + * underrun */ +#define XEM_EIR_XMIT_LFIFO_OVER_MASK 0x00000800UL /* Transmit length fifo + * overrun */ +#define XEM_EIR_XMIT_LFIFO_UNDER_MASK 0x00001000UL /* Transmit length fifo + * underrun */ +#define XEM_EIR_XMIT_PAUSE_MASK 0x00002000UL /* Transmit pause pkt + * received */ +#define XEM_EIR_RECV_DFIFO_OVER_MASK 0x00004000UL /* Receive data fifo + * overrun */ +#define XEM_EIR_RECV_MISSED_FRAME_MASK 0x00008000UL /* Receive missed frame + * error */ +#define XEM_EIR_RECV_COLLISION_MASK 0x00010000UL /* Receive collision + * error */ +#define XEM_EIR_RECV_FCS_ERROR_MASK 0x00020000UL /* Receive FCS error */ +#define XEM_EIR_RECV_LEN_ERROR_MASK 0x00040000UL /* Receive length field + * error */ +#define XEM_EIR_RECV_SHORT_ERROR_MASK 0x00080000UL /* Receive short frame + * error */ +#define XEM_EIR_RECV_LONG_ERROR_MASK 0x00100000UL /* Receive long frame + * error */ +#define XEM_EIR_RECV_ALIGN_ERROR_MASK 0x00200000UL /* Receive alignment + * error */ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/***************************************************************************** +* +* Low-level driver macros and functions. The list below provides signatures +* to help the user use the macros. +* +* u32 XEmac_mReadReg(u32 BaseAddress, int RegOffset) +* void XEmac_mWriteReg(u32 BaseAddress, int RegOffset, u32 Mask) +* +* void XEmac_mSetControlReg(u32 BaseAddress, u32 Mask) +* void XEmac_mSetMacAddress(u32 BaseAddress, u8 *AddressPtr) +* +* void XEmac_mEnable(u32 BaseAddress) +* void XEmac_mDisable(u32 BaseAddress) +* +* u32 XEmac_mIsTxDone(u32 BaseAddress) +* u32 XEmac_mIsRxEmpty(u32 BaseAddress) +* +* void XEmac_SendFrame(u32 BaseAddress, u8 *FramePtr, int Size) +* int XEmac_RecvFrame(u32 BaseAddress, u8 *FramePtr) +* +*****************************************************************************/ + +/****************************************************************************/ +/** +* +* Read the given register. +* +* @param BaseAddress is the base address of the device +* @param RegOffset is the register offset to be read +* +* @return The 32-bit value of the register +* +* @note None. +* +*****************************************************************************/ +#define XEmac_mReadReg(BaseAddress, RegOffset) \ + XIo_In32((BaseAddress) + (RegOffset)) + +/****************************************************************************/ +/** +* +* Write the given register. +* +* @param BaseAddress is the base address of the device +* @param RegOffset is the register offset to be written +* @param Data is the 32-bit value to write to the register +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +#define XEmac_mWriteReg(BaseAddress, RegOffset, Data) \ + XIo_Out32((BaseAddress) + (RegOffset), (Data)) + +/****************************************************************************/ +/** +* +* Set the contents of the control register. Use the XEM_ECR_* constants +* defined above to create the bit-mask to be written to the register. +* +* @param BaseAddress is the base address of the device +* @param Mask is the 16-bit value to write to the control register +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +#define XEmac_mSetControlReg(BaseAddress, Mask) \ + XIo_Out32((BaseAddress) + XEM_ECR_OFFSET, (Mask)) + +/****************************************************************************/ +/** +* +* Set the station address of the EMAC device. +* +* @param BaseAddress is the base address of the device +* @param AddressPtr is a pointer to a 6-byte MAC address +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +#define XEmac_mSetMacAddress(BaseAddress, AddressPtr) \ +{ \ + u32 MacAddr; \ + \ + MacAddr = ((AddressPtr)[0] << 8) | (AddressPtr)[1]; \ + XIo_Out32((BaseAddress) + XEM_SAH_OFFSET, MacAddr); \ + \ + MacAddr = ((AddressPtr)[2] << 24) | ((AddressPtr)[3] << 16) | \ + ((AddressPtr)[4] << 8) | (AddressPtr)[5]; \ + \ + XIo_Out32((BaseAddress) + XEM_SAL_OFFSET, MacAddr); \ +} + +/****************************************************************************/ +/** +* +* Enable the transmitter and receiver. Preserve the contents of the control +* register. +* +* @param BaseAddress is the base address of the device +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +#define XEmac_mEnable(BaseAddress) \ +{ \ + u32 Control; \ + Control = XIo_In32((BaseAddress) + XEM_ECR_OFFSET); \ + Control &= ~(XEM_ECR_XMIT_RESET_MASK | XEM_ECR_RECV_RESET_MASK); \ + Control |= (XEM_ECR_XMIT_ENABLE_MASK | XEM_ECR_RECV_ENABLE_MASK); \ + XIo_Out32((BaseAddress) + XEM_ECR_OFFSET, Control); \ +} + +/****************************************************************************/ +/** +* +* Disable the transmitter and receiver. Preserve the contents of the control +* register. +* +* @param BaseAddress is the base address of the device +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +#define XEmac_mDisable(BaseAddress) \ + XIo_Out32((BaseAddress) + XEM_ECR_OFFSET, \ + XIo_In32((BaseAddress) + XEM_ECR_OFFSET) & \ + ~(XEM_ECR_XMIT_ENABLE_MASK | XEM_ECR_RECV_ENABLE_MASK)) + +/****************************************************************************/ +/** +* +* Check to see if the transmission is complete. +* +* @param BaseAddress is the base address of the device +* +* @return TRUE if it is done, or FALSE if it is not. +* +* @note None. +* +*****************************************************************************/ +#define XEmac_mIsTxDone(BaseAddress) \ + (XIo_In32((BaseAddress) + XEM_ISR_OFFSET) & XEM_EIR_XMIT_DONE_MASK) + +/****************************************************************************/ +/** +* +* Check to see if the receive FIFO is empty. +* +* @param BaseAddress is the base address of the device +* +* @return TRUE if it is empty, or FALSE if it is not. +* +* @note None. +* +*****************************************************************************/ +#define XEmac_mIsRxEmpty(BaseAddress) \ + (!(XIo_In32((BaseAddress) + XEM_ISR_OFFSET) & XEM_EIR_RECV_DONE_MASK)) + +/****************************************************************************/ +/** +* +* Reset MII compliant PHY +* +* @param BaseAddress is the base address of the device +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +#define XEmac_mPhyReset(BaseAddress) \ +{ \ + u32 Control; \ + Control = XIo_In32((BaseAddress) + XEM_ECR_OFFSET); \ + Control &= ~XEM_ECR_PHY_ENABLE_MASK; \ + XIo_Out32((BaseAddress) + XEM_ECR_OFFSET, Control); \ + Control |= XEM_ECR_PHY_ENABLE_MASK; \ + XIo_Out32((BaseAddress) + XEM_ECR_OFFSET, Control); \ +} + +/************************** Function Prototypes ******************************/ + +void XEmac_SendFrame(u32 BaseAddress, u8 * FramePtr, int Size); +int XEmac_RecvFrame(u32 BaseAddress, u8 * FramePtr); + +#endif /* end of protection macro */ diff -urN -X bkexcl linux-2.4.22/drivers/net/xilinx_enet/xemac_options.c linuxppc-2.4/drivers/net/xilinx_enet/xemac_options.c --- linux-2.4.22/drivers/net/xilinx_enet/xemac_options.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/net/xilinx_enet/xemac_options.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,319 @@ +/* $Id: xemac_options.c,v 1.1 2002/12/09 17:42:43 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xemac_options.c +* +* Functions in this file handle configuration of the XEmac driver. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rpm  07/31/01 First release
+* 1.00b rpm  02/20/02 Repartitioned files and functions
+* 1.00c rpm  12/05/02 New version includes support for simple DMA
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xemac_i.h" +#include "xio.h" + +/************************** Constant Definitions *****************************/ + +#define XEM_MAX_IFG 32 /* Maximum Interframe gap value */ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/* + * A table of options and masks. This table maps the user-visible options with + * the control register masks. It is used in Set/GetOptions as an alternative + * to a series of if/else pairs. Note that the polled options does not have a + * corresponding entry in the control register, so it does not exist in the + * table. + */ +typedef struct { + u32 Option; + u32 Mask; +} OptionMap; + +static OptionMap OptionsTable[] = { + {XEM_UNICAST_OPTION, XEM_ECR_UNICAST_ENABLE_MASK}, + {XEM_BROADCAST_OPTION, XEM_ECR_BROAD_ENABLE_MASK}, + {XEM_PROMISC_OPTION, XEM_ECR_PROMISC_ENABLE_MASK}, + {XEM_FDUPLEX_OPTION, XEM_ECR_FULL_DUPLEX_MASK}, + {XEM_LOOPBACK_OPTION, XEM_ECR_LOOPBACK_MASK}, + {XEM_MULTICAST_OPTION, XEM_ECR_MULTI_ENABLE_MASK}, + {XEM_FLOW_CONTROL_OPTION, XEM_ECR_PAUSE_FRAME_MASK}, + {XEM_INSERT_PAD_OPTION, XEM_ECR_XMIT_PAD_ENABLE_MASK}, + {XEM_INSERT_FCS_OPTION, XEM_ECR_XMIT_FCS_ENABLE_MASK}, + {XEM_INSERT_ADDR_OPTION, XEM_ECR_XMIT_ADDR_INSERT_MASK}, + {XEM_OVWRT_ADDR_OPTION, XEM_ECR_XMIT_ADDR_OVWRT_MASK}, + {XEM_STRIP_PAD_FCS_OPTION, XEM_ECR_RECV_STRIP_ENABLE_MASK} +}; + +#define XEM_NUM_OPTIONS (sizeof(OptionsTable) / sizeof(OptionMap)) + +/*****************************************************************************/ +/** +* +* Set Ethernet driver/device options. The device must be stopped before +* calling this function. The options are contained within a bit-mask with each +* bit representing an option (i.e., you can OR the options together). A one (1) +* in the bit-mask turns an option on, and a zero (0) turns the option off. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param OptionsFlag is a bit-mask representing the Ethernet options to turn on +* or off. See xemac.h for a description of the available options. +* +* @return +* +* - XST_SUCCESS if the options were set successfully +* - XST_DEVICE_IS_STARTED if the device has not yet been stopped +* +* @note +* +* This function is not thread-safe and makes use of internal resources that are +* shared between the Start, Stop, and SetOptions functions, so if one task +* might be setting device options while another is trying to start the device, +* protection of this shared data (typically using a semaphore) is required. +* +******************************************************************************/ +XStatus +XEmac_SetOptions(XEmac * InstancePtr, u32 OptionsFlag) +{ + u32 ControlReg; + int Index; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + return XST_DEVICE_IS_STARTED; + } + + ControlReg = XIo_In32(InstancePtr->BaseAddress + XEM_ECR_OFFSET); + + /* + * Loop through the options table, turning the option on or off + * depending on whether the bit is set in the incoming options flag. + */ + for (Index = 0; Index < XEM_NUM_OPTIONS; Index++) { + if (OptionsFlag & OptionsTable[Index].Option) { + ControlReg |= OptionsTable[Index].Mask; /* turn it on */ + } else { + ControlReg &= ~OptionsTable[Index].Mask; /* turn it off */ + } + } + + /* + * TODO: need to validate addr-overwrite only if addr-insert? + */ + + /* + * Now write the control register. Leave it to the upper layers + * to restart the device. + */ + XIo_Out32(InstancePtr->BaseAddress + XEM_ECR_OFFSET, ControlReg); + + /* + * Check the polled option + */ + if (OptionsFlag & XEM_POLLED_OPTION) { + InstancePtr->IsPolled = TRUE; + } else { + InstancePtr->IsPolled = FALSE; + } + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Get Ethernet driver/device options. The 32-bit value returned is a bit-mask +* representing the options. A one (1) in the bit-mask means the option is on, +* and a zero (0) means the option is off. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* +* @return +* +* The 32-bit value of the Ethernet options. The value is a bit-mask +* representing all options that are currently enabled. See xemac.h for a +* description of the available options. +* +* @note +* +* None. +* +******************************************************************************/ +u32 +XEmac_GetOptions(XEmac * InstancePtr) +{ + u32 OptionsFlag = 0; + u32 ControlReg; + int Index; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Get the control register to determine which options are currently set. + */ + ControlReg = XIo_In32(InstancePtr->BaseAddress + XEM_ECR_OFFSET); + + /* + * Loop through the options table to determine which options are set + */ + for (Index = 0; Index < XEM_NUM_OPTIONS; Index++) { + if (ControlReg & OptionsTable[Index].Mask) { + OptionsFlag |= OptionsTable[Index].Option; + } + } + + if (InstancePtr->IsPolled) { + OptionsFlag |= XEM_POLLED_OPTION; + } + + return OptionsFlag; +} + +/*****************************************************************************/ +/** +* +* Set the Interframe Gap (IFG), which is the time the MAC delays between +* transmitting frames. There are two parts required. The total interframe gap +* is the total of the two parts. The values provided for the Part1 and Part2 +* parameters are multiplied by 4 to obtain the bit-time interval. The first +* part should be the first 2/3 of the total interframe gap. The MAC will reset +* the interframe gap timer if carrier sense becomes true during the period +* defined by interframe gap Part1. Part1 may be shorter than 2/3 the total and +* can be as small as zero. The second part should be the last 1/3 of the total +* interframe gap, but can be as large as the total interframe gap. The MAC +* will not reset the interframe gap timer if carrier sense becomes true during +* the period defined by interframe gap Part2. +* +* The device must be stopped before setting the interframe gap. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param Part1 is the interframe gap part 1 (which will be multiplied by 4 to +* get the bit-time interval). +* @param Part2 is the interframe gap part 2 (which will be multiplied by 4 to +* get the bit-time interval). +* +* @return +* +* - XST_SUCCESS if the interframe gap was set successfully +* - XST_DEVICE_IS_STARTED if the device has not been stopped +* +* @note +* +* None. +* +******************************************************************************/ +XStatus +XEmac_SetInterframeGap(XEmac * InstancePtr, u8 Part1, u8 Part2) +{ + u32 Ifg; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(Part1 < XEM_MAX_IFG); + XASSERT_NONVOID(Part2 < XEM_MAX_IFG); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Be sure device has been stopped + */ + if (InstancePtr->IsStarted == XCOMPONENT_IS_STARTED) { + return XST_DEVICE_IS_STARTED; + } + + Ifg = Part1 << XEM_IFGP_PART1_SHIFT; + Ifg |= (Part2 << XEM_IFGP_PART2_SHIFT); + XIo_Out32(InstancePtr->BaseAddress + XEM_IFGP_OFFSET, Ifg); + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Get the interframe gap, parts 1 and 2. See the description of interframe gap +* above in XEmac_SetInterframeGap(). +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param Part1Ptr is a pointer to an 8-bit buffer into which the interframe gap +* part 1 value will be copied. +* @param Part2Ptr is a pointer to an 8-bit buffer into which the interframe gap +* part 2 value will be copied. +* +* @return +* +* None. The values of the interframe gap parts are copied into the +* output parameters. +* +******************************************************************************/ +void +XEmac_GetInterframeGap(XEmac * InstancePtr, u8 * Part1Ptr, u8 * Part2Ptr) +{ + u32 Ifg; + + XASSERT_VOID(InstancePtr != NULL); + XASSERT_VOID(Part1Ptr != NULL); + XASSERT_VOID(Part2Ptr != NULL); + XASSERT_VOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + Ifg = XIo_In32(InstancePtr->BaseAddress + XEM_IFGP_OFFSET); + *Part1Ptr = (Ifg & XEM_IFGP_PART1_MASK) >> XEM_IFGP_PART1_SHIFT; + *Part2Ptr = (Ifg & XEM_IFGP_PART2_MASK) >> XEM_IFGP_PART2_SHIFT; +} diff -urN -X bkexcl linux-2.4.22/drivers/net/xilinx_enet/xemac_phy.c linuxppc-2.4/drivers/net/xilinx_enet/xemac_phy.c --- linux-2.4.22/drivers/net/xilinx_enet/xemac_phy.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/net/xilinx_enet/xemac_phy.c 2003-07-05 10:55:27.000000000 +1000 @@ -0,0 +1,282 @@ +/* $Id: xemac_phy.c,v 1.1 2002/12/09 17:42:43 moleres Exp $ */ +/****************************************************************************** +* +* Author: Xilinx, Inc. +* +* +* 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. +* +* +* XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" AS A +* COURTESY TO YOU. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION AS +* ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION OR STANDARD, +* XILINX IS MAKING NO REPRESENTATION THAT THIS IMPLEMENTATION IS FREE +* FROM ANY CLAIMS OF INFRINGEMENT, AND YOU ARE RESPONSIBLE FOR OBTAINING +* ANY THIRD PARTY RIGHTS YOU MAY REQUIRE FOR YOUR IMPLEMENTATION. +* XILINX EXPRESSLY DISCLAIMS ANY WARRANTY WHATSOEVER WITH RESPECT TO +* THE ADEQUACY OF THE IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY +* WARRANTIES OR REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM +* CLAIMS OF INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND +* FITNESS FOR A PARTICULAR PURPOSE. +* +* +* Xilinx hardware products are not intended for use in life support +* appliances, devices, or systems. Use in such applications is +* expressly prohibited. +* +* +* (c) Copyright 2002-2003 Xilinx Inc. +* All rights reserved. +* +* +* 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. +* +******************************************************************************/ +/*****************************************************************************/ +/** +* +* @file xemac_phy.c +* +* Contains functions to read and write the PHY through the Ethernet MAC MII +* registers. These assume an MII-compliant PHY. +* +*
+* MODIFICATION HISTORY:
+*
+* Ver   Who  Date     Changes
+* ----- ---- -------- -----------------------------------------------
+* 1.00a rpm  07/31/01 First release
+* 1.00b rpm  02/20/02 Repartitioned files and functions
+* 1.00c rpm  12/05/02 New version includes support for simple DMA
+* 
+* +******************************************************************************/ + +/***************************** Include Files *********************************/ + +#include "xbasic_types.h" +#include "xemac_i.h" +#include "xio.h" + +/************************** Constant Definitions *****************************/ + +#define XEM_MAX_PHY_ADDR 32 /* Maximum PHY address */ +#define XEM_MAX_PHY_REG 32 /* Maximum PHY register number */ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/************************** Function Prototypes ******************************/ + +/************************** Variable Definitions *****************************/ + +/*****************************************************************************/ +/** +* +* Read the current value of the PHY register indicated by the PhyAddress and +* the RegisterNum parameters. The MAC provides the driver with the ability to +* talk to a PHY that adheres to the Media Independent Interface (MII) as +* defined in the IEEE 802.3 standard. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param PhyAddress is the address of the PHY to be read (supports multiple +* PHYs) +* @param RegisterNum is the register number, 0-31, of the specific PHY register +* to read +* @param PhyDataPtr is an output parameter, and points to a 16-bit buffer into +* which the current value of the register will be copied. +* +* @return +* +* - XST_SUCCESS if the PHY was read from successfully +* - XST_NO_FEATURE if the device is not configured with MII support +* - XST_EMAC_MII_BUSY if there is another PHY operation in progress +* - XST_EMAC_MII_READ_ERROR if a read error occurred between the MAC and the PHY +* +* @note +* +* This function is not thread-safe. The user must provide mutually exclusive +* access to this function if there are to be multiple threads that can call it. +*

+* There is the possibility that this function will not return if the hardware +* is broken (i.e., it never sets the status bit indicating that the read is +* done). If this is of concern to the user, the user should provide protection +* from this problem - perhaps by using a different timer thread to monitor the +* PhyRead thread. +* +******************************************************************************/ +XStatus +XEmac_PhyRead(XEmac * InstancePtr, u32 PhyAddress, + u32 RegisterNum, u16 * PhyDataPtr) +{ + u32 MiiControl; + u32 MiiData; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(PhyAddress < XEM_MAX_PHY_ADDR); + XASSERT_NONVOID(RegisterNum < XEM_MAX_PHY_REG); + XASSERT_NONVOID(PhyDataPtr != NULL); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Make sure the device has the management interface + */ + if (!InstancePtr->HasMii) { + return XST_NO_FEATURE; + } + + /* + * Verify that there is no operation in progress already + */ + MiiControl = XIo_In32(InstancePtr->BaseAddress + XEM_MGTCR_OFFSET); + if (MiiControl & XEM_MGTCR_START_MASK) { + /* operation in progress */ + return XST_EMAC_MII_BUSY; + } + + /* + * Set up the MII control register first. We set up a control word with + * the PHY address and register number, then indicate the direction (read), + * then start the operation. + */ + MiiControl = PhyAddress << XEM_MGTCR_PHY_ADDR_SHIFT; + MiiControl |= (RegisterNum << XEM_MGTCR_REG_ADDR_SHIFT); + MiiControl |= (XEM_MGTCR_RW_NOT_MASK | XEM_MGTCR_START_MASK | + XEM_MGTCR_MII_ENABLE_MASK); + + XIo_Out32(InstancePtr->BaseAddress + XEM_MGTCR_OFFSET, MiiControl); + + /* + * Wait for the operation to complete + */ + do { + MiiControl = + XIo_In32(InstancePtr->BaseAddress + XEM_MGTCR_OFFSET); + } + while (MiiControl & XEM_MGTCR_START_MASK); + + /* + * Now read the resulting MII data register. First check to see if + * an error occurred before reading and returning the value in + * the MII data register. + */ + if (MiiControl & XEM_MGTCR_RD_ERROR_MASK) { + /* + * MII read error occurred. Upper layer will need to retry. + */ + return XST_EMAC_MII_READ_ERROR; + } + + /* + * Retrieve the data from the 32-bit register, then copy it to + * the 16-bit output parameter. + */ + MiiData = XIo_In32(InstancePtr->BaseAddress + XEM_MGTDR_OFFSET); + + *PhyDataPtr = (u16) MiiData; + + return XST_SUCCESS; +} + +/*****************************************************************************/ +/** +* +* Write data to the specified PHY register. The Ethernet driver does not +* require the device to be stopped before writing to the PHY. Although it is +* probably a good idea to stop the device, it is the responsibility of the +* application to deem this necessary. The MAC provides the driver with the +* ability to talk to a PHY that adheres to the Media Independent Interface +* (MII) as defined in the IEEE 802.3 standard. +* +* @param InstancePtr is a pointer to the XEmac instance to be worked on. +* @param PhyAddress is the address of the PHY to be written (supports multiple +* PHYs) +* @param RegisterNum is the register number, 0-31, of the specific PHY register +* to write +* @param PhyData is the 16-bit value that will be written to the register +* +* @return +* +* - XST_SUCCESS if the PHY was written to successfully. Since there is no error +* status from the MAC on a write, the user should read the PHY to verify the +* write was successful. +* - XST_NO_FEATURE if the device is not configured with MII support +* - XST_EMAC_MII_BUSY if there is another PHY operation in progress +* +* @note +* +* This function is not thread-safe. The user must provide mutually exclusive +* access to this function if there are to be multiple threads that can call it. +*

+* There is the possibility that this function will not return if the hardware +* is broken (i.e., it never sets the status bit indicating that the write is +* done). If this is of concern to the user, the user should provide protection +* from this problem - perhaps by using a different timer thread to monitor the +* PhyWrite thread. +* +******************************************************************************/ +XStatus +XEmac_PhyWrite(XEmac * InstancePtr, u32 PhyAddress, + u32 RegisterNum, u16 PhyData) +{ + u32 MiiControl; + + XASSERT_NONVOID(InstancePtr != NULL); + XASSERT_NONVOID(PhyAddress < XEM_MAX_PHY_ADDR); + XASSERT_NONVOID(RegisterNum < XEM_MAX_PHY_REG); + XASSERT_NONVOID(InstancePtr->IsReady == XCOMPONENT_IS_READY); + + /* + * Make sure the device has the management interface + */ + if (!InstancePtr->HasMii) { + return XST_NO_FEATURE; + } + + /* + * Verify that there is no operation in progress already + */ + MiiControl = XIo_In32(InstancePtr->BaseAddress + XEM_MGTCR_OFFSET); + if (MiiControl & XEM_MGTCR_START_MASK) { + /* operation in progress */ + return XST_EMAC_MII_BUSY; + } + + /* + * Set up the MII data register first. Write the 16-bit input + * value to the 32-bit data register. + */ + XIo_Out32(InstancePtr->BaseAddress + XEM_MGTDR_OFFSET, (u32) PhyData); + + /* + * Now set up the MII control register. We set up a control + * word with the PHY address and register number, then indicate + * the direction (write), then start the operation. + */ + MiiControl = PhyAddress << XEM_MGTCR_PHY_ADDR_SHIFT; + MiiControl |= (RegisterNum << XEM_MGTCR_REG_ADDR_SHIFT); + MiiControl |= (XEM_MGTCR_START_MASK | XEM_MGTCR_MII_ENABLE_MASK); + + XIo_Out32(InstancePtr->BaseAddress + XEM_MGTCR_OFFSET, MiiControl); + + /* + * Wait for the operation to complete + */ + do { + MiiControl = + XIo_In32(InstancePtr->BaseAddress + XEM_MGTCR_OFFSET); + } + while (MiiControl & XEM_MGTCR_START_MASK); + + /* + * There is no status indicating whether the operation was + * successful or not. + */ + return XST_SUCCESS; +} diff -urN -X bkexcl linux-2.4.22/drivers/pcmcia/Config.in linuxppc-2.4/drivers/pcmcia/Config.in --- linux-2.4.22/drivers/pcmcia/Config.in 2002-06-10 19:33:25.000000000 +1000 +++ linuxppc-2.4/drivers/pcmcia/Config.in 2003-03-14 20:46:44.000000000 +1100 @@ -29,5 +29,8 @@ if [ "$CONFIG_8xx" = "y" ]; then dep_tristate ' M8xx support' CONFIG_PCMCIA_M8XX $CONFIG_PCMCIA fi + if [ "$CONFIG_405LP" = "y" ]; then + dep_tristate ' IBM 4xx PCCF support' CONFIG_PCMCIA_PCCF_4xx $CONFIG_PCMCIA + fi fi endmenu diff -urN -X bkexcl linux-2.4.22/drivers/pcmcia/Makefile linuxppc-2.4/drivers/pcmcia/Makefile --- linux-2.4.22/drivers/pcmcia/Makefile 2002-05-16 18:04:21.000000000 +1000 +++ linuxppc-2.4/drivers/pcmcia/Makefile 2003-03-14 20:46:44.000000000 +1100 @@ -61,6 +61,7 @@ obj-$(CONFIG_PCMCIA_SA1100) += sa1100_cs.o obj-$(CONFIG_PCMCIA_M8XX) += m8xx_pcmcia.o +obj-$(CONFIG_PCMCIA_PCCF_4xx) += pccf_4xx.o sa1100_cs-objs-y := sa1100_generic.o sa1100_cs-objs-$(CONFIG_SA1100_ADSBITSY) += sa1100_adsbitsy.o sa1111_generic.o diff -urN -X bkexcl linux-2.4.22/drivers/pcmcia/pccf_4xx.c linuxppc-2.4/drivers/pcmcia/pccf_4xx.c --- linux-2.4.22/drivers/pcmcia/pccf_4xx.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/pcmcia/pccf_4xx.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,1294 @@ +/* Device driver for the IBM 4xx PCMCIA/CF+ Interface Macro (PCCF) + * + * 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 + * + * Copyright (C) 2001, International Business Machines Corporation + * All Rights Reserved. + * Copyright (C) 2002 MontaVista Software, Inc. + * + * Eric Van Hensbegren + * IBM Austin Research Lab + * + * Based on original code by Magnus Damm for mpc8xx series driver + * + * May, 2001 - Original version for Pecan + * August, 2001 - Modified by Benjamin Li (IBM Austin) for 405LP + * October, 2001 - Modified/Documented by Bishop Brock (IBM Research Austin) + * bcbrock@us.ibm.com + * August, 2002 - Conversion to standalone in-kernel driver by Todd + * Poynor (MontaVista Software) + **************************************************************************** + + Module Parameters + ================= + + pwr5by3=[0|1] + + 0 - [ Default ] Cards are powered by the Vcc voltage they require, either + 3.3 V or 5 V. If the power rail requested is not available, the card + insertion will fail. + + 1 - Cards that request 5 V will be powered by 3.3 V instead. See below for + further details. + + ignore_wrprot=[0|1] + + 0 - [ Default ] Drivers that request write-protected regions will fail + unless the configuration can actually write protect the region. + + 1 - If this module can satisfy a request to write protect a region it will, + otherwise it will simply print a warning and carry on. + + pc_debug= + + A standard parameter for PCMCIA modules. If modules are compiled with + -DPCMCIA_DEBUG=, then debugging prints are enabled and is the default + debugging level. The level can be changed by the pc_debug= parameter + when the module is loaded. Debug level 0 suppresses all messages. + + Introduction + ============ + + The 4xx PCCF (PCMCIA/CF+) macro is an "internal" EBC peripheral that sits + between the EBC controller and the chip IOs. When enabled, PCCF takes control + of 4 EBC chip selects and other EBC signals, and does a straightforward + mapping of EBC transactions to PCMCIA/CF+ transactions. The type of PCMCIA/CF+ + transaction (Attribute Memory, Common Memory, I/O, or Macro Control) is + determined by a simple address decode of the high-order bits of the physical + address. PCCF was first implemented as an external EBC peripheral in an FPGA + on the IBM Austin Research Pecan board. + + PCCF is not compatible with the ExCA standard. PCCF supports only a single + memory window, and this driver does not attempt to partition the single + physical window into multiple virtual windows. Although the PCCF macro + supports only a single IO area as well, this driver gives the illusion that 2 + IO windows exist. This is possible because IO windows don't involve address + translation. On the other hand, the implementation can not support different + IO window-specific parameters on the two IO windows. + + As implemented in the 405LP, PCCF is not strictly compatible with the PCMCIA + specification, as it only provides 23 of the 26 address bits called for in the + specification. Thus 405LP/PCCF may not support some archaic PCMCIA SRAM + cards, but will support modern PCMCIA cards such as IDE disks and network + adapters, and fully support CF+ cards (but see discussion on power below). + + PCCF supports a single PCMCIA/CF slot. + + EBC Setup and Memory Map + ========================= + + The EBC interface to PCCF can be configured in numerous ways. Also, the + ability of PCCF to generate valid PCMCIA/CF transactions critically depends on + the proper setup of the access parameters (EBC0_BnAP) register(s) for the EBC + bank(s) controlling PCCF. EBC setup is currently handled by this driver for + all supported configurations. This driver maps the PCCF macro in a single EBC + bank at a configuration-dependent address, and the access parameters provide a + minimum 300 ns cycle time for all memory and IO accesses. The EBC access + parameters *must* include sample-on-ready to allow slow devices to control the + timing of their transactions, and the EBC0_CFG register will specify at least + a 12 us timeout (for PCMCIA specification compatibility). + In order to guarantee correct operation for all cards, the board + design must correctly bring in the PCMCIA WAIT# signal. + + The PCCF macro maps a 32 MB physical address space as indicated below. + Offsets are relative to the physical base address. + + 0x00000000 - 0x007fffff : 8 MB PCCF control register space + 0x00800000 - 0x00ffffff : 8 MB Attribute memory space + 0x01000000 - 0x017fffff : 8 MB Common memory space + 0x01800000 - 0x018fffff : 8 MB I/O space + + Endianess + ========= + + PCMCIA is a little-endian specification. The PCCF controller byte-swaps all + 16-bit accesses. This means that 16-bit accesses will need to use + byte-swapping IO, as normally occurs in 4xx kernels. The PCCF control + registers are big-endian, however. That's why the accesses of those registers + use in_be16 and out_be16, which are non-swapping operations. + + A note on IDE: IDE control and status operations are 8-bit operations, so + endianess is not an issue. IDE sector reads/writes do not use swapping IO on + PowerPC, *except* for the IDE identification sector, which is swapped so that + it can be interpreted. This allows removable drives to be created on e.g., an + IA32 laptop and read with this controller and vice versa. + + Legacy IO and Address Mapping + ============================= + + Requests for memory windows are given a physical address, which the caller + will (redundantly) ioremap(). Requests for I/O space windows are not + modified. For legacy IO to work, the _IO_BASE symbols is set to reference + isa_io_base, which holds the virtual address of our IO area mapping. + + Power Considerations + ==================== + + Vcc + --- + + The PCCF macro control register includes bits for both 3.3 V and 5 V, Vcc + control. On the 405LP, however, only the 3.3 V control is brought out of the + chip, as 405LP I/O are not 5 V compliant. + + To reduce I/O count the 405LP also does not connect the VS2# signal. VS2# is + pulled high internally to the PCCF macro. + + A 405LP-based system designed for 5 V PCMCIA support will require external + voltage-conversion buffers and an alternate means for selecting the 5 V Vcc + level, e.g., a GPIO pin or CPLD (as on Beech) . Similarly, a 405LP-based + system providing X.X V operation will need a way to signal VS2#. + + The Pecan FPGA implementation of PCCF supports both 3.3 V and 5 V operation, + and both VS1# and VS2#. + + If the current configuration does not support 5V operation, then inserting a 5 + V card into the slot will fail. Vcc = 3.3 V is always assumed to be present. + Some "5 V" PCMCIA cards will operate correctly at Vcc = 3.3 V, however. To + support this mode of operation, the driver can be loaded with the parameter + + pwr5by3=1 + + which will cause all 5 V cards to be powered by 3.3 V (even if the + configuration normally supports 5 V operation). Beware that this may cause + equipment damage (unlikely on a well-designed board) or loss of data (perhaps + more likely). + + Vpp + --- + + PCCF does not directly support Vpp power selection. The default assumption is + that the external power supply is configured according to the PCMCIA standard, + and will supply Vpp = Vcc when power is applied to the socket. If a board is + constructed such that Vpp power selection is possible, the routine + set_Vpp() et. al. will need to be changed accordingly. + + Limitations + =========== + + The controller does not generate interrupts for memory-card status change + events such as battery voltage changes or changes on the READY/BUSY line. + + The macro and card IRQ lines are fixed; The controller can not route the card + interrupt to another IRQ. This may have implications for legacy device + drivers. + + The PCCF macro includes a single control register for all of the memory and IO + registers. It is not possible to set different access parameters (i.e., + 8-bit, 16-bit, auto-size) for different areas. The access parameters are + reset every time a new memory or I/O window is mapped. This may cause some + device drivers to fail when using this controller. + + Furthermore, this module assumes that the highest-level device driver knows + whether it is accessing 8- or 16-bit IO registers, and it is up to the driver + to do inb(), inw() etc. as appropriate for the IO area being accessed. The + PCCF macro can not split 16-bit bus transactions into a pair of 8-bit + transactions as some other controllers can. + + There is currently no support for optimizing memory and IO access speeds. [ + It's not clear this is ever used; In any event, don't do this until we have + our variable-speed mechanism in place. ] + + There is currently no support for write-protecting windows. This can be added + but will require BIOS-like intervention to reprogram the EBC banks as + protection comes on and off line. Single EBC-bank configurations like Pecan + will be difficult and painful to write-protect correctly. + +****************************************************************************/ + +#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 + +MODULE_AUTHOR("Eric Van Hensbergen "); +MODULE_DESCRIPTION("IBM PCCF 4xx socket driver"); +MODULE_PARM(pwr5by3, "i"); +MODULE_PARM_DESC(pwr5by3, "Experimental: Power 5 V cards with 3.3 V"); +MODULE_PARM(ignore_wrprot, "i"); +MODULE_PARM_DESC(ignore_wrprot, "Experimental: Ignore write protect requests"); + +static int pwr5by3 = 0; +static int ignore_wrprot = 0; + +static const char *version = + "pccf_4xx.c $Revision: 1.3 $ $Date: 2001/10/26 02:22:41 $ (Eric Van Hensbergen)"; + +#define PCMCIA_DEBUG 0 + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +MODULE_PARM_DESC(pc_debug, + "Set pc_debug to a non-0 value to enable debug msgs."); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +#else +#define DEBUG(n, args...) +#endif + +/* Configurations */ + +#define VCC5V_SUPPORT 1 + +/* Minimum bus cycle time (in ns) */ + +#define MIN_SPEED 300 + +#define PCMCIA_ATTRIB_OFFSET 0x00800000 +#define PCMCIA_ATTRIB_PADDR (PCCF_4XX_PADDR + PCMCIA_ATTRIB_OFFSET) + +#define PCMCIA_MEM_WIN_NO 1 +#define PCMCIA_IO_WIN_NO 2 + +#define PCMCIA_CTRL(offset)\ + ((volatile unsigned short *)((u8 *) pccf_4xx_macro_vaddr + (offset))) + +/* PCCF control registers are big-endian */ + +#define GET_PCMCIA_CTRL(offset) (in_be16(PCMCIA_CTRL(offset))) +#define SET_PCMCIA_CTRL(value, offset) (out_be16(PCMCIA_CTRL(offset), value)) + +#define PCMCIA_CSR GET_PCMCIA_CTRL(0x00) /* Card Status Register */ +#define PCMCIA_CCR GET_PCMCIA_CTRL(0x02) /* Card Control Register */ +#define PCMCIA_ISR GET_PCMCIA_CTRL(0x04) /* Interrupt Status Register */ +#define PCMCIA_ICR GET_PCMCIA_CTRL(0x06) /* Interrupt Control Register */ +#define PCMCIA_MRR GET_PCMCIA_CTRL(0x0e) /* Macro Reset Register */ + +#define SET_PCMCIA_CSR(value) SET_PCMCIA_CTRL(value, 0x00) +#define SET_PCMCIA_CCR(value) SET_PCMCIA_CTRL(value, 0x02) +#define SET_PCMCIA_ISR(value) SET_PCMCIA_CTRL(value, 0x04) +#define SET_PCMCIA_ICR(value) SET_PCMCIA_CTRL(value, 0x06) +#define SET_PCMCIA_MRR(value) SET_PCMCIA_CTRL(value, 0x0e) + +#ifndef get_field16 +#define get_field16(reg, hi, lo)\ +((reg >> (15 - (lo))) & ~(-1 << ((lo) - (hi) + 1))) + +#define make_field16(val, hi, lo)\ +(((val) & ~(-1 << ((lo) - (hi) + 1))) << (15 - (lo))) +#endif /* get_field16 */ + +#ifndef get_field32 +#define get_field32(reg, hi, lo)\ +((reg >> (31 - (lo))) & ~(-1 << ((lo) - (hi) + 1))) + +#define make_field32(val, hi, lo)\ +(((val) & ~(-1 << ((lo) - (hi) + 1))) << (31 - (lo))) + +#define set_field32(word, hi, lo, val)\ +(((word) & ~(make_field32(0xffffffff, hi, lo))) | make_field32(val, hi, lo)) +#endif /* get_field32 */ + +#define PCMCIA_CSR_DETECT(csr) (get_field16(csr, 2, 2)) +#define PCMCIA_CSR_VS1(csr) (get_field16(csr, 5, 5)) +#define PCMCIA_CSR_VS2(csr) (get_field16(csr, 4, 4)) +#define PCMCIA_CSR_IO(csr) (get_field16(csr, 8, 8)) +#define PCMCIA_CSR_PWR(csr) (get_field16(csr, 14, 15)) +#define PCMCIA_CSR_READY(csr) (get_field16(csr, 12, 12)==0) + +#define PCMCIA_CCR_MODE(ccr) get_field16(ccr, 0, 1) +#define PCMCIA_CCR_3V(ccr) get_field16(ccr, 6, 6) +#define PCMCIA_CCR_5V(ccr) get_field16(ccr, 7, 7) +#define PCMCIA_CCR_816(ccr) get_field16(ccr, 12, 12) +#define PCMCIA_CCR_IOIS16(ccr) get_field16(ccr, 13, 13) + +#define PCMCIA_ISR_STSCHG(isr) get_field16(isr, 6, 6) +#define PCMCIA_ISR_DETECT(isr) get_field16(isr, 7, 7) + +#define PCMCIA_ICR_STSCHG(icr) get_field16(icr, 4, 4) +#define PCMCIA_ICR_DETECT(icr) get_field16(icr, 5, 5) + +/* These are values used to represent voltage levels */ +#define VOLT00 00 +#define VOLT33 33 +#define VOLT50 50 + +/**************************************************************************** + PCCF control routines +****************************************************************************/ + +/* Pecan and Beech Pass 1 use the PCCF macro for power control (although Beech + Pass 1 doesn't support +5V. + + Beech Pass 2 uses an FPGA register to control the 5V power supply. When + moving between power modes on Beech, we're always careful to transit via a + state that carefully powers-down the socket (order is important). The + ground state on the Beech 2 socket power is PCCF 3V bit == 0, FPGA 5V bit = + 0. Setting exactly one of these to 1 enables the proper voltage on the + socket. */ + +static void +pcmcia_0v(void) +{ /* Remove power */ + +#if defined(CONFIG_BEECH) + { + unsigned long flags; + + save_flags(flags); + cli(); + *beech_fpga_reg_4 &= ~FPGA_REG_4_PCMCIA_5V; + restore_flags(flags); + } +#endif + + { + unsigned short ccr = PCMCIA_CCR; + + ccr &= ~0x0200; /* -+3V */ + ccr &= ~0x0100; /* -+5V */ + SET_PCMCIA_CCR(ccr); + } +} + +static void +pcmcia_3v(void) +{ /* Supply 3 V */ + unsigned short ccr = PCMCIA_CCR; + + pcmcia_0v(); + + ccr |= 0x0200; /* ++3V */ + ccr &= ~0x0100; /* -+5V */ + SET_PCMCIA_CCR(ccr); +} + +static void +pcmcia_5v(void) +{ /* Supply 5 V */ + unsigned short ccr = PCMCIA_CCR; + + pcmcia_0v(); + + ccr &= ~0x0200; /* -+3V */ + ccr |= 0x0100; /* ++5V */ + SET_PCMCIA_CCR(ccr); + +#if defined(CONFIG_BEECH) + { + unsigned long flags; + + save_flags(flags); + cli(); + *beech_fpga_reg_4 |= FPGA_REG_4_PCMCIA_5V; + restore_flags(flags); + } +#endif +} + +static void +pcmcia_reset(void) +{ /* Assert card reset for 10 us */ + unsigned short ccr = PCMCIA_CCR; + + ccr |= 0x0080; /* Reset */ + SET_PCMCIA_CCR(ccr); + udelay(10); + ccr = PCMCIA_CCR; + ccr &= ~0x0080; + SET_PCMCIA_CCR(ccr); +} + +static void +pcmcia_mrr(void) +{ /* Reset the PCCF macro */ + SET_PCMCIA_MRR(0xffff); +} + +static void +pcmcia_mem(void) +{ /* Set memory mode */ + unsigned short ccr = PCMCIA_CCR; + + ccr |= 0xc000; + SET_PCMCIA_CCR(ccr); +} + +static void +pcmcia_io(void) +{ /* Set memory and IO mode */ + unsigned short ccr = PCMCIA_CCR; + + ccr |= 0x4000; + ccr &= ~0x8000; + SET_PCMCIA_CCR(ccr); +} + +static void +pcmcia_clr_cdi(void) +{ /* Clear the card-detect interrupt */ + unsigned short icr = PCMCIA_ICR; + + icr |= 0x0100; + SET_PCMCIA_ICR(icr); + icr = PCMCIA_ICR; + icr &= ~0x0100; + SET_PCMCIA_ICR(icr); +} + +static void +pcmcia_unmask_cdi(void) +{ /* Unmask the card-detect interrupt */ + unsigned short icr = PCMCIA_ICR; + + icr &= ~0x0400; + SET_PCMCIA_ICR(icr); +} + +static void +pcmcia_unmask_sti(void) +{ /* Unmask the status-change interrupt */ + unsigned short icr = PCMCIA_ICR; + + icr &= ~0x0800; + SET_PCMCIA_ICR(icr); +} + +static void +pcmcia_mask_cdi(void) +{ /* Mask the card-detect interrupt */ + unsigned short icr = PCMCIA_ICR; + + icr |= 0x0400; + SET_PCMCIA_ICR(icr); +} + +static void +pcmcia_mask_sti(void) +{ /* Mask the status-change interrupt */ + unsigned short icr = PCMCIA_ICR; + + icr |= 0x0800; + SET_PCMCIA_ICR(icr); +} + +static void +pcmcia_16bit(void) +{ /* Set 16-bit mode */ + unsigned short ccr = PCMCIA_CCR; + + ccr &= ~0x000c; + SET_PCMCIA_CCR(ccr); +} + +static void +pcmcia_autosz(void) +{ /* Set IOIS16# mode */ + unsigned short ccr = PCMCIA_CCR; + + ccr &= ~0x000c; + ccr |= 0x0004; + SET_PCMCIA_CCR(ccr); +} + +static void +pcmcia_8bit(void) +{ /* Set 8-bit mode */ + unsigned short ccr = PCMCIA_CCR; + + ccr &= ~0x000c; + ccr |= 0x0008; + SET_PCMCIA_CCR(ccr); +} + +/* This structure records the macro interrupt number and its handler and + private data, a saved copy of the socket state, and memory and IO window + structures. */ + +typedef struct socket_info_t { + u_char intr; + void (*handler) (void *info, u_int events); + void *info; + socket_state_t state; + struct pccard_mem_map mem_win[PCMCIA_MEM_WIN_NO]; + struct pccard_io_map io_win[PCMCIA_IO_WIN_NO]; +} socket_info_t; + +/* Our socket(s) support(s) PCMCIA/CF+ cards (not cardbus) with static memory + maps, no ISA IRQs, and a single fixed socket interrupt. + + The PCCF controller does not place any restrictions on memory window sizes. + However, the card services driver seems to use the map_size a lot during + numerous ioremap()ing and iounmap()ing operations. To be safe we are + setting the map_size parameter to PAGE_SIZE, which is (similar to) what + other drivers do. + +*/ + +static socket_cap_t capabilities = { + SS_CAP_PCCARD | SS_CAP_STATIC_MAP, /* PCMCIA, static map */ + 0, /* No ISA interrupts */ + PAGE_SIZE, /* Window size granularity */ + 0, /* io_offset */ + PCCF_4XX_CARD_IRQ, /* Single fixed socket interrupt */ + NULL, /* No CardBus support */ + NULL /* No virtual bus operations */ +}; + +/* Socket parameters and data */ + +#define TOTAL_NUM_OF_SOCKETS 1 +static int sockets = TOTAL_NUM_OF_SOCKETS; +static socket_info_t socket[TOTAL_NUM_OF_SOCKETS]; + +/**************************************************************************** + Configuration-specific code +****************************************************************************/ + +/* Voltage control. See comments at the beginning of the file. + + The 'poweroff' situation is called out separately because we also use the + occasion of a powerdown to reset some state. This is the default state of + the socket - status change interrupt masked, the card in memory mode and + 16-bit mode. + + These routines will probably be slightly different for each + chip/board. set_voltage() is called with the requested Vcc and Vpp values, + which may be modified. In the event of an error, power is removed from the + socket. */ + +static int +set_Vcc(int *Vcc) +{ + switch (*Vcc) { + case VOLT00: + case VOLT33: + break; + case VOLT50: + if (pwr5by3) { + printk(KERN_WARNING "pccf_4xx: Warning: Powering 5 V " + "card with 3.3 V. Good Luck!\n"); + *Vcc = VOLT33; + } else if (!VCC5V_SUPPORT) { + printk(KERN_ERR "pccf_4xx: The card requested " + "Vcc = 5 V, which is not supported by this\n" + "platform. You can try reloading the module " + " with the parameter pwr5by3=1,\n" + "which will cause all 5 V cards to be powered " + " by 3.3 V. Beware that this may cause\n" + "equipment damage or loss of data.\n"); + return -EINVAL; + } + break; + default: + printk(KERN_ERR "pccf_4xx: Illegal Vcc voltage = %d\n", *Vcc); + return -EINVAL; + break; + } + + switch (*Vcc) { + case VOLT00: + pcmcia_0v(); + break; + case VOLT33: + pcmcia_3v(); + break; + default: + pcmcia_5v(); + break; + } + return 0; +} + +/* We assume that if Vpp request is <= Vcc, then Vcc = Vpp is OK. If we're in + pwr5by3 mode then Vcc 3.3, Vpp = 5 is OK, too. The thing that can't be + supported (on PECAN) are old memory cards that require 12 V for flash + update. */ + +static int +set_Vpp(int *Vcc, int *Vpp) +{ + if (*Vcc != *Vpp) { + if (*Vpp > *Vcc) { + if (!(pwr5by3 && (*Vcc = VOLT33) & (*Vpp = VOLT50))) { + printk(KERN_ERR "pccf_4xx: Can't supply" + " Vpp = %d with Vcc = %d\n", *Vpp, *Vcc); + return -EINVAL; + } + } + DEBUG(0, "pccf_4xx: Setting Vpp = Vcc = " + "%d.%d\n", *Vcc / 10, *Vcc % 10); + *Vpp = *Vcc; + } + return 0; +} + +static void +poweroff(void) +{ + int Vcc = 0, Vpp = 0; + + set_Vcc(&Vcc); + set_Vpp(&Vcc, &Vpp); + pcmcia_mask_sti(); + pcmcia_mem(); + pcmcia_16bit(); +} + +static int +set_voltage(int *Vcc, int *Vpp) +{ + if ((*Vcc == 0) && (*Vpp == 0)) + poweroff(); + if (set_Vcc(Vcc)) + goto error_exit; + if (set_Vpp(Vcc, Vpp)) + goto error_exit; + return 0; + + error_exit: + poweroff(); + return -EINVAL; +} + +/* Write protection is currently unimplemented */ + +static int +write_protect_io(unsigned int sock, u_char map) +{ + if (ignore_wrprot) { + printk(KERN_WARNING "pccf_4xx: Request to write protect IO " + "map %d on socket %d will be ignored.\n", map, sock); + return 0; + } else { + printk(KERN_ERR "pccf_4xx: Request to write protect IO " + "map %d on socket %d can not be satisfied. " + "Aborting.\n You can try reloading the module " + "with the paramater ignore_wrprot=1 which " + "will aviod this error, but might allow " + "critical data on the card to be overwritten.\n", + map, sock); + return -EINVAL; + } +} + +static int +write_unprotect_io(unsigned int sock, u_char map) +{ + return 0; +} + +static int +write_protect_mem(unsigned int sock, u_char map) +{ + if (ignore_wrprot) { + printk(KERN_WARNING "pccf_4xx: Request to write protect " + "memory map %d on socket %d will be ignored.\n", + map, sock); + return 0; + } else { + printk(KERN_ERR "pccf_4xx: Request to write protect memory " + "map %d on socket %d can not be satisfied. " + "Aborting.\n You can try reloading the module " + "with the paramater ignore_wrprot=1 which " + "will aviod this error, but might allow " + "critical data on the card to be overwritten.\n", + map, sock); + return -EINVAL; + } +} + +static int +write_unprotect_mem(unsigned int sock, u_char map) +{ + return 0; +} + +/**************************************************************************** + PCMCIA Driver +****************************************************************************/ + +/* This is the handler for the PCCF macro interrupt. The only interrupts + signalled by PCCF are the card detection and status change. This driver can + only clear the card-detect interrupt - the card driver must take care of + status change interrupts. The status-change interrupt bit is only asserted + by PCCF when in IO mode. + + Card detect interrupts always power off the socket and return the socket to + an initial state. If a card-detect interrupt occurs any status change + interrupt is bogus and is ignored -- the card ain*t there or has just been + inserted. */ + +static u_int pending_events[TOTAL_NUM_OF_SOCKETS]; +static spinlock_t pending_event_lock = SPIN_LOCK_UNLOCKED; + +static void +pccf_4xx_task(void *dummy) +{ + u_int events; + int i; + + for (i = 0; i < TOTAL_NUM_OF_SOCKETS; i++) { + spin_lock_irq(&pending_event_lock); + events = pending_events[i]; + pending_events[i] = 0; + spin_unlock_irq(&pending_event_lock); + + if (events && socket[i].handler) + socket[i].handler(socket[i].info, events); + } +} + +static struct tq_struct pccf_4xx_tqentry = { + routine:pccf_4xx_task +}; + +static void +pccf_4xx_interrupt(int irq, void *dev, struct pt_regs *regs) +{ + unsigned int events; + unsigned short isr = PCMCIA_ISR; + + DEBUG(4, "pccf_4xx: interrupt(%d)\n" + "csr = 0x%04x, ccr = 0x%04x, isr = 0x%04x icr = 0x%04x\n", + irq, PCMCIA_CSR, PCMCIA_CCR, PCMCIA_ISR, PCMCIA_ICR); + + if (PCMCIA_ISR_DETECT(isr)) { + pcmcia_clr_cdi(); + poweroff(); + events = SS_DETECT; + } else if (PCMCIA_ISR_STSCHG(isr)) + events = SS_STSCHG; + else + events = 0; + + DEBUG(2, "pccf_4xx: socket 0 event 0x%02x\n", events); + + if (events) { + spin_lock(&pending_event_lock); + pending_events[0] |= events; + spin_unlock(&pending_event_lock); + schedule_task(&pccf_4xx_tqentry); + } +} + +/* This routine registers a callback to be called when the socket driver + receives card status change events. */ + +static int +pccf_4xx_register_callback(unsigned int sock, + void (*handler) (void *, unsigned int), void *info) +{ + DEBUG(4, "pccf_4xx: register_callback(%d) = 0x%p, 0x%p\n", + sock, handler, info); + + if (sock >= TOTAL_NUM_OF_SOCKETS) + return -EINVAL; + + socket[sock].handler = handler; + socket[sock].info = info; + if (handler == NULL) { + MOD_DEC_USE_COUNT; + } else { + MOD_INC_USE_COUNT; + } + return 0; +} + +/* The capabilities are copied to the caller. */ + +static int +pccf_4xx_inquire_socket(unsigned int sock, socket_cap_t * cap) +{ + if (sock >= TOTAL_NUM_OF_SOCKETS) { + DEBUG(4, "pccf_4xx: inquire_socket(%d) failed\n", sock); + return -EINVAL; + } + + DEBUG(0, "pccf_4xx: inquire_socket(%d)\n", sock); + + *cap = capabilities; + return 0; +} + +/* Return the socket status. The PCCF controller does not support all of the + status signals, e.g., the BVD signals. The SS_STSCHG flag is only set if an + unmasked STSCHG interrupt is pending. */ + +static int +pccf_4xx_get_status(unsigned int sock, u_int * value) +{ + unsigned short csr = PCMCIA_CSR; + unsigned short isr = PCMCIA_ISR; + *value = 0; + + if (sock >= TOTAL_NUM_OF_SOCKETS) { + DEBUG(4, "pccf_4xx: get_status(%d) failed\n", sock); + return -EINVAL; + } + + if (PCMCIA_CSR_DETECT(csr)) { + *value |= SS_DETECT; + } + if (PCMCIA_CSR_IO(csr)) { + if (PCMCIA_ISR_STSCHG(isr)) + *value |= SS_STSCHG; + } else { + if (PCMCIA_CSR_READY(csr)) { + *value |= SS_READY; + } + } + + if (PCMCIA_CSR_PWR(csr)) { + *value |= SS_POWERON; + } + + switch ((PCMCIA_CSR_VS1(csr) << 1) | PCMCIA_CSR_VS2(csr)) { + case 3: /* 5V card */ + break; + case 1: + *value |= SS_3VCARD; + break; + default: + *value |= SS_XVCARD; + break; + }; + + DEBUG(1, "pccf_4xx: GetStatus(%d) = %#4.4x\n", sock, *value); + return 0; +} + +/* Copy the socket state structure. */ + +static int +pccf_4xx_get_socket(unsigned int sock, socket_state_t * state) +{ + if (sock >= TOTAL_NUM_OF_SOCKETS) { + DEBUG(4, "pccf_4xx: get_socket(%d) failed\n", sock); + return -EINVAL; + } + + *state = socket[sock].state; /* copy the whole structure */ + + DEBUG(3, "GetSocket(%d) = flags %#3.3x, Vcc %d, Vpp %d, " + "io_irq %d, csc_mask %#2.2x\n", sock, state->flags, + state->Vcc, state->Vpp, state->io_irq, state->csc_mask); + return 0; +} + +/* Set the socket parameters, and apply power to the slot. + + If the io_irq error is triggered it would indicate that the card driver has + its own idea of which interrupt it wants to use, and that its idea is + wrong. Again, this controller does not generate interrupts for memory card + status changes. */ + +static int +pccf_4xx_set_socket(unsigned int sock, socket_state_t * state) +{ + int Vcc = state->Vcc; + int Vpp = state->Vpp; + + if (sock >= TOTAL_NUM_OF_SOCKETS) { + DEBUG(4, "pccf_4xx: set_socket(%d) failed\n", sock); + return -EINVAL; + } + + DEBUG(1, "pccf_4xx: SetSocket(%d, flags %#3.3x, Vcc %d, Vpp %d, " + "io_irq %d, csc_mask %#2.2x)\n", sock, state->flags, + state->Vcc, state->Vpp, state->io_irq, state->csc_mask); + + /* Apply/remove power */ + + set_voltage(&Vcc, &Vpp); + + /* Decode csc_mask requests. */ + + if (state->csc_mask & SS_DETECT) + pcmcia_unmask_cdi(); + else + pcmcia_mask_cdi(); /* This would be extremely bizarre */ + + if (state->csc_mask & SS_STSCHG) + pcmcia_unmask_sti(); + else + pcmcia_mask_sti(); + + if (state->csc_mask & (SS_READY | SS_BATDEAD | SS_BATWARN)) + printk(KERN_WARNING "pccf_4xx: Warning: The PCMCIA controller " + "is unable to automatically detect one or more memory " + "card status changes requested by the device driver. " + "The installed device may not function correctly with " + "this driver in this system, and data may be lost " + "due to battery failure.\n"); + + /* Perform flag actions */ + + if (state->flags & SS_RESET) { + if (Vcc == VOLT00) + printk(KERN_WARNING "pccf_4xx: Request to reset " + " unpowered slot ignored\n"); + else + pcmcia_reset(); + } + + if (state->flags & SS_IOCARD) { + pcmcia_io(); + if (state->io_irq && (state->io_irq != PCCF_4XX_CARD_IRQ)) { + printk(KERN_CRIT "pccf_4xx: io_irq requested as %d, " + "should be %d. Aborting.\n", + state->io_irq, PCCF_4XX_CARD_IRQ); + return -EINVAL; + } + } else { + pcmcia_mem(); + } + + if (state->flags & SS_SPKR_ENA) + printk(KERN_WARNING "pccf_4xx: The request to enable the " + " speaker pin of the installed card will be " + "ignored.\n"); + + /* SS_OUTPUT_ENA -- Nothing to do here in any supported + configuration + SS_PWR_AUTO -- Not supported by the controller */ + + /* Copy the state to the socket */ + + socket[sock].state = *state; + + return 0; +} + +/* Copy the IO map back to the caller */ + +static int +pccf_4xx_get_io_map(unsigned int sock, struct pccard_io_map *io) +{ + u_char map; + + if (sock >= TOTAL_NUM_OF_SOCKETS) { + DEBUG(4, "pccf_4xx: get_io_map(%d) failed\n", sock); + return -EINVAL; + } + + map = io->map; + if (io->map >= PCMCIA_IO_WIN_NO) { + printk(KERN_ERR "pccf_4xx: Driver requested IO map %d " + "which is not supported by this controller\n", map); + return -EINVAL; + } + + *io = socket[sock].io_win[io->map]; + + DEBUG(1, "pccf_4xx: GetIOMap(%d, %d) = %#2.2x, %d ns, " + "%#4.4x-%#4.4x\n", sock, map, io->flags, io->speed, + io->start, io->stop); + return 0; +} + +/* Set up an IO map. This driver uses 2 virtual windows, although there is only + one physical address space on the PCCF macro. + + There's nothing really to "turn on" here. We presume the EBC is always set + up and ready to go. */ + +static int +pccf_4xx_set_io_map(unsigned int sock, struct pccard_io_map *io) +{ + socket_info_t *s = &socket[sock]; + u_char map; + int error; + + if (sock >= TOTAL_NUM_OF_SOCKETS) { + DEBUG(4, "pccf_4xx: set_io_map(%d) failed\n", sock); + return -EINVAL; + } + + DEBUG(1, "pccf_4xx: SetIOMap(%d, %d, %#2.2x, %d ns, " + "%#4.4x-%#4.4x)\n", sock, io->map, io->flags, + io->speed, io->start, io->stop); + + map = io->map; + + if ((io->map >= PCMCIA_IO_WIN_NO) || + (io->start >= PCCF_4XX_IO_WINSIZE) || + (io->stop >= PCCF_4XX_IO_WINSIZE) || (io->stop < io->start)) { + printk(KERN_ERR "pccf_4xx: set_io_map: Illegal " + "IO map: map = %d, start = 0x%08x, stop = 0x%08x\n", + io->map, io->start, io->stop); + return -EINVAL; + } + + if (io->flags & MAP_ACTIVE) { + + if (io->flags & MAP_WRPROT) { + if ((error = write_protect_io(sock, io->map))) + return error; + } else { + if ((error = write_unprotect_io(sock, io->map))) + return error; + } + + if (io->flags & MAP_16BIT) { + pcmcia_16bit(); + } else if (io->flags & MAP_AUTOSZ) { + pcmcia_autosz(); + } else { + pcmcia_8bit(); + } + + /* MAP_0WS ignored + MAP_USE_WAIT ignored (assumed) + MAP_PREFECTH ignored (could be implemented with some + MMU gyrations) + + speed ignored */ + } + + /* Copy the struct and modify the flags to be accurate based on our + limitations. */ + + s->io_win[io->map] = *io; + s->io_win[io->map].flags &= + (MAP_ACTIVE | MAP_WRPROT | MAP_AUTOSZ | MAP_16BIT); + + return 0; +} + +/* Copy the memory map back to the caller */ + +static int +pccf_4xx_get_mem_map(unsigned int sock, struct pccard_mem_map *mem) +{ + if (sock >= TOTAL_NUM_OF_SOCKETS) { + DEBUG(4, "pccf_4xx: get_mem_map(%d) failed\n", sock); + return -EINVAL; + } + + if (mem->map >= PCMCIA_MEM_WIN_NO) { + printk(KERN_ERR "pccf_4xx: Driver requested memory map %d " + "which is not supported by this controller\n", mem->map); + return -EINVAL; + } + + *mem = socket[sock].mem_win[mem->map]; /* copy the struct */ + + DEBUG(1, "pccf_4xx: GetMemMap(%d, %d) = %#2.2x, %d ns, %#5.5lx-%#5." + "5lx, %#5.5x\n", sock, mem->map, mem->flags, mem->speed, + mem->sys_start, mem->sys_stop, mem->card_start); + + return 0; +} + +/* Set the memory map. */ + +static int +pccf_4xx_set_mem_map(unsigned int sock, struct pccard_mem_map *mem) +{ + socket_info_t *s = &socket[sock]; + int error; + if (sock >= TOTAL_NUM_OF_SOCKETS) { + DEBUG(4, "pccf_4xx: set_mem_map(%d) failed\n", sock); + return -EINVAL; + } + + if ((mem->map >= PCMCIA_MEM_WIN_NO) || + (mem->sys_start > mem->sys_stop) || + ((mem->sys_stop - mem->sys_start) >= PCCF_4XX_MEM_WINSIZE) || + ((mem->card_start + (mem->sys_stop - mem->sys_start)) >= + PCCF_4XX_MEM_WINSIZE)) { + DEBUG(4, "pccf_4xx: set_mem_map: Illegal " + "memory map: map = %d, start = 0x%08lx, stop = 0x%08lx " + "card_start = 0x%08x\n", + mem->map, mem->sys_start, mem->sys_stop, mem->card_start); + return -EINVAL; + } + + DEBUG(1, "pccf_4xx: SetMemMap[entry]" + "(%d, %d, %#2.2x, %d ns, %#8.8lx-%#8.8" + "lx, %#5.5x)\n", sock, mem->map, mem->flags, mem->speed, + mem->sys_start, mem->sys_stop, mem->card_start); + + if (mem->flags & MAP_ACTIVE) { + mem->sys_stop -= mem->sys_start; + if (mem->flags & MAP_ATTRIB) + mem->sys_start = PCMCIA_ATTRIB_PADDR + mem->card_start; + else + mem->sys_start = PCCF_4XX_MEM_PADDR + mem->card_start; + mem->sys_stop += mem->sys_start; + } + + DEBUG(2, "pccf_4xx: SetMemMap[exit]" + "(%d, %d, %#2.2x, %d ns, %#8.8lx-%#8.8" + "lx, %#5.5x)\n", sock, mem->map, mem->flags, mem->speed, + mem->sys_start, mem->sys_stop, mem->card_start); + + if (mem->flags & MAP_ACTIVE) { + + if (mem->flags & MAP_WRPROT) { + if ((error = write_protect_mem(sock, mem->map))) + return error; + } else { + if ((error = write_unprotect_mem(sock, mem->map))) + return error; + } + + if (mem->flags & MAP_16BIT) { + pcmcia_16bit(); + } else if (mem->flags & MAP_AUTOSZ) { + pcmcia_autosz(); + } else { + pcmcia_8bit(); + } + + /* MAP_0WS ignored + MAP_USE_WAIT ignored (assumed) */ + + if (mem->speed > MIN_SPEED) { + printk(KERN_ERR "pccf_4xx: Memory speed %d ns is not " + "supported.\n", mem->speed); + return -EINVAL; + } + } + + /* Copy the struct and modify the flags to be accurate based on our + limitations. */ + + s->mem_win[mem->map] = *mem; + s->mem_win[mem->map].flags &= + (MAP_ACTIVE | MAP_WRPROT | MAP_AUTOSZ | MAP_16BIT); + + return 0; +} + +static int +pccf_4xx_sock_init(unsigned int s) +{ + DEBUG(3, "pccf_4xx: sock_init(%d)\n", s); + + pccf_4xx_set_socket(s, &dead_socket); + return 0; + +} + +static int +pccf_4xx_suspend(unsigned int s) +{ + return (pccf_4xx_set_socket(s, &dead_socket)); +} +static void +pccf_4xx_proc_setup(unsigned int sock, struct proc_dir_entry *base) +{ +} + +/* Service table and service dispatching */ + +static struct pccard_operations pccf_4xx_services = { + &pccf_4xx_sock_init, + &pccf_4xx_suspend, + &pccf_4xx_register_callback, + &pccf_4xx_inquire_socket, + &pccf_4xx_get_status, + &pccf_4xx_get_socket, + &pccf_4xx_set_socket, + &pccf_4xx_get_io_map, + &pccf_4xx_set_io_map, + &pccf_4xx_get_mem_map, + &pccf_4xx_set_mem_map, + &pccf_4xx_proc_setup +}; + +/* The initialization/exit functions. */ + +static int __init +init_pccf_4xx(void) +{ + servinfo_t serv; + int error; + + printk(KERN_INFO "%s\n", version); + + DEBUG(0, "pccf_4xx: Physical base address is 0x%08lx\n", PCCF_4XX_PADDR); + DEBUG(0, "pccf_4xx: Virtual base addresses: macro=0x%p, mem=0x%lx" + " io=0x%lx\n", pccf_4xx_macro_vaddr, _ISA_MEM_BASE, _IO_BASE); + + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE + "pccf_4xx: Card services version does not match!\n" + "Card Services is 0x%04x, CS_RELEASE_CODE is 0x%04x\n", + serv.Revision, CS_RELEASE_CODE); + return -EBUSY; + } + + pcmcia_mrr(); /* Reset the PCCF Macro */ + poweroff(); /* Make sure socket is powered down */ + pcmcia_mask_cdi(); /* Mask card-detect interrupt */ + pcmcia_mask_sti(); /* Mask status change interrupt */ + pcmcia_clr_cdi(); /* Clear card detect status */ + + /* Note that the PCMCIA macro interrupts are not shared. */ + + socket[0].intr = PCCF_4XX_MACRO_IRQ; + + if ((error = request_irq(PCCF_4XX_MACRO_IRQ, pccf_4xx_interrupt, 0, "pcmcia", + &socket[0])) != 0) { + printk(KERN_ERR "pccf_4xx: IRQ %d not available!\n", PCCF_4XX_MACRO_IRQ); + return error; + } + + if ((error = register_ss_entry(sockets, &pccf_4xx_services)) != 0) { + printk(KERN_ERR "pccf_4xx: register_ss_entry() failed!\n"); + goto error_ss; + } + + return 0; + + error_ss: + free_irq(socket[0].intr, &socket[0]); + return error; +} + +static void __exit +exit_pccf_4xx(void) +{ + poweroff(); + pcmcia_mrr(); + + request_module("pcmcia_core"); + + unregister_ss_entry(&pccf_4xx_services); + + free_irq(socket[0].intr, &socket[0]); +} + +module_init(init_pccf_4xx); +module_exit(exit_pccf_4xx); diff -urN -X bkexcl linux-2.4.22/drivers/sound/Config.in linuxppc-2.4/drivers/sound/Config.in --- linux-2.4.22/drivers/sound/Config.in 2003-08-24 09:04:15.000000000 +1000 +++ linuxppc-2.4/drivers/sound/Config.in 2003-08-26 11:57:13.000000000 +1000 @@ -235,3 +235,11 @@ define_tristate CONFIG_INPUT_GAMEPORT y fi fi + +if [ "$CONFIG_BEECH" = "y" ]; then + dep_tristate ' CSI/TI-TLV320AIC23 on Beech' CONFIG_SOUND_CSITI $CONFIG_SOUND +fi + +if [ "$CONFIG_ARCTIC2" = "y" ]; then + dep_tristate ' CSI/TI-TLV320AIC23 on Arctic-II' CONFIG_SOUND_CSITI $CONFIG_SOUND +fi diff -urN -X bkexcl linux-2.4.22/drivers/sound/Makefile linuxppc-2.4/drivers/sound/Makefile --- linux-2.4.22/drivers/sound/Makefile 2003-08-24 09:04:15.000000000 +1000 +++ linuxppc-2.4/drivers/sound/Makefile 2003-08-26 11:57:13.000000000 +1000 @@ -81,6 +81,8 @@ obj-$(CONFIG_SOUND_RME96XX) += rme96xx.o obj-$(CONFIG_SOUND_BT878) += btaudio.o obj-$(CONFIG_SOUND_IT8172) += ite8172.o ac97_codec.o +obj-$(CONFIG_SOUND_CSI3K) += ibmcsi3k.o +obj-$(CONFIG_SOUND_CSITI) += ibmcsiti.o ifeq ($(CONFIG_MIDI_EMU10K1),y) obj-$(CONFIG_SOUND_EMU10K1) += sound.o diff -urN -X bkexcl linux-2.4.22/drivers/sound/ibmcsi3k.c linuxppc-2.4/drivers/sound/ibmcsi3k.c --- linux-2.4.22/drivers/sound/ibmcsi3k.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/sound/ibmcsi3k.c 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,3725 @@ +/* + * ibmcsi3k.c : IBM PowerPC 405LP Codec Serial Interface (CSI) + + * Si3000 voiceband codec driver + * for the 405LP evaluation board + * + * Based on various sound drivers in linux/drivers/sound, including but not limited to + * es1370.c and vwsnd.c, as well as an unfinished driver from IBM Austin Research Lab. + * + * Copyright (C) 2002 IBM Corp. + * + * Module command line parameters: + * To be determined + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, hopefully OSS compatible + * /dev/mixer standard /dev/mixer device, hopefully OSS compatible + * + * TODOs: + * + * - Sampling rate is fixed at 11.025 KHz. + * - Sample format is limited to 16 bit big endian. + * (this is a deviation since an OSS DSP device is supposed to support 8 bit as default.) + * - Drain DAC/ADC + * - Fragment handling + * - Simultaneous capture and playback (TBD) + * - MMAP support (TBD) + * - Split CSI and Si3000 drivers (TBD) + * - Module parameters (TBD) + * - 128 bit per frame timing adjustment for all CPU speeds (TBD) + * - Tune retry counts and jiffies + * - Revisit inline functions + * + * - Write ibmcsi3k.txt in the Documentation directory + * - Anything else? + * + * 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 +#include + +#include + +#include "ibmcsi3k.h" + +/*****************************************************************************/ +/* Start #defines that might belong in ibm405lp.h */ + +#define CSI0_IO_BASE 0xEF600900 /* CSI I/O base address */ + +#define CSI0_ER (CSI0_IO_BASE + 0) /* Enable register */ +#define CSI0_CFG (CSI0_IO_BASE + 4) /* Configuration register */ +#define CSI0_SR (CSI0_IO_BASE + 8) /* Status register */ +#define CSI0_TBUF (CSI0_IO_BASE + 0x0C) /* Transmit buffer */ +#define CSI0_RBUF (CSI0_IO_BASE + 0x10) /* Receive buffer */ +#define CSI0_ID (CSI0_IO_BASE + 0x14) /* ID */ +#define CSI0_SCR (CSI0_IO_BASE + 0x18) /* Sleep control register */ + +#define CSI0_EXTENT 28 /* I/O address extent */ +#define CSI_ID_405LP 0x1107 /* CSI core ID (halfword) */ + + +#define CSI_ER_ECSI 0x00000001 /* Enable the CSI */ +#define CSI_ER_RXEN 0x00000002 /* Receive enable */ +#define CSI_ER_TXEN 0x00000004 /* Transmit enable */ + +#define CSI_ER_ESLOT(n) (0x80000000 >> (n)) /* Enable (n)th slot */ + +#define CSI_SR_TOD 0x00000001 /* Transmit overrun */ +#define CSI_SR_ROD 0x00000002 /* Receive overrun */ +#define CSI_SR_CSIB 0x00008000 /* CSI Busy */ + +/*-----------------------------------------------------------------------------+ +| DMA Channel Control register +| (Device Control Register bus register DCRN_DMA0_CR0 through DCRN_DMA0_CR3) ++-----------------------------------------------------------------------------*/ + +#define DCRN_DMA_CR_CE 0x80000000 /* Channel Enable */ +#define DCRN_DMA_CR_CIE 0x40000000 /* Channel Interrupt Enable */ +#define DCRN_DMA_CR_TD 0x20000000 /* Transfer Direction */ +#define DCRN_DMA_CR_PL 0x10000000 /* Peripheral Location */ +#define DCRN_DMA_CR_PW_8 0x00000000 /* 8 bit peripheral width */ +#define DCRN_DMA_CR_PW_16 0x04000000 /* 16 bit peripheral width */ +#define DCRN_DMA_CR_PW_32 0x08000000 /* 32 bit peripheral width */ +#define DCRN_DMA_CR_PW_64 0x0c000000 /* 64 bit peripheral width */ +#define DCRN_DMA_CR_DAI 0x02000000 /* Destination Addr Increment */ +#define DCRN_DMA_CR_SAI 0x01000000 /* Source Address Increment */ +#define DCRN_DMA_CR_BE 0x00800000 /* Buffer Enable */ +#define DCRN_DMA_CR_TM_BUFFERED 0x00000000 /* Buffered Transfer mode */ +#define DCRN_DMA_CR_TM_SW_MEM_TO_MEM 0x00400000 /* Software started mem to mem*/ +#define DCRN_DMA_CR_TM_HW_MEM_TO_MEM 0x00600000 /* Hardware paced mem to mem */ +#define DCRN_DMA_CR_PSC_0 0x00000000 /* 0 Peripheral Setup Cycles */ +#define DCRN_DMA_CR_PSC_1 0x00080000 /* 1 Peripheral Setup Cycles */ +#define DCRN_DMA_CR_PSC_2 0x00100000 /* 2 Peripheral Setup Cycles */ +#define DCRN_DMA_CR_PSC_3 0x00180000 /* 3 Peripheral Setup Cycles */ +#define DCRN_DMA_CR_PSC(n) (((n)&0x3)<<19) /* n Peripheral setup cycles */ +#define DCRN_DMA_CR_PWC(n) (((n)&0x3f)<<13) /* n peripheral wait cycles */ +#define DCRN_DMA_CR_PHC(n) (((n)&0x7)<<10) /* n peripheral hold cycles */ +#define DCRN_DMA_CR_ETD 0x00000200 /* EOT/TC Pin Direction */ +#define DCRN_DMA_CR_TCE 0x00000100 /* Terminal Count Enable */ +#define DCRN_DMA_CR_CP_MASK 0x000000C0 /* Channel Priority */ +#define DCRN_DMA_CR_CP(n) (((n)&0x3)<<6) +#define DCRN_DMA_CR_PF 0x00000030 /* Memory read prefetch trans */ +#define DCRN_DMA_CR_PF_1 0x00000000 /* Prefetch 1 dword */ +#define DCRN_DMA_CR_PF_2 0x00000010 /* Prefetch 2 dword */ +#define DCRN_DMA_CR_PF_4 0x00000020 /* Prefetch 4 dword */ +#define DCRN_DMA_CR_PCE 0x00000008 /* Parity check enable */ +#define DCRN_DMA_CR_DEC 0x00000004 /* Address decrement */ + +/*-----------------------------------------------------------------------------+ +| DMA Status Register +| (Device Control Register bus register DCRN_DMA0_SR) ++-----------------------------------------------------------------------------*/ +/* (n) = DMA channel number, 0-3 */ + +#define DCRN_DMA_SR_CS(n) (0x80000000 >>(n)) /* Terminal count status */ +#define DCRN_DMA_SR_TS(n) (0x08000000 >>(n)) /* End Of Transfer status */ +#define DCRN_DMA_SR_RI(n) (0x00800000 >>(n)) /* Error status */ +#define DCRN_DMA_SR_IR(n) (0x00080000 >>(n)) /* Internal DMA request pending */ +#define DCRN_DMA_SR_ER(n) (0x00008000 >>(n)) /* External DMA request pending */ +#define DCRN_DMA_SR_CB(n) (0x00000800 >>(n)) /* Channel Busy */ +#define DCRN_DMA_SR_SG(n) (0x00000080 >>(n)) /* Scatter/gather status */ + +/* Status register bits for the (n)th channel (write to clear) */ +#define DCRN_DMA_SR_ALL(n) (DCRN_DMA_SR_CS(n) | \ + DCRN_DMA_SR_TS(n) | \ + DCRN_DMA_SR_RI(n) | \ + DCRN_DMA_SR_IR(n) | \ + DCRN_DMA_SR_ER(n) | \ + DCRN_DMA_SR_CB(n) | \ + DCRN_DMA_SR_SG(n) ) + +/* DCRN_DMA0_SGC Scatter/Gather Command Register bits */ +#define DCRN_DMA_SGC_SSG0 0x80000000 +#define DCRN_DMA_SGC_SSG1 0x40000000 +#define DCRN_DMA_SGC_SSG2 0x20000000 +#define DCRN_DMA_SGC_SSG3 0x10000000 + +#define DCRN_DMA_SGC_EM0 0x00008000 +#define DCRN_DMA_SGC_EM1 0x00004000 +#define DCRN_DMA_SGC_EM2 0x00002000 +#define DCRN_DMA_SGC_EM3 0x00001000 + + +struct dma_sgdt { /* Must be word aligned */ + unsigned int ccw; /* Channel Control Word */ + void *srcP; /* Source address (physical) */ + void *destP; /* Destination address (physical) */ + unsigned int ctrl; /* MSB = link bit, lower halfword = count */ + /* Other 3 bits unused */ + void *nextP; /* Next scatter/gather descriptor list physical address */ + /* ------------------------------------- Private use ---------------*/ + struct dma_sgdt *prevV; /* Prev scatter/gather descriptor list virtual address */ + struct dma_sgdt *nextV; /* Next */ + unsigned int dummy; /* Reserved (for 16 byte alignment) */ +}; + + + +/* End ibm405lp.h candidates */ + + +/*****************************************************************************/ +/* Driver specific defines */ +/*****************************************************************************/ + +/* The DMA channels for the CSI are hardcoded in the 405LP chip, so we hardcode them. */ +/* If a future chip adopts programmable channel assignment, I expect access to DMA */ +/* channels would be handled by a separate driver. */ + +#define IBMCSI_TXDMA 0 /* Transmit from CSI to Si3000 : channel 0 */ +#define IBMCSI_RXDMA 1 /* Receive from Si3000 to CSI : channel 1 */ + +#define IBMCSI_TXDMA_IRQ 5 +#define IBMCSI_RXDMA_IRQ 6 + +#define IBMCSI_DMA_SR DCRN_DMASR + +/* Transmit (playback) DMA registers */ +#define IBMCSI_TXDMA_CR DCRN_DMACR0 +#define IBMCSI_TXDMA_DA DCRN_DMADA0 +#define IBMCSI_TXDMA_SA DCRN_DMASA0 +#define IBMCSI_TXDMA_CT DCRN_DMACT0 + +/* Receive (capture) DMA registers */ +#define IBMCSI_RXDMA_CR DCRN_DMACR1 +#define IBMCSI_RXDMA_DA DCRN_DMADA1 +#define IBMCSI_RXDMA_SA DCRN_DMASA1 +#define IBMCSI_RXDMA_CT DCRN_DMACT1 + + +#define IBMCSI_TXDMA_CONFIG ( /* Channel disabled */ \ + DCRN_DMA_CR_CIE | /* Channel interrupt enabled */ \ + /* Memory to peripheral */ \ + DCRN_DMA_CR_PL | /* Peripheral on OPB */ \ + DCRN_DMA_CR_PW_32 | /* 32 bit wide peripheral */ \ + /* Dest address not incremented */ \ + DCRN_DMA_CR_SAI | /* Source address incremented */ \ + /* Peripheral transfer mode */ \ + /* Peripheral setup cycle 0 */ \ + DCRN_DMA_CR_PWC(2) | /* Peripheral wait cycle 3 */ \ + /* Peripheral hold cycle 0 */ \ + DCRN_DMA_CR_ETD | /* EOTn = TC */ \ + DCRN_DMA_CR_TCE ) /* Terminal count enable */ + +#define IBMCSI_TXDMA_GO ( IBMCSI_TXDMA_CONFIG | DCRN_DMA_CR_CE ) /* For int */ +#define IBMCSI_TXDMA_GO_NOI ( IBMCSI_TXDMA_GO & ~DCRN_DMA_CR_CIE ) /* For polling */ + +#define IBMCSI_RXDMA_CONFIG ( /* Channel disabled */ \ + DCRN_DMA_CR_CIE | /* Channel interrupt enabled */ \ + DCRN_DMA_CR_TD | /* Peripheral to memory */ \ + DCRN_DMA_CR_PL | /* Peripheral on OPB */ \ + DCRN_DMA_CR_PW_32 | /* 32 bit wide peripheral */ \ + DCRN_DMA_CR_DAI | /* Dest address incremented */ \ + /* Source address not incremented */ \ + /* Peripheral transfer mode */ \ + /* Peripheral setup cycle 0 */ \ + DCRN_DMA_CR_PWC(2) | /* Peripheral wait cycle 3 */ \ + /* Peripheral hold cycle 0 */ \ + DCRN_DMA_CR_ETD | /* EOTn = TC */ \ + DCRN_DMA_CR_TCE ) /* Terminal count enable */ + +#define IBMCSI_RXDMA_GO (IBMCSI_RXDMA_CONFIG | DCRN_DMA_CR_CE ) +#define IBMCSI_RXDMA_GO_NOI (IBMCSI_RXDMA_GO & ~DCRN_DMA_CR_CIE ) + +#define IBMCSI_DEFAULT_SAMPLING_RATE 11025 + +/* Scatter/Gather related */ +#define USE_SG + +#define DAC_TIMER_PERIOD (HZ/50) +#define ADC_TIMER_PERIOD (HZ/50) + +#define TX_SG DCRN_ASG0 +#define RX_SG DCRN_ASG1 + +#define TX_SG_ENABLE DCRN_DMA_SGC_SSG0 +#define TX_SG_MASK DCRN_DMA_SGC_EM0 + +#define RX_SG_ENABLE DCRN_DMA_SGC_SSG1 +#define RX_SG_MASK DCRN_DMA_SGC_EM1 + +/*****************************************************************************/ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS +#define DBG(x) {} +/*#define DBG(x) {x}*/ + +#define IBMCSI3K_MAGIC 0xB31BCB /* Copied from the Austin Research version */ +/* TODO: verify this value is legitimate */ + +#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) /* TODO: check out this value */ +#define DMABUF_MINORDER 1 /* TODO: ditto */ + +#define IBMCSI_WRITE(reg, val) (__raw_writel(val, reg)) +#define IBMCSI_READ(reg) (__raw_readl(reg)) + +/* Enable the following if using consistent_alloc() for DMA buffer */ +/* #define USECONSISTENTALLOC */ + +/* Enable the following to debug FSYNC timing. This will toggle touch panel sense lines. */ +/* #define TIMINGDEBUGPROBE */ + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != IBMCSI3K_MAGIC) { \ + printk(KERN_ERR "ibmcsi3k: invalid signature (magic) in private data\n"); \ + return -ENXIO; \ + } \ +}) + + +/*****************************************************************************/ +/* Static variables, globals and structs */ +/*****************************************************************************/ + +static const unsigned sample_shift[] = { 0, 1, 1, 2 }; +static LIST_HEAD(devs); + +/* -------------------------------------------------------------------- */ +/* Private data structure for the devices supported by this driver */ +/* -------------------------------------------------------------------- */ + +struct ibmcsi3k_state { + unsigned int magic; /* Magic signature value for sanity check */ + + unsigned int state; /* Driver state (DAC/ADC running, Halt, etc.) */ + + + /* DSP device variables */ + struct list_head devs; /* For multi-device support; not used for the 405LP */ + int dev_dsp; /* Return value from register_sound_dsp */ + /* We are a DSP device because we do not support mu-law encoding */ + spinlock_t lock; + + struct semaphore dsp_sem; + struct semaphore open_sem; + + mode_t open_mode; + wait_queue_head_t open_wait; + + /* Mixer device variables */ + /* Generic */ + int dev_mixer; /* Return value from register_sound_mixer */ + struct semaphore mix_sem; + + + unsigned int requested_sample_rate; /* TODO: variable sampling rate to be implemented */ + unsigned int actual_sample_rate; + + unsigned int modcnt; + + /* We hardcode the hardware resource assignments in the defines: */ + /* DMA channels, DMA IRQs, CSI address, CSI IRQ. */ + /* If this driver is ported to a CPU that supports dynamic assignment */ + /* these would go here. */ + + /* Buffers */ + unsigned char *write_line; /* Pointer to a small buffer for register writes */ + dma_addr_t dma_write_line; + unsigned char *read_line; /* Pointer to a small buffer for register reads */ + dma_addr_t dma_read_line; + + unsigned int timeout; /* Timeout flag for Si3000 register access */ + + struct dmabuf { /* Control blocks for audio playback (dac) and capture (adc) */ + /* The important ones... */ + void *rawbuf; /* DMA buffer logical address */ + dma_addr_t dmaaddr; /* DMA buffer physical address */ + unsigned hwptr, swptr; /* Offsets from rawbuf for data. HWPTR = DMAC, SWPTR = driver */ + int count; + + wait_queue_head_t wait; + + /* And the rest, inherited from sample drivers... */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + + unsigned total_bytes; + unsigned error; /* Over/underrun */ +#if defined(USE_SG) + unsigned sg_count; +#endif + + } dma_dac, dma_adc; + + unsigned int sync_interval ; /* FSYNC synchronization interval */ + +#if defined(USE_SG) + struct timer_list dac_timer; + struct timer_list adc_timer; + struct dma_sgdt *dac_free_sgdt_q; + struct dma_sgdt *adc_free_sgdt_q; + struct dma_sgdt *dac_active_sgdt_q; + struct dma_sgdt *adc_active_sgdt_q; + + struct dma_sgdt *dac_sgdt_lastV; /* Anchors */ + struct dma_sgdt *adc_sgdt_lastV; + + struct dma_sgdt *adc_prev_sgdt; +#endif + +}; + +/* Driver state flags */ +#define IBMCSI_DEFAULT_STATE 0x00000000 +#define IBMCSI_DAC_RUNNING 0x00010000 +#define IBMCSI_ADC_RUNNING 0x00020000 +#define IBMCSI_HALT 0x00040000 + + +/*****************************************************************************/ +/* Function Prototypes */ +/* */ +/* Quote: "Warning: function declaration is not prototype" (gcc 2.95.3) */ +/*****************************************************************************/ + + +/*****************************************************************************/ +/* Driver function prototypes */ +/*****************************************************************************/ + +static int __init ibmcsi3k_setup(char *str); /* Kernel driver setup */ +static int __init init_ibmcsi3k(void); /* Driver initialization */ +static void __exit cleanup_ibmcsi3k(void); /* Driver exit cleanup */ + +/*****************************************************************************/ +/* DSP driver function prototypes */ +/*****************************************************************************/ + +/* Top level */ +static int ibmcsi3k_open(struct inode *inode, struct file *file); +static ssize_t ibmcsi3k_read(struct file *file, char *buffer, size_t count, loff_t *ppos); +static ssize_t ibmcsi3k_write(struct file *file, const char *buffer, size_t count, loff_t *ppos); +static unsigned int ibmcsi3k_poll(struct file *file, struct poll_table_struct *wait); +static int ibmcsi3k_release(struct inode *inode, struct file *file); +static int ibmcsi3k_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); + +/* Interrupt handlers */ +static void ibmcsi3k_dac_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void ibmcsi3k_adc_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void ibmcsi_adc_timer(unsigned long param); +static void ibmcsi_dac_timer(unsigned long param); + +/* Utility routines */ +static unsigned long copy_reformat_to_user(char *pDest, const char *pSrc, size_t count); +static unsigned long copy_reformat_from_user(char *pDest, const char *pSrc, size_t count); + +static void start_adc(struct ibmcsi3k_state *s); +static void start_dac(struct ibmcsi3k_state *s); +static int drain_dac(struct ibmcsi3k_state *s, int nonblock); +static inline void stop_adc(struct ibmcsi3k_state *s); +static inline void stop_dac(struct ibmcsi3k_state *s); + +static inline void dealloc_dmabuf(struct ibmcsi3k_state *s, struct dmabuf *buf); +static int prog_dmabuf(struct ibmcsi3k_state *s, struct dmabuf *buf, unsigned rate, unsigned fmt); +static inline int prog_dmabuf_adc(struct ibmcsi3k_state *s); +static inline int prog_dmabuf_dac(struct ibmcsi3k_state *s); + +static inline unsigned get_hwptr(struct ibmcsi3k_state *s, struct dmabuf *buf, unsigned channel); +static void ibmcsi3k_update_ptr(struct ibmcsi3k_state *s); + +static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c); + +static inline unsigned ld2(unsigned int x); + +/* Scatter/Gather descriptor table maintenance routines */ + +static void init_sgdt_q(struct dma_sgdt *queue, int count); +static struct dma_sgdt *get_sgdt(struct dma_sgdt **queueaddress); +static void free_sgdt(struct dma_sgdt **queueaddress, struct dma_sgdt *dt); +static unsigned int check_sgdt_range(struct ibmcsi3k_state *s, struct dma_sgdt* dt, int count); + +/*****************************************************************************/ +/* Mixer driver function prototypes */ +/*****************************************************************************/ + +/* Top level */ +static int ibmcsi3k_open_mixdev(struct inode *inode, struct file *file); +static int ibmcsi3k_ioctl_mixdev(struct inode *ioctl, struct file *file, unsigned int cmd, unsigned long arg); +static int ibmcsi3k_release_mixdev(struct inode *inode, struct file *file); + +static int mixer_read_ioctl(struct ibmcsi3k_state *s, unsigned int nr, caddr_t arg); +static int mixer_write_ioctl(struct ibmcsi3k_state *s, unsigned int nr, caddr_t arg); + +/* Mixer read routines */ +static int ibmcsi3k_get_volume(struct ibmcsi3k_state *s) ; +static int ibmcsi3k_get_line(struct ibmcsi3k_state *s) ; +static int ibmcsi3k_get_mic(struct ibmcsi3k_state *s) ; +static int ibmcsi3k_get_speaker(struct ibmcsi3k_state *s) ; +static int ibmcsi3k_get_txpga(struct ibmcsi3k_state *s) ; +static int ibmcsi3k_get_rxpga(struct ibmcsi3k_state *s) ; +static int ibmcsi3k_get_recsrc(struct ibmcsi3k_state *s) ; +static int ibmcsi3k_get_outsrc(struct ibmcsi3k_state *s) ; + +/* Mixer write routines */ +static int ibmcsi3k_set_volume(struct ibmcsi3k_state *s, int val); +static int ibmcsi3k_set_line(struct ibmcsi3k_state *s, int val) ; +static int ibmcsi3k_set_mic(struct ibmcsi3k_state *s, int val) ; +static int ibmcsi3k_set_speaker(struct ibmcsi3k_state *s, int val) ; +static int ibmcsi3k_set_txpga(struct ibmcsi3k_state *s, int val) ; +static int ibmcsi3k_set_rxpga(struct ibmcsi3k_state *s, int val) ; +static int ibmcsi3k_set_recsrc(struct ibmcsi3k_state *s, int val) ; +static int ibmcsi3k_set_outsrc(struct ibmcsi3k_state *s, int val) ; + +/* Si3000 control register access routines */ +static unsigned char si3000_read_reg( struct ibmcsi3k_state *s, int reg ); +static unsigned char si3000_read_reg2( struct ibmcsi3k_state *s, int reg, int retrycount); + +static void si3000_write_reg2( struct ibmcsi3k_state *s, int reg, unsigned char val, int retrycount) ; +static void si3000_write_reg( struct ibmcsi3k_state *s, int reg, unsigned char val) ; + +static unsigned char si3000_transfer( struct ibmcsi3k_state *s, + int reg, int read , unsigned char val , unsigned int sync_value); + + +static int si3000_set_sampling_rate(struct ibmcsi3k_state *s, int val); +static int si3000_get_sampling_rate(struct ibmcsi3k_state *s); + +static void ibmcsi3k_synchronize(struct ibmcsi3k_state *s); + +/*****************************************************************************/ + +/* maximum number of devices; only used for command line params */ +#define NR_DEVICE 1 + +static int micbias[NR_DEVICE] = { 0, }; + +static unsigned int devindex = 0; + +MODULE_PARM(micbias, "1-" __MODULE_STRING(NR_DEVICE) "i"); +MODULE_PARM_DESC(micbias, "sets the +5V bias for an electret microphone"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Benjamin Li, Bishop Brock, Ken Inoue"); +MODULE_DESCRIPTION("IBM PPC 405LP CSI / SI Si3000 Audio Driver"); + + +/*****************************************************************************/ +/*****************************************************************************/ +/* Code Section */ +/*****************************************************************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Module Initialization */ +/*****************************************************************************/ + +#ifndef MODULE + +/* Currently no module parameters are supported. The following is left as template. */ + +static int __init ibmcsi3k_setup(char *str) +{ + static unsigned __initdata nr_dev = 0; + + if (nr_dev >= NR_DEVICE) + return 0; + + (void) + ( + get_option(&str,&micbias [nr_dev]) + ); + + nr_dev++; + return 1; +} + +__setup("ibmcsi3k=", ibmcsi3k_setup); + +#endif /* MODULE */ + +/*****************************************************************************/ +/* Initialization tables */ +/*****************************************************************************/ + +static /*const*/ struct file_operations ibmcsi3k_audio_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + read: ibmcsi3k_read, + write: ibmcsi3k_write, + poll: ibmcsi3k_poll, + ioctl: ibmcsi3k_ioctl, + mmap: NULL, + open: ibmcsi3k_open, + release: ibmcsi3k_release, +}; + +static /*const*/ struct file_operations ibmcsi3k_mixer_fops = { + owner: THIS_MODULE, + llseek: no_llseek, + ioctl: ibmcsi3k_ioctl_mixdev, + open: ibmcsi3k_open_mixdev, + release: ibmcsi3k_release_mixdev, +}; + + +typedef int (*PIF) (struct ibmcsi3k_state *, int); + +static struct inittable { + PIF proc; + int val; +} inittable[] __initdata = { + { ibmcsi3k_set_recsrc, SOUND_MASK_LINE|SOUND_MASK_MIC } , + { ibmcsi3k_set_volume, 100 }, + { ibmcsi3k_set_line, 100 }, + { ibmcsi3k_set_speaker, 100 }, + { ibmcsi3k_set_mic, 100 }, + { ibmcsi3k_set_rxpga, 100 }, + { ibmcsi3k_set_txpga, 75 } +}; + +/*****************************************************************************/ +/* Driver Initialization */ +/*****************************************************************************/ +static int __init init_ibmcsi3k(void) +{ + struct ibmcsi3k_state *s; + mm_segment_t fs; + int i, ret; +#if 0 + unsigned long end_time; +#endif + + static unsigned long si3000_lines_virt = 0; + static dma_addr_t si3000_lines = (dma_addr_t)NULL; + + printk(KERN_INFO "ibmcsi3k: version v0.01 compiled at " __TIME__ " " __DATE__ "\n"); + +#ifdef CONFIG_PM + /* ---- Power up the CSI core during initialization ---- */ + ppc4xx_cpm_fr(CPM_CSI, 0); +#endif + + /* ---- Allocate line buffers ---- */ + if (!(si3000_lines_virt = (unsigned long)consistent_alloc(GFP_ATOMIC | GFP_DMA, PAGE_SIZE, &(si3000_lines) ))) { + printk(KERN_ERR "ibmcsi3k: out of memory\n"); + return -ENOMEM; + } + mem_map_reserve(virt_to_page(__va(si3000_lines))); + + /* ---- Allocate and initialize private data ---- */ + if (!(s = kmalloc(sizeof(struct ibmcsi3k_state), GFP_KERNEL))) { + printk(KERN_WARNING "ibmcsi3k: out of memory\n"); + return -ENOMEM; + } + memset(s, 0, sizeof(struct ibmcsi3k_state)); + + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + s->magic = IBMCSI3K_MAGIC; + + s->write_line = (void *)si3000_lines_virt; + s->dma_write_line = si3000_lines; + s->read_line = (void *)si3000_lines_virt + 128; + s->dma_read_line = si3000_lines + 128; + + if (!request_region(CSI0_IO_BASE, CSI0_EXTENT, "ibmcsi3k")) { + printk(KERN_ERR "ibmcsi3k: I/O ports %x-%x in use\n", CSI0_IO_BASE, CSI0_EXTENT-1); + ret = -EBUSY; + goto err_region; + } + +#ifdef TIMINGDEBUGPROBE /* Debug probe */ + request_region(0xef600a00, 16, "ibmcsi3k"); +#endif + + if ( ( IBMCSI_READ(CSI0_ID) >> 16 ) != CSI_ID_405LP) { + printk(KERN_WARNING "ibmcsi3k: Unexpected CSI ID %x\n", IBMCSI_READ(CSI0_ID)); + goto err_irq1; + } + else { + printk(KERN_INFO "ibmcsi3k: Found chip \n"); + } + + /* Interrupts - currently held indefinitely since no external device will be using them */ + /* on the 405LP evaluation board. */ + /* Mixer and DSP use the same interrupts. */ + + if ((ret=request_irq(IBMCSI_TXDMA_IRQ, ibmcsi3k_dac_interrupt, SA_SHIRQ, "ibmcsi3k",s))) { + printk(KERN_ERR "ibmcsi3k: IRQ %d in use\n", IBMCSI_TXDMA_IRQ); + goto err_irq1; + } + if ((ret=request_irq(IBMCSI_RXDMA_IRQ, ibmcsi3k_adc_interrupt, SA_SHIRQ, "ibmcsi3k",s))) { + printk(KERN_ERR "ibmcsi3k: IRQ %d in use\n",IBMCSI_RXDMA_IRQ); + goto err_irq2; + } + +#if 0 /* TODO: CSI error interrupts not implemented */ + if ((ret=request_irq(21, ibmcsi3k_csi_interrupt, SA_SHIRQ, "ibmcsi3k",s))) { + printk(KERN_ERR "ibmcsi3k: irq 21 in use\n"); + goto err_irq; + } +#endif + + /* register devices */ + if ((s->dev_dsp = register_sound_dsp(&ibmcsi3k_audio_fops, -1)) < 0) { + ret = s->dev_dsp; + goto err_dev1; + } + if ((s->dev_mixer = register_sound_mixer(&ibmcsi3k_mixer_fops, -1)) < 0) { + ret = s->dev_mixer; + goto err_dev2; + } + + fs = get_fs(); + set_fs(KERNEL_DS); + + /* Initialize the chips: DMA controller, CSI and codec. */ + + /* Set the Si3000 PLL to 11.025 KHz - the values depend on master clock */ + /* TODO: sampling rate is hardcoded for now */ + + if (!si3000_set_sampling_rate(s, IBMCSI_DEFAULT_SAMPLING_RATE)) { + printk(KERN_ERR "ibmcsi3k: cannot set default sampling rate\n"); + goto err_dev3; + } + + /* Initialize mixer settings (see inittable[] above for procedures and values. */ + for (i=0; idevs, &devs); + /* Increment device index */ + if (devindex < NR_DEVICE-1) + devindex++; + +#ifdef CONFIG_PM + /* ---- Power down the CSI core until an open() request ---- */ + /*ppc4xx_cpm_fr(CPM_CSI, 1); Bug?*/ +#endif + + return 0; + + err_dev3: /* Error exit initializing chips */ + unregister_sound_mixer(s->dev_mixer); + err_dev2: /* Error exit registering mixer device */ + unregister_sound_dsp(s->dev_dsp); + err_dev1: + free_irq(5, s); + err_irq2: + free_irq(6, s); +#if 0 /* TODO */ + free_irq(21,s); +#endif + err_irq1: + release_region(CSI0_IO_BASE, CSI0_EXTENT); + err_region: + kfree(s); +#ifdef CONFIG_PM + /*ppc4xx_cpm_fr(CPM_CSI, 1); Bug?*/ +#endif + return ret; + +} + +/*****************************************************************************/ +/* Driver Cleanup */ +/*****************************************************************************/ +static void __exit cleanup_ibmcsi3k(void) +{ + printk(KERN_INFO "ibmcsi3k: unloading\n"); +} + +module_init(init_ibmcsi3k); +module_exit(cleanup_ibmcsi3k); + + +/*****************************************************************************/ +/*****************************************************************************/ +/* Part I : DSP device /dev/dsp */ +/*****************************************************************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/* /dev/dsp Open */ +/*****************************************************************************/ +static int ibmcsi3k_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct ibmcsi3k_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct ibmcsi3k_state, devs); + if (!((s->dev_dsp ^ minor) & ~0xf)) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & (file->f_mode & (FMODE_READ | FMODE_WRITE))) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + __set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + s->open_mode |= (file->f_mode & (FMODE_READ | FMODE_WRITE)); + spin_lock_irqsave(&s->lock, flags); + +#ifdef CONFIG_PM + /* ppc4xx_cpm_fr(CPM_CSI, 0); Bug? Power up the CSI core */ +#endif + + /* TODO: sampling rate hardcoded */ +#ifdef USE_SG /* Use Scatter/Gather DMA */ + { + struct dma_sgdt *dacp, *adcp; + +#define SGDT_COUNT ((PAGE_SIZE/2)/(sizeof(struct dma_sgdt))) + DBG(printk("ibmcsi: %d SGDTs\n", (int)SGDT_COUNT)); + + s->dac_free_sgdt_q = (struct dma_sgdt *)(s->write_line); + s->adc_free_sgdt_q = (struct dma_sgdt *)(s->write_line) + SGDT_COUNT; + + init_sgdt_q(s->dac_free_sgdt_q, SGDT_COUNT); + init_sgdt_q(s->adc_free_sgdt_q, SGDT_COUNT); + + /* Prepare default (anchor) descriptor for DAC */ + dacp = s->dac_active_sgdt_q = get_sgdt(&(s->dac_free_sgdt_q)); + + dacp->ccw = (unsigned int) (IBMCSI_TXDMA_GO_NOI) ; + + dacp->nextP = (void *) virt_to_phys(dacp); /* Loop in on itself */ + + dacp->srcP = (void *) virt_to_phys(&(dacp->dummy)); /* Point to dummy data */ + dacp->destP = (void *)CSI0_TBUF; + dacp->dummy = 0; /* Dummy neutral data (FIXME: currently assumes signed 16 format) */ + dacp->ctrl = 0x80000001; /* Link, count = 1 */ + + s->dac_sgdt_lastV = dacp; + + adcp = s->adc_active_sgdt_q = get_sgdt(&(s->adc_free_sgdt_q)); + adcp->ccw = (unsigned int) (IBMCSI_RXDMA_GO_NOI) ; + adcp->nextP = (void *) virt_to_phys(adcp); + adcp->ctrl = 0x80000001; + + adcp->srcP = (void *)CSI0_RBUF; + adcp->destP = (void *)virt_to_phys(&(adcp->dummy)); + + s->adc_sgdt_lastV = adcp; + + DBG(printk("ibmcsi: s = %8.8x free_sgdt_q = %8.8x at %8.8x lastV = %8.8x at %8.8x\n", s, s->dac_free_sgdt_q ,&s->dac_free_sgdt_q,s->dac_sgdt_lastV,&s->dac_sgdt_lastV)); + + } /* TODO: split the above into read and write */ +#endif /* USE_SG */ + + + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + /* TODO: data format hardcoded */ + + IBMCSI_WRITE(CSI0_ER, 0); /* Disable CSI */ + IBMCSI_WRITE(CSI0_SR, CSI_SR_TOD | CSI_SR_ROD); /* Clear CSI errors */ + mtdcr(IBMCSI_TXDMA_CR, 0); /* Disable DMA Tx */ + mtdcr(IBMCSI_RXDMA_CR, 0); /* Disable DMA Rx */ + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_TXDMA) | DCRN_DMA_SR_ALL(IBMCSI_RXDMA) ) ; + /* Clear DMA status bits */ + } + + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags = s->dma_dac.subdivision = 0; + s->dma_dac.enabled = 1; + + /* TODO: data format hardcoded */ + + s->dma_dac.hwptr = 0; + s->dma_dac.swptr = 0; + s->dma_dac.count = 0; +#ifdef USE_SG + s->dma_dac.sg_count = 0; + init_timer(&s->dac_timer); + s->dac_timer.function = ibmcsi_dac_timer; + s->dac_timer.data = (unsigned long)s; + s->dac_timer.expires = jiffies + DAC_TIMER_PERIOD; + add_timer(&s->dac_timer); +#endif + } + + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + init_MUTEX(&s->dsp_sem); + return 0; +} + +/*****************************************************************************/ +/* /dev/dsp Read */ +/*****************************************************************************/ +static ssize_t ibmcsi3k_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct ibmcsi3k_state *s = (struct ibmcsi3k_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + down(&s->dsp_sem); + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + goto out; + + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + /* Spinlock on */ + spin_lock_irqsave(&s->lock, flags); + + swptr = s->dma_adc.swptr; + if (s->dma_adc.hwptr >= s->dma_adc.swptr) { + cnt = s->dma_adc.hwptr - swptr; + } + else { /* HWPTR wrapped */ + cnt = s->dma_adc.dmasize - swptr; + } + + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + + spin_unlock_irqrestore(&s->lock, flags); + /* Spinlock off */ + + if (cnt > count*2 ) /* cnt is raw (4 bytes per sample), count is cooked (2 bytes per sample) */ + cnt = count * 2; + + if (cnt <= 0) { + if (s->dma_adc.enabled) + start_adc(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + up(&s->dsp_sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out; + } + down(&s->dsp_sem); + if (s->dma_adc.mapped) + { + ret = -ENXIO; + goto out; + } + continue; + } + + if (copy_reformat_to_user(buffer, s->dma_adc.rawbuf + swptr, cnt/4)) { + if (!ret) + ret = -EFAULT; + goto out; + } + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); + count -= cnt/2; + buffer += cnt/2; + ret += cnt/2; + if (s->dma_adc.enabled) + start_adc(s); + } +out: + up(&s->dsp_sem); + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + + +/*****************************************************************************/ +/* /dev/dsp Write */ +/*****************************************************************************/ +static ssize_t ibmcsi3k_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct ibmcsi3k_state *s = (struct ibmcsi3k_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + + if (!access_ok(VERIFY_READ, buffer, count)) { + return -EFAULT; + } + down(&s->dsp_sem); + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) { + printk("DEBUG: ibmcsi3k_write dac ready %d prog_dmabuf retcode %d \n", s->dma_dac.ready, ret); + goto out; + } + ret = 0; + add_wait_queue(&s->dma_dac.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + cnt = s->dma_dac.dmasize-swptr; /* cnt = available buffer */ + if (s->dma_dac.count + cnt > s->dma_dac.dmasize) /* Truncate to fit in dma buffer */ + cnt = s->dma_dac.dmasize - s->dma_dac.count; + + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + + /* TODO: format assumed 16 bit for now */ + if (cnt > (count * 2)) + cnt = count * 2; + + if (cnt <= 0) { + if (s->dma_dac.enabled) + start_dac(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + up(&s->dsp_sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out; + } + down(&s->dsp_sem); + if (s->dma_dac.mapped) + { + ret = -ENXIO; + goto out; + } + continue; + } + + if (copy_reformat_from_user(s->dma_dac.rawbuf + swptr, buffer, cnt/4)) { /* cnt/4 = # of samples */ + printk("DEBUG: copy_reformat_from_user failed\n"); + if (!ret) + ret = -EFAULT; + goto out; + } + +#ifndef USECONSISTENTALLOC + dma_cache_wback_inv((unsigned long)(s->dma_dac.rawbuf + swptr), cnt); +#endif + + swptr = (swptr + cnt) % s->dma_dac.dmasize; /* Wrap */ + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += cnt; + s->dma_dac.endcleared = 0; /* Remember to zero clear one fragment at the end */ + +#ifdef USE_SG /* Scatter/Gather mode */ + s->dma_dac.sg_count += cnt; +#endif + + spin_unlock_irqrestore(&s->lock, flags); +/* TODO: broken if odd byte written. Also, format is assumed 16 bit */ + count -= cnt / 2; + buffer += cnt / 2; + ret += cnt / 2; + + if (s->dma_dac.enabled) + start_dac(s); + + } +out: + up(&s->dsp_sem); + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +/*****************************************************************************/ +/* /dev/dsp Poll */ +/*****************************************************************************/ +static unsigned int ibmcsi3k_poll(struct file *file, struct poll_table_struct *wait) +{ + struct ibmcsi3k_state *s = (struct ibmcsi3k_state *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + ibmcsi3k_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + else { + if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +/*****************************************************************************/ +/* /dev/dsp IOCTL */ +/*****************************************************************************/ +static int ibmcsi3k_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct ibmcsi3k_state *s = (struct ibmcsi3k_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, mapped, ret; + + VALIDATE_STATE(s); + mapped = ((file->f_mode & FMODE_WRITE) && s->dma_dac.mapped) || + ((file->f_mode & FMODE_READ) && s->dma_adc.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val >= 0) { + /* Check if we are open */ + if (s->state & (~file->f_mode) & (FMODE_READ|FMODE_WRITE)) + return -EINVAL; + /* Si3000 supports sampling rates 4KHz thru 12KHz. */ + if (val < 4000) val = 4000; + if (val > 12000) val = 120000; + + stop_adc(s); + stop_dac(s); + s->dma_adc.ready = s->dma_dac.ready = 0; + spin_lock_irqsave(&s->lock, flags); + /* TODO: Reprogram Si3000 PLL here, if you dare!! */ + spin_unlock_irqrestore(&s->lock, flags); + } + + return put_user(IBMCSI_DEFAULT_SAMPLING_RATE, (int *)arg); /* Hardcoded for now */ + + case SNDCTL_DSP_STEREO: + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + return put_user(1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_S16_BE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + return put_user(AFMT_S16_BE, (int *)arg); + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if ((file->f_mode) & FMODE_READ && (s->state & IBMCSI_ADC_RUNNING)) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && (s->state & IBMCSI_DAC_RUNNING)) + 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) { + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + s->dma_adc.enabled = 1; + start_adc(s); + } + else { + s->dma_adc.enabled = 0; + stop_adc(s); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) + return ret; + s->dma_dac.enabled = 1; + start_dac(s); + } + else { + s->dma_dac.enabled = 0; + stop_dac(s); + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ibmcsi3k_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + count = s->dma_dac.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac.dmasize - count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ibmcsi3k_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + count = s->dma_adc.count; + if (count < 0) + count = 0; + abinfo.bytes = count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ibmcsi3k_update_ptr(s); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ibmcsi3k_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ibmcsi3k_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac(s))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user(IBMCSI_DEFAULT_SAMPLING_RATE, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + return put_user(2, (int *)arg); + + case SOUND_PCM_READ_BITS: + return put_user(16,(int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + + } + return -EINVAL; +} + +/*****************************************************************************/ +/* /dev/dsp MMAP (Not supported) */ +/*****************************************************************************/ +#if 0 /* No mmap support */ +static int ibmcsi3k_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ibmcsi3k_state *s = (struct ibmcsi3k_state *)file->private_data; + struct dmabuf *db; + int ret = 0; + unsigned long size; + + VALIDATE_STATE(s); + printk("DEBUG: ibmcsi3k_mmap\n"); + lock_kernel(); + down(&s->dsp_sem); + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_dac(s)) != 0) { + goto out; + } + db = &s->dma_dac; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_adc(s)) != 0) { + goto out; + } + db = &s->dma_adc; + } else { + ret = -EINVAL; + goto out; + } + if (vma->vm_pgoff != 0) { + ret = -EINVAL; + goto out; + } + size = vma->vm_end - vma->vm_start; + if (size > (PAGE_SIZE << db->buforder)) { + ret = -EINVAL; + goto out; + } + if (remap_page_range(vma->vm_start, virt_to_phys(db->rawbuf), size, vma->vm_page_prot)) { + ret = -EAGAIN; + goto out; + } + db->mapped = 1; +out: + up(&s->dsp_sem); + unlock_kernel(); + return ret; +} +#endif + + +/*****************************************************************************/ +/* /dev/dsp Release */ +/*****************************************************************************/ +static int ibmcsi3k_release(struct inode *inode, struct file *file) +{ + struct ibmcsi3k_state *s = (struct ibmcsi3k_state *)file->private_data; + + VALIDATE_STATE(s); + lock_kernel(); + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); +#ifdef USE_SG + del_timer_sync(&s->dac_timer); +#endif + synchronize_irq(); + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); +#ifdef USE_SG /* ADC to be implemented */ + /* del_timer_sync(&s->adc_timer); */ +#endif + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= ~(file->f_mode & (FMODE_READ|FMODE_WRITE)); +#ifdef CONFIG_PM + /* if (!s->open_mode) + ppc4xx_cpm_fr(CPM_CSI, 1); Bug? */ +#endif + wake_up(&s->open_wait); + up(&s->open_sem); + unlock_kernel(); + return 0; +} + + + +/*****************************************************************************/ +/*****************************************************************************/ +/* /dev/dsp suppport routines */ +/*****************************************************************************/ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Start DAC (Digital to Analog Conversion - Audio Playback) */ +/*****************************************************************************/ +static void start_dac(struct ibmcsi3k_state *s) +{ +#ifdef USE_SG /* Scatter/Gather mode DMA */ + unsigned long flags; + unsigned fragremain; + struct dma_sgdt *dt; + unsigned int count; + + DBG(printk("start_dac_sg state %8.8x count %d ready %d sgcnt %d hwptr %d\n", + s->state, s->dma_dac.count, s->dma_dac.ready, s->dma_dac.sg_count, s->dma_dac.hwptr)); + + spin_lock_irqsave(&s->lock, flags); /* Spinlock held from here to end */ + + if (s->dma_dac.sg_count) { /* There is data that needs to be posted */ + + if (s->dma_dac.hwptr >= s->dma_dac.dmasize) {/* Sanity check */ + s->dma_dac.hwptr = 0; + } + + count = s->dma_dac.sg_count; + if (s->dma_dac.hwptr + count > s->dma_dac.dmasize) { + count = s->dma_dac.dmasize - s->dma_dac.hwptr; + } + + if ( count > 65536*4 ) count = 65536*4 ; /* 4 bytes per sample, DMA count limited to 64K */ + + if ( count > 4 ) { /* Got enough data to do DMA */ + + dt = get_sgdt(&(s->dac_free_sgdt_q)); /* Get descriptor */ + + if (dt) { /* Descriptor available */ + DBG(printk("new dt %8.8x ",dt)); + + /* TODO: Break data into fragments. Easiest way for now! */ + + dt->ccw = (unsigned int) (IBMCSI_TXDMA_GO_NOI) ; + dt->ctrl = 0x80000000 | ((count/4)&0xffff); + dt->srcP = (void *) virt_to_phys(s->dma_dac.rawbuf + s->dma_dac.hwptr); + dt->destP = (void *) CSI0_TBUF; + dt->nextP = (struct dma_sgdt *) virt_to_phys(s->dac_sgdt_lastV); + + asm volatile ("sync"); + + DBG(printk("TX SG %8.8x, last %8.8x\n", mfdcr(TX_SG),s->dac_sgdt_lastV)); + DBG(printk("TX SA %8.8x, count %d \n", + mfdcr(IBMCSI_TXDMA_SA), mfdcr(IBMCSI_TXDMA_CT))); + DBG(printk("last->phys %8.8x\n",s->dac_sgdt_lastV->srcP)); + + if ( mfdcr(DCRN_ASGC) & TX_SG_ENABLE ) { + unsigned int current_sa = mfdcr(IBMCSI_TXDMA_SA); + if ( (struct dmag_sgdt *)current_sa == s->dac_sgdt_lastV->srcP ) { + s->dac_active_sgdt_q = dt; + stop_dac(s); + udelay(100); + } + else if ( mfdcr(IBMCSI_TXDMA_CT) <=2 ) { + int timeout=0; + /* No time to safely reprogram this pass */ + /* Wait until this pass completes. */ + while (current_sa == mfdcr(IBMCSI_TXDMA_SA)) { + timeout++; + if (timeout > 1000000) { + printk("ibmcsi: DMA timeout!!\n"); + break; + } + }; + + if ((struct dma_sgdt *)mfdcr(IBMCSI_TXDMA_SA) == + s->dac_sgdt_lastV->srcP) { + stop_dac(s); + udelay(100); + } + } + } + else { + s->dac_active_sgdt_q = dt; /* This gets programmed only if not active */ + } + + /* Insert new dt between last anchor and previous data */ + + dt->nextV = s->dac_sgdt_lastV; + dt->prevV = s->dac_sgdt_lastV->prevV; + + if (dt->prevV) { + dt->prevV->nextV = dt; + dt->prevV->nextP = (void *)virt_to_phys(dt); + } + + s->dac_sgdt_lastV->prevV = dt; + + if ( ( mfdcr(DCRN_ASGC) & TX_SG_ENABLE ) ) { + if ( mfdcr(TX_SG) == virt_to_phys(s->dac_sgdt_lastV) ) { + /* Next descriptor is the bottom anchor */ + + mtdcr(TX_SG, virt_to_phys(dt)); + /* Replace the next descriptor address with myself */ + /* Not sure if this works */ + } + } + + asm volatile ("sync"); + + /* Update count and pointer */ + s->dma_dac.sg_count -= count; + s->dma_dac.hwptr += count; + if (s->dma_dac.hwptr >= s->dma_dac.dmasize) { + s->dma_dac.hwptr = 0; + } + + } /* End if descriptor available */ + else { + DBG(printk("No dt!\n")); + } + + /* If DMA is not already running, kick it. */ + + if (!(mfdcr(DCRN_ASGC) & TX_SG_ENABLE )) { + DBG(printk("kick\n")); + + s->state |= IBMCSI_DAC_RUNNING; + + /* Clear Terminal Count etc. */ + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_TXDMA)); + + /* Clear CSI overrun / underrun */ + IBMCSI_WRITE(CSI0_SR, CSI_SR_ROD | CSI_SR_TOD); + + /* Write address of the first scatter/gather descriptor table */ + mtdcr(TX_SG, virt_to_phys(s->dac_active_sgdt_q)); + + /* Enable scatter/gather */ + mtdcr(DCRN_ASGC, mfdcr(DCRN_ASGC) | TX_SG_ENABLE | TX_SG_MASK) ; + + /* Set up CSI config */ + IBMCSI_WRITE(CSI0_CFG, 0); + +#if 0 /* DMATIMINGPROBE */ + __raw_writel(0x54a54000, 0xef600a00); /* Turn on touch panel X2 line */ +#endif + asm volatile ("sync"); /* Sync */ + + /* Start CSI, enable slot 0 and Tx. */ + IBMCSI_WRITE(CSI0_ER, CSI_ER_ESLOT(0) | CSI_ER_TXEN | CSI_ER_ECSI ); + + udelay(100); + DBG(printk("CSI SA %8.8x, CT %8.8x",mfdcr(IBMCSI_TXDMA_SA), mfdcr(IBMCSI_TXDMA_CT))); + } + else { +/* DBG(printk("no kick \n")); */ + } + + } /* End count not zero */ + } + + spin_unlock_irqrestore(&s->lock, flags); + +#else /* Normal mode */ + + unsigned long flags; + unsigned fragremain; + + spin_lock_irqsave(&s->lock, flags); + + if (!(s->state & IBMCSI_DAC_RUNNING) && (s->dma_dac.mapped || s->dma_dac.count > 0) + && s->dma_dac.ready) { + s->state |= IBMCSI_DAC_RUNNING; + + fragremain = s->dma_dac.count; + + if (fragremain > s->dma_dac.fragsize) fragremain = s->dma_dac.fragsize; + + /* Disable CSI */ + IBMCSI_WRITE(CSI0_ER, 0); + IBMCSI_WRITE(CSI0_CFG, 0); + + /* Disable Tx DMA channel first before modifying other bits in CR */ + mtdcr(IBMCSI_TXDMA_CR, 0); + + /* Clear Terminal Count etc. */ + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_TXDMA)); + + /* Clear CSI overrun / underrun */ + IBMCSI_WRITE(CSI0_SR, CSI_SR_ROD | CSI_SR_TOD); + + /* Prepare Tx DMA channel */ + mtdcr(IBMCSI_TXDMA_CR, IBMCSI_TXDMA_CONFIG); + + /* Set Tx DMA channel source address */ + mtdcr(IBMCSI_TXDMA_SA, (unsigned)(s->dma_dac.dmaaddr) + (unsigned)(s->dma_dac.hwptr) ); + + /* Set Tx DMA channel count */ + mtdcr(IBMCSI_TXDMA_CT, fragremain / 2); + + /* Set Tx DMA channel destination address */ + mtdcr(IBMCSI_TXDMA_DA, CSI0_TBUF); + + /* Reconfigure and enable Tx DMA channel */ + mtdcr(IBMCSI_TXDMA_CR, IBMCSI_TXDMA_GO); + + asm volatile ("sync"); /* Sync */ + + /* Start CSI, enable slot 0 and Tx. */ + IBMCSI_WRITE(CSI0_ER, CSI_ER_ESLOT(0) | CSI_ER_TXEN | CSI_ER_ECSI ); + } + + spin_unlock_irqrestore(&s->lock, flags); +#endif /* End normal mode DMA */ +} + +/*****************************************************************************/ +/* Start ADC (Analog to Digital Conversion - Audio Capture) */ +/*****************************************************************************/ +static void start_adc(struct ibmcsi3k_state *s) +{ + unsigned long flags; + unsigned fragremain; + + spin_lock_irqsave(&s->lock, flags); + + if (!(s->state & IBMCSI_ADC_RUNNING) && + (s->dma_adc.mapped || + s->dma_adc.count < (signed) (s->dma_adc.dmasize - 4*s->dma_adc.fragsize)) + /* Multiply by 4 since we do 32 bit transfer per count */ + && s->dma_adc.ready) { + s->state |= IBMCSI_ADC_RUNNING; + + fragremain = (s->dma_adc.dmasize - s->dma_adc.count)/4; + + if (fragremain > s->dma_adc.fragsize) fragremain = s->dma_adc.fragsize; + + /* Disable CSI, then disable Rx DMA channel (must be in this order.) */ + IBMCSI_WRITE(CSI0_ER, 0); + mtdcr(IBMCSI_RXDMA_CR, 0); + + /* Clear Rx channel status flags (Terminal Count etc.) if any */ + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_RXDMA)); + + /* Clear CSI overrun / underrun errors if any */ + IBMCSI_WRITE(CSI0_SR, CSI_SR_ROD | CSI_SR_TOD); + + + /* Reconfigure Rx DMA channel */ + mtdcr(IBMCSI_RXDMA_CR, IBMCSI_RXDMA_CONFIG); + + /* Set Rx DMA channel DESTINATION address (to memory) */ + mtdcr(IBMCSI_RXDMA_DA, (unsigned)(s->dma_adc.dmaaddr) + (unsigned)(s->dma_adc.hwptr) ); + + /* Set Rx DMA channel transfer count */ + mtdcr(IBMCSI_RXDMA_CT, fragremain); + + /* Set Rx DMA channel SOURCE address (from CSI Rx buffer) */ + mtdcr(IBMCSI_RXDMA_SA, CSI0_RBUF); + + /* Enable Rx DMA channel */ + mtdcr(IBMCSI_RXDMA_CR, IBMCSI_RXDMA_GO); + + asm volatile ("sync"); /* Sync all mtdcr instructions and MMIO accesses */ + + /* Configure CSI for 256 bits per frame */ + IBMCSI_WRITE(CSI0_CFG, 0); + + /* Start CSI */ + IBMCSI_WRITE(CSI0_ER, CSI_ER_ESLOT(0) | CSI_ER_RXEN | CSI_ER_ECSI ); + } + + spin_unlock_irqrestore(&s->lock, flags); +} + +/*****************************************************************************/ +/* Stop DAC */ +/*****************************************************************************/ +static inline void stop_dac(struct ibmcsi3k_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + + IBMCSI_WRITE(CSI0_ER,0); /* Stop CSI */ + IBMCSI_WRITE(CSI0_TBUF,0); /* Zero out tx buffer to prevent popping sound */ +#ifdef USE_SG /* Scatter/Gather */ + mtdcr(DCRN_ASGC,(mfdcr(DCRN_ASGC) | TX_SG_MASK) & ~TX_SG_ENABLE); +#endif + mtdcr(IBMCSI_TXDMA_CR, + mfdcr(IBMCSI_TXDMA_CR) & ~DCRN_DMA_CR_CE); /* Stop Tx DMA channel */ + s->state &= ~IBMCSI_DAC_RUNNING; + + spin_unlock_irqrestore(&s->lock, flags); + +} + +/*****************************************************************************/ +/* Stop ADC */ +/*****************************************************************************/ +static inline void stop_adc(struct ibmcsi3k_state *s) { + + unsigned long flags; + spin_lock_irqsave(&s->lock, flags); + + /* TODO: currently assumes only playback or capture at a time, no duplex. */ + IBMCSI_WRITE(CSI0_ER,0); /* Stop CSI0 */ + mtdcr(IBMCSI_RXDMA_CR, mfdcr(IBMCSI_RXDMA_CR) & ~DCRN_DMA_CR_CE); + s->state &= ~IBMCSI_ADC_RUNNING; + + spin_unlock_irqrestore(&s->lock, flags); +} + + +/*****************************************************************************/ +/* Allocate / initialize DMA buffer and related variables */ +/*****************************************************************************/ +static int prog_dmabuf(struct ibmcsi3k_state *s, struct dmabuf *db, unsigned rate, unsigned fmt) +{ + int order; + unsigned bytepersec; + unsigned bufs; + struct page *page, *pend; + + + db->hwptr = db->swptr = db->total_bytes = db->count = db->error = db->endcleared = 0; + + if (!db->rawbuf) { /* DMA buffer not allocated yet, go get it. */ + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; order >= DMABUF_MINORDER; order--) { + +#ifdef USECONSISTENTALLOC + if ((db->rawbuf = consistent_alloc(GFP_ATOMIC | GFP_DMA, order, &(db->dmaaddr)))) { + db->hwptr = 0; + break; + } +#else + if ((db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, order))) { + db->dmaaddr = (dma_addr_t)virt_to_phys(db->rawbuf); + break; + } +#endif + } + + if (!db->rawbuf) + return -ENOMEM; + + DBG(printk("buforder: %8.8x\n", order)); + + db->buforder = order; + /* now mark the pages as reserved; otherwise remap_page_range doesn't do what we want */ + pend = virt_to_page(__va(db->dmaaddr) + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(__va(db->dmaaddr)); page <= pend; page++) { + mem_map_reserve(page); + } + +#if defined(USE_SG) + { /* Program the count and address into all descriptors in the ring */ + struct dma_sgdt *pWork = s->adc_free_sgdt_q; /* Use all the queue for now */ + unsigned dma_count = ((long)PAGE_SIZE << db->buforder) / 4; + + s->adc_active_sgdt_q = pWork; + + while (pWork) { /* Sanity check */ + pWork->ccw = (unsigned int) (IBMCSI_RXDMA_GO_NOI) ; + pWork->srcP = (void *)CSI0_RBUF; + pWork->destP = (void *)db->dmaaddr; + pWork->ctrl = dma_count | 0x80000000; + + if (pWork->nextV == NULL) { + pWork->nextV = s->adc_active_sgdt_q; /* Create a ring */ + } + pWork->nextP = (void *) virt_to_phys(pWork->nextV); + + pWork = pWork->nextV; + if (pWork == s->adc_active_sgdt_q) break; + } + if (pWork == NULL) printk("ibmcsi3k: INTERNAL ERROR! Queue corrupted.\n"); + } +#endif + + } + + bytepersec = IBMCSI_DEFAULT_SAMPLING_RATE * 4; /* TODO: Hardcoded for 11.025 KHz */ + + bufs = PAGE_SIZE << db->buforder; + + + /* TODO: decypher the following code block (inherited from es1370.c) */ + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } + else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + /* End TODO */ + + /* TODO: Sample format hardcoded - assumes 0 is neutral sample. */ + memset(db->rawbuf, 0, db->dmasize); + + db->enabled = 1; + db->ready = 1; + return 0; +} + +/*****************************************************************************/ +/* Prepare DMA buffer for ADC */ +/*****************************************************************************/ +static inline int prog_dmabuf_adc(struct ibmcsi3k_state *s) +{ + stop_adc(s); + + /* TODO: Sampling rate hardcoded at 11.025 KHz. */ + return prog_dmabuf(s,&s->dma_adc,IBMCSI_DEFAULT_SAMPLING_RATE,0); +} + +/*****************************************************************************/ +/* Prepare DMA buffer for DAC */ +/*****************************************************************************/ +static inline int prog_dmabuf_dac(struct ibmcsi3k_state *s) +{ + stop_dac(s); + + /* TODO: Sampling rate hardcoded at 11.025 KHz. */ + return prog_dmabuf(s, &s->dma_dac, IBMCSI_DEFAULT_SAMPLING_RATE, 0); +} + +/*****************************************************************************/ +/* Deallocate DMA buffer */ +/*****************************************************************************/ +static inline void dealloc_dmabuf(struct ibmcsi3k_state *s, struct dmabuf *db) +{ + struct page *page, *pend; + + if (db->rawbuf) { + /* undo marking the pages as reserved */ + pend = virt_to_page(__va(db->dmaaddr) + (PAGE_SIZE << db->buforder) - 1); + for (page = virt_to_page(__va(db->dmaaddr)); page <= pend; page++) + mem_map_unreserve(page); + +#ifdef USECONSISTENTALLOC + consistent_free((void *)db->rawbuf, db->buforder); +#else + free_pages((unsigned long)db->rawbuf, db->buforder); +#endif + } + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + + + +/*****************************************************************************/ +/* Procedure get_hwptr: Update DMA controller address offset hwptr */ +/* (returns # of bytes transferred) */ +/*****************************************************************************/ +static inline unsigned get_hwptr(struct ibmcsi3k_state *s, struct dmabuf *db, unsigned channel) +{ + unsigned hwptr, diff; + switch (channel) { + case IBMCSI_TXDMA: /* Tx DMA channel */ + hwptr = (unsigned)mfdcr(IBMCSI_TXDMA_SA) - (unsigned)(db->dmaaddr); + /* Get source offset for next transfer on Tx DMA channel */ + break; + case IBMCSI_RXDMA: /* Rx DMA channel */ + hwptr = (unsigned)mfdcr(IBMCSI_RXDMA_DA) - (unsigned)(db->dmaaddr); + break; + default:/* not supported */ + return 0; + } + + /* Calculate number of actual data byte transfers */ + diff = hwptr - db->hwptr; + /* The pointer is void * into physical memory; diff here is in bytes */ + + if (hwptr >= db-> dmasize) /* Wrap */ + hwptr = 0; + + if (hwptr < 0) { /* Out of area */ + hwptr = 0; + diff = 0; /* Fix up */ + } + db->hwptr = hwptr; /* Update next transfer start offset */ + return diff; +} + +/*****************************************************************************/ +/* Procedure clear_advance: */ +/* Fill the tail end of buffer with neutral sample data */ +/*****************************************************************************/ +static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *)buf) + bptr, c, x); + bptr = 0; + len -= x; + } + memset(((char *)buf) + bptr, c, len); +} + +/*****************************************************************************/ +/* Update pointers for the next DMA operation. */ +/* * Must be called with spinlock held. */ +/*****************************************************************************/ +static void ibmcsi3k_update_ptr(struct ibmcsi3k_state *s) +{ + int diff; + + /* update ADC pointer */ + if (s->state & IBMCSI_ADC_RUNNING) { + diff = get_hwptr(s, &s->dma_adc, IBMCSI_RXDMA); /* ADC channel (Rx DMA) */ + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) { + wake_up(&s->dma_adc.wait); + } + else { + DBG(printk("!@ %8.8x %8.8x\n", s->dma_adc.count, s->dma_adc.fragsize)); + } + + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->state &= ~IBMCSI_ADC_RUNNING; + s->dma_adc.error++; + } + else { + s->state &= ~IBMCSI_ADC_RUNNING; + start_adc(s); + } + } + /* TODO: Give some thoughts about mmap support */ + } + + /* update DAC pointer */ + if (s->state & IBMCSI_DAC_RUNNING) { + + diff = get_hwptr(s, &s->dma_dac, IBMCSI_TXDMA); /* DAC channel (Tx DMA) */ + + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { /* mapped = 0 if prepared */ + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } + else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + mtdcr(IBMCSI_TXDMA_CR, 0); /* Disable Tx DMA channel */ + s->state &= ~IBMCSI_DAC_RUNNING; + s->dma_dac.error++; + } + else if (s->dma_dac.count <= (signed)s->dma_dac.fragsize && !s->dma_dac.endcleared) { + clear_advance(s->dma_dac.rawbuf, s->dma_dac.dmasize, s->dma_dac.swptr, + s->dma_dac.fragsize, 0 ); + s->dma_dac.endcleared = 1; + } + + s->state &= ~IBMCSI_DAC_RUNNING; /* Let start_dac() do it */ + if (s->dma_dac.count + (signed)s->dma_dac.fragsize <= (signed)s->dma_dac.dmasize) { + wake_up(&s->dma_dac.wait); + } + else start_dac(s); + + } + } +} + + +/*****************************************************************************/ +/* DAC (Playback) Timer Interrupt Handler */ +/*****************************************************************************/ +static void ibmcsi_dac_timer(unsigned long param) { + +#ifdef USE_SG + struct ibmcsi3k_state *s = (struct ibmcsi3k_state *)param; + struct dma_sgdt *pCurrent_dt, *pNext_dt, *pWork; + struct dma_sgdt *pAnchor; + int count = 0; + + pAnchor = s->dac_sgdt_lastV; + + if ( ( mfdcr(DCRN_ASGC) & TX_SG_ENABLE ) && /* DAC running */ + ( mfdcr(IBMCSI_TXDMA_CT) <= 4 ) && /* No time to do anything */ + /* 4 samples = a little less than 100us at 44KHz */ + ( (void *)mfdcr( IBMCSI_TXDMA_SA ) != (pAnchor->srcP) ) ) { + udelay(100); /* Let the 4 samples run its course */ + } + + if ( mfdcr(DCRN_ASGC) & TX_SG_ENABLE ) { /* DAC running */ + + if (pAnchor->prevV) { /* Data queued */ + + pNext_dt = (struct dma_sgdt *) phys_to_virt(mfdcr(TX_SG)); /* Next S/G descriptor table */ + + if (check_sgdt_range(s, pNext_dt, SGDT_COUNT)) { + pCurrent_dt = pNext_dt->prevV; + + if (pCurrent_dt) { + pWork = pCurrent_dt; + if (pWork->prevV) { + struct dma_sgdt *pTemp = pWork; + pWork = pWork->prevV; + pTemp->prevV = NULL; /* Dequeue previous data */ + + while (pWork) { + count += ((unsigned int)(pWork->ctrl)) & (unsigned int) 0xffff; + pTemp = pWork; + pWork = pWork->prevV; /* Fetch next uplink pointer */ + free_sgdt( &(s->dac_free_sgdt_q), pTemp ); + } + } + s->dac_active_sgdt_q = pCurrent_dt; + } + + } + + s->dma_dac.total_bytes += count * 4; + s->dma_dac.count -= count * 4; + if (s->dma_dac.count <= 0) { + + /* stop_dac(s); *//* ??? */ + + s->dma_dac.error++; + } + if (count) { + wake_up(&s->dma_dac.wait); + } + } + } + + mod_timer(&s->dac_timer, jiffies + DAC_TIMER_PERIOD); +#endif + +} + +/*****************************************************************************/ +/* DAC (Playback) Interrupt Handler */ +/*****************************************************************************/ +static void ibmcsi3k_dac_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +#ifdef USE_SG + printk("ibmcsi: spurious dac int\n"); + return; +#else + struct ibmcsi3k_state *s = (struct ibmcsi3k_state *)dev_id; + /* We assume no interrupt sharing. Eval board has no other peripherals on the audio IRQs */ + + spin_lock(&s->lock); + + /* Stop CSI first, then clear DMA TX interrupt. */ + IBMCSI_WRITE(CSI0_ER, 0); /* Disable CSI */ + + DBG(printk("DEBUG: DMA0_SR %8x\n", mfdcr(IBMCSI_DMA_SR))); + + mtdcr(IBMCSI_TXDMA_CR, 0); /* Disable Tx DMA channel */ + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_TXDMA)); /* Clear Terminal Count etc. */ + IBMCSI_WRITE(CSI0_SR, CSI_SR_TOD); /* Clear Tx overrun for good measure */ + + DBG(IBMCSI_WRITE(CSI0_SCR, 0x02)); /* Debug breakpoint */ + if ( !(s->state & IBMCSI_HALT) ) { /* Update pointers later if in HALT state. */ + ibmcsi3k_update_ptr(s); + } + spin_unlock(&s->lock); +#endif +} + + +/*****************************************************************************/ +/* ADC (Capture) Interrupt Handler */ +/*****************************************************************************/ +static void ibmcsi3k_adc_interrupt(int irq, void *dev_id, struct pt_regs *regs) { + struct ibmcsi3k_state *s = (struct ibmcsi3k_state *)dev_id; + + spin_lock(&s->lock); + + /* Stop CSI, then clear DMA Rx interrupt. */ + IBMCSI_WRITE(CSI0_ER, 0); + mtdcr(IBMCSI_RXDMA_CR, 0); + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_RXDMA)); /* Clear Terminal Count etc. */ + IBMCSI_WRITE(CSI0_SR, CSI_SR_ROD); /* Clear Rx overrun for good measure */ + + ibmcsi3k_update_ptr(s); + spin_unlock(&s->lock); +} +/* ibmcsi_adc_timer to be added */ + +/*****************************************************************************/ +/* Drain DAC (flush playback buffer) */ +/*****************************************************************************/ +static int drain_dac(struct ibmcsi3k_state *s, int nonblock) +{ +#if 0 /* TODO! : Drain_dac not implemented yet. Timeout value depends on sampling rate. */ + DECLARE_WAITQUEUE(wait, current); + + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 + / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); + tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (!schedule_timeout(tmo + 1)) + DBG(printk(KERN_DEBUG "ibmcsi3k: DMA timed out??\n");) + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +#else + return 0; +#endif +} + +/*****************************************************************************/ +/* Reformat and copy device format data to user format data */ +/* (for capture) */ +/*****************************************************************************/ +static unsigned long copy_reformat_to_user(char *pDest, const char *pSrc, size_t count) { + unsigned long ret; + unsigned int i; + char *pWrp = (char *)pSrc ; /* Point at the first sample */ + char *pRdp = (char *)pSrc ; + + /* Copy 'count' SAMPLES of 16 bit data from pSrc to pDest */ + /* Contract in place on source */ + for (i=0; i= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +#if defined(USE_SG) +/*****************************************************************************/ +/* Scatter/Gather descriptor queue handlers */ +/*****************************************************************************/ +/* Spinlocks TBD (rely on global spinlocks for now) */ + +/* Initialize logical (virtual address) pool of descriptor tables */ +static void init_sgdt_q(struct dma_sgdt *queue, int count) { + int i; + struct dma_sgdt *work = queue; + + work->ctrl = 0; + work->prevV = NULL; /* Terminate backward link */ + for (i=1;i")); + if (*queueaddress == NULL) return NULL; + + work->prevV = NULL; /* Unlink backward */ + *queueaddress = work->nextV; /* Update queue pointer */ + + if (*queueaddress) + (*queueaddress)->prevV = NULL; + + work->nextV = NULL; /* Unlink forward */ + + DBG(printk("get_sgdt q %8.8x ret %8.8x\n",*queueaddress, work)); + return work; +} + +/* Return a descriptor table to free pool */ +static void free_sgdt(struct dma_sgdt **queueaddress, struct dma_sgdt *dt) { + struct dma_sgdt *work = *queueaddress; + DBG(printk("free_sgdt %8.8x queue %8.8x ", dt, work)); + DBG(printk("<")); + + *queueaddress = dt; /* Update queue read pointer */ + dt->prevV = NULL; /* Unlink backward */ + dt->nextV = work; /* Link forward */ + if (work) work->prevV = dt; /* Link backward if non-null */ + + DBG(printk("now %8.8x\n",*queueaddress)); +} + +/* Check the range of descriptor (stubbed out for now) */ +static unsigned int check_sgdt_range(struct ibmcsi3k_state *s, struct dma_sgdt *dt, int count) { + return 1; +} +#endif + +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ +/* Part II : Mixer device /dev/mixer */ +/********************************************************************************/ +/********************************************************************************/ +/********************************************************************************/ + + +/* ---------------------------------------------------------------------------- */ +/* The Si3000 codec supports the following channels: */ +/* - Master Volume (Line out; std. 600 ohm impedance) */ +/* - Speaker (Speaker; 32 ohm impedance) */ +/* - Line (Line in) */ +/* - Mic (Mic in) */ +/* - Phone In/Out (Handset; not connected on IBM 405LP Evaluation Board) */ +/* */ +/* ---------------------------------------------------------------------------- */ + + +/*****************************************************************************/ +/* /dev/mixer Open */ +/*****************************************************************************/ +static int ibmcsi3k_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct list_head *list; + struct ibmcsi3k_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct ibmcsi3k_state, devs); + if (s->dev_mixer == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + init_MUTEX(&s->mix_sem); + return 0; +} + +/*****************************************************************************/ +/* /dev/mixer IOCTL */ +/*****************************************************************************/ +/* This is the ioctl entry to the mixer driver. */ +static int ibmcsi3k_ioctl_mixdev(struct inode *ioctl, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct ibmcsi3k_state *s = (struct ibmcsi3k_state *) file->private_data; + const unsigned int nr = _IOC_NR(cmd); + int retval; + + down(&s->mix_sem); + { + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "IBMCSI3K", sizeof(info.id)); + strncpy(info.name, "IBM 405LP CSI3K", sizeof(info.name)); + info.modify_counter = s->modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "IBMCSI3K", sizeof(info.id)); + strncpy(info.name, "IBM 405LP CSI3K", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + + if (_SIOC_DIR(cmd) == _SIOC_READ) + retval = mixer_read_ioctl(s, nr, (caddr_t) arg); + else if ( (_SIOC_DIR(cmd) == _SIOC_WRITE) || + (_SIOC_DIR(cmd) == (_SIOC_READ | _SIOC_WRITE) ) ) + retval = mixer_write_ioctl(s, nr, (caddr_t) arg); + else + retval = -EINVAL; + } + up(&s->mix_sem); + return retval; +} + +/*****************************************************************************/ +/* /dev/mixer Release */ +/*****************************************************************************/ +static int ibmcsi3k_release_mixdev(struct inode *inode, struct file *file) +{ + struct ibmcsi3k_state *s = (struct ibmcsi3k_state *)file->private_data; + + VALIDATE_STATE(s); + return 0; +} + + +/*****************************************************************************/ +/* /dev/mixer Read IOCTLs */ +/*****************************************************************************/ +static int mixer_read_ioctl(struct ibmcsi3k_state *s, unsigned int nr, caddr_t arg) +{ + int val = -1; + + switch (nr) { + case SOUND_MIXER_CAPS: + val = 0; /* Can support mixing mic and line in */ + break; + + case SOUND_MIXER_DEVMASK: + val = ( SOUND_MASK_VOLUME | SOUND_MASK_LINE | /* Line out / Line in */ + SOUND_MASK_MIC | SOUND_MASK_SPEAKER | /* Mic in / Speaker out */ + +#if 0 /* No support for handset hardware */ + SOUND_MASK_PHONEIN | SOUND_MASK_PHONEOUT | +#endif + + SOUND_MASK_OGAIN | SOUND_MASK_IGAIN ); /* Tx/Rx PGA */ + break; + + case SOUND_MIXER_STEREODEVS: + val = 0 ; /* No stereo devices */ + break; + + case SOUND_MIXER_OUTMASK: + val = 0 ; /* No routing of inputs to outputs */ + break; + + case SOUND_MIXER_RECMASK: /* Recording devices */ + val = (SOUND_MASK_LINE | SOUND_MASK_MIC ); + break; + + case SOUND_MIXER_VOLUME: /* Line out volume */ + val = ibmcsi3k_get_volume(s); + break; + + case SOUND_MIXER_LINE: /* Line in volume */ + val = ibmcsi3k_get_line(s); + break; + + case SOUND_MIXER_MIC: /* Mic in volume */ + val = ibmcsi3k_get_mic(s); + break; + + case SOUND_MIXER_SPEAKER: /* Speaker out volume */ + val = ibmcsi3k_get_speaker(s); + break; + + case SOUND_MIXER_OGAIN: /* Output Pgmable Gain/Attenuation */ + val = ibmcsi3k_get_txpga(s); + break; + + case SOUND_MIXER_IGAIN: /* Input Pgmable Gain/Attenuation */ + val = ibmcsi3k_get_rxpga(s); + break; + + case SOUND_MIXER_OUTSRC: /* Unmuted output sources */ + val = ibmcsi3k_get_outsrc(s); + break; + + case SOUND_MIXER_RECSRC: /* Unmuted recording sources */ + val = ibmcsi3k_get_recsrc(s); + break; + default: + return -EINVAL; + } + return put_user(val, (int *) arg); +} + +/*****************************************************************************/ +/* /dev/mixer Write IOCTLs */ +/*****************************************************************************/ +static int mixer_write_ioctl(struct ibmcsi3k_state *s, unsigned int nr, caddr_t arg) +{ + int val; + int err; + + err = get_user(val, (int *) arg); + + if (err) + return -EFAULT; + switch (nr) { + case SOUND_MIXER_VOLUME: + val = ibmcsi3k_set_volume(s, val>>8); + break; + + case SOUND_MIXER_LINE: + val = ibmcsi3k_set_line(s, val>>8); + break; + + case SOUND_MIXER_MIC: + val = ibmcsi3k_set_mic(s, val>>8); + break; + + case SOUND_MIXER_SPEAKER: + val = ibmcsi3k_set_speaker(s, val>>8); + break; + + case SOUND_MIXER_IGAIN: + val = ibmcsi3k_set_rxpga(s, val>>8); + break; + + case SOUND_MIXER_OGAIN: + val = ibmcsi3k_set_txpga(s, val>>8); + break; + + case SOUND_MIXER_RECSRC: + if (s->state & (IBMCSI_DAC_RUNNING | IBMCSI_ADC_RUNNING) ) + return -EBUSY; /* can't change recsrc while running */ + val = ibmcsi3k_set_recsrc(s, val); + break; + + case SOUND_MIXER_OUTSRC: + val = ibmcsi3k_set_outsrc(s, val); + break; + + default: + return -EINVAL; + } + if (val < 0) + return val; + return put_user(val, (int *) arg); +} + + +/*****************************************************************************/ +/* Mixer IOCTL routines */ +/*****************************************************************************/ + + /************************/ + /************************/ + /* Get routines */ + /************************/ + /************************/ + +/*****************************************************************************/ +/* Read Line Out volume */ +/*****************************************************************************/ +static int ibmcsi3k_get_volume(struct ibmcsi3k_state *s) { + int val ; + + unsigned char attenuation = + ( si3000_read_reg( s, SI3000_AA /* 9 */ ) + & SI3000_AA_LOT /* 0x0C */ ) + >> SI3000_AA_LOT_OFFSET /* 2 */ ; + /* Register 9 bit 3:2 11= -18dB, 10=-12 dB, 01=-6 dB, 00= 0 dB */ + + unsigned char active = + ( si3000_read_reg( s, SI3000_ADC_VC /* 6 */ ) + & SI3000_ADC_VC_LOM /* 0x02 */ ) + >> SI3000_ADC_VC_LOM_OFFSET /* 1 */ ; + /* Register 6 bit 1 0 = Line Out Mute, 1 = Active */ + + int volume[4] = { 100, 75, 50, 25 }; /* Map to 0-100 */ + + if (active) val = volume[attenuation]; + else val = 0; + + return val; +} + +/*****************************************************************************/ +/* Read Line In volume (VOLUME) */ +/*****************************************************************************/ +static int ibmcsi3k_get_line(struct ibmcsi3k_state *s) { + int val; + + unsigned char gain, mute, temp; + int volume[4] = { 0, 33, 66, 100 } ; + + temp = si3000_read_reg( s, SI3000_RXGC1 /* 5 */ ); + /* Register 5 bit 7:6 11=20dB, 10=10dB, 01=0dB (00 reserved) */ + /* bit 5 1 = Line In Mute (reverse polarity vs out */ + + gain = ( temp & SI3000_RXGC1_LIG /* 0xC0 */ ) >> SI3000_RXGC1_LIG_OFFSET /* 6 */ ; + mute = temp & SI3000_RXGC1_LIM /* 0x20 */ ; + + if (mute) val = 0; + else val = volume[gain]; + + return val; +} + +/*****************************************************************************/ +/* Read Mic In volume (MIC) */ +/*****************************************************************************/ +static int ibmcsi3k_get_mic(struct ibmcsi3k_state *s) { + int val ; + int mic_volume[4] = { 25, 50, 75, 100 } ; + + unsigned char gain, mute, temp; + temp = si3000_read_reg( s, SI3000_RXGC1 /* 5 */ ); + /* Register 5 bit 4:3 11=30dB, 10=20dB, 01=10dB, 00 = 0dB */ + /* bit 2 0 = Mic In Mute (reverse polarity vs out) */ + + gain = ( temp & SI3000_RXGC1_MIG /* 0x18 */ ) + >> SI3000_RXGC1_MIG_OFFSET /* 3 */; + mute = ( temp & SI3000_RXGC1_MIM /* 0x04 */ ); + + if (mute) val = 0; + else val = mic_volume[gain]; + + return val; +} + +/*****************************************************************************/ +/* Read Speaker volume (AltPCM / PCM2) */ +/*****************************************************************************/ +static int ibmcsi3k_get_speaker(struct ibmcsi3k_state *s) { + int val ; + int speaker_volume[4] = { 100, 75, 50, 25 }; + + unsigned char attenuation = + ( si3000_read_reg( s, SI3000_AA /* 9 */ ) + & SI3000_AA_SOT /* 0x03 */ ); + /* Register 9 bit 1:0 Speaker Attenuation */ + /* 11=-18dB 10=-12 dB 01=-6dB 00=0dB */ + unsigned char active = + ( si3000_read_reg( s, SI3000_DAC_VC /* 7 */ ) + & ( SI3000_DAC_VC_SLM | SI3000_DAC_VC_SRM) /* 0x03 */ ); + /* Register 7 bit 1 0 = Speaker Left Mute, bit 0 0 = Speaker Right Mute */ + + if (active) val = speaker_volume[attenuation]; + else val = 0; + + return val; +} + +/*****************************************************************************/ +/* Read Output Programmable Gain / Attenuation (OGAIN) */ +/*****************************************************************************/ +static int ibmcsi3k_get_txpga(struct ibmcsi3k_state *s) { + int val; + unsigned char gain = ( si3000_read_reg( s, SI3000_DAC_VC /* 7 */ ) + & SI3000_DAC_VC_TXG /* 0x7C */ ) + >> SI3000_DAC_VC_TXG_OFFSET /* 2 */ ; + + val = ( (unsigned int) gain * 100) / 31; + + return val; +} + +/*****************************************************************************/ +/* Read Input Programmable Gain / Attenuation (IGAIN) */ +/*****************************************************************************/ +static int ibmcsi3k_get_rxpga(struct ibmcsi3k_state *s) { + int val; + unsigned char gain = ( si3000_read_reg( s, SI3000_ADC_VC /* 6 */ ) + & SI3000_ADC_VC_RXG /* 0x7C */ ) + >> SI3000_ADC_VC_RXG_OFFSET /* 2 */ ; + + val = ((unsigned int) gain * 100 ) / 31; /* 31 = binary 111 11 (5 bits), remap to 0-100 */ + + return val; +} + +/*****************************************************************************/ +/* Read Recording Source */ +/*****************************************************************************/ +static int ibmcsi3k_get_recsrc(struct ibmcsi3k_state *s) { + int val = SOUND_MASK_LINE | SOUND_MASK_MIC; + unsigned char temp = si3000_read_reg( s, SI3000_RXGC1 /* 5 */ ); + + if ( temp & SI3000_RXGC1_LIM /* 0x20 */ ) val &= ~SOUND_MASK_LINE; + if ( temp & SI3000_RXGC1_MIM /* 0x04 */ ) val &= ~SOUND_MASK_MIC; + + return val; +} + +/*****************************************************************************/ +/* Read Output Source */ +/*****************************************************************************/ +static int ibmcsi3k_get_outsrc(struct ibmcsi3k_state *s) { + int val = 0; + unsigned char temp = si3000_read_reg( s, SI3000_ADC_VC /* 6 */ ); + if ( temp & SI3000_ADC_VC_LOM /* 0x02 */ ) /* Line out active (1) */ + val |= SOUND_MASK_VOLUME; + + temp = si3000_read_reg( s, SI3000_DAC_VC /* 7 */ ); + if ( temp & (SI3000_DAC_VC_SLM | SI3000_DAC_VC_SRM) ) /* Speaker left or right active (1) */ + val |= SOUND_MASK_SPEAKER; + + return val; +} + +/*****************************************************************************/ + + /************************/ + /************************/ + /* Set routines */ + /************************/ + /************************/ + +/*****************************************************************************/ +/* Set Line Out volume (VOLUME) */ +/*****************************************************************************/ +static int ibmcsi3k_set_volume(struct ibmcsi3k_state *s, int val) { + + if (val < 0) val = 0; + if (val > 100) val = 100; + + if ( val == 0) { /* Mute */ + si3000_write_reg( s, SI3000_ADC_VC /* 6 */ , + si3000_read_reg( s, SI3000_ADC_VC ) + & ~SI3000_ADC_VC_LOM /* 0xFD */ ) ; /* Bit 1 off */ + } + else { + unsigned char temp; + si3000_write_reg( s, SI3000_ADC_VC /* 6 */ , + si3000_read_reg( s, SI3000_ADC_VC ) + | SI3000_ADC_VC_LOM /* 0x02 */ ) ; /* Unmute */ + + temp = ( ( 100 - val ) / 25 ) << SI3000_AA_LOT_OFFSET /* 2 */ ; + si3000_write_reg( s, SI3000_AA /* 9 */, + ( si3000_read_reg( s, SI3000_AA ) + & ~SI3000_AA_LOT /* 0xF3 */ ) | temp ) ; + } + + return ibmcsi3k_get_volume(s); +} + +/*****************************************************************************/ +/* Set Line In volume (LINE) */ +/*****************************************************************************/ +static int ibmcsi3k_set_line(struct ibmcsi3k_state *s, int val) { + + if (val < 0) val = 0; + if (val > 100) val = 100; + + if ( val == 0 ) { + si3000_write_reg( s, SI3000_RXGC1 /* 5 */, + si3000_read_reg( s, SI3000_RXGC1 ) + | SI3000_RXGC1_LIM /* 0x20 */ ) ; /* Bit 5 on */ + } + else { + unsigned char temp; + + val = ( (val + 31) / 33 ); + + if (val == 0) temp = 1; /* Patch ;-) */ + else temp = (unsigned char) val; + + temp = temp << SI3000_RXGC1_LIG_OFFSET /* 6 */ ; + si3000_write_reg( s, SI3000_RXGC1 /* 5 */, + ( si3000_read_reg( s, SI3000_RXGC1 ) + & ~(SI3000_RXGC1_LIG | SI3000_RXGC1_LIM) /* 0x1F */ ) + | temp ) ; + /* Write bits 7:6, also bit 5 off (unmute) */ + } + + return ibmcsi3k_get_line(s); +} + +/*****************************************************************************/ +/* Set Mic In volume (MIC) */ +/*****************************************************************************/ +static int ibmcsi3k_set_mic(struct ibmcsi3k_state *s, int val) { + + if (val < 0) val = 0; + if (val > 100) val = 100; + + if (val == 0) { /* Mute mic in */ + si3000_write_reg( s, SI3000_RXGC1 /* 5 */, + si3000_read_reg( s, SI3000_RXGC1 ) | SI3000_RXGC1_MIM /* 0x04 */ ) ; /* Bit 2 on */ + } + else { + unsigned char temp; + + val = (( ( val + 24) / 25 ) - 1); + temp = (( (unsigned char) val ) & 0x03 ) << SI3000_RXGC1_MIG_OFFSET /* 3 */ ; + si3000_write_reg( s, SI3000_RXGC1 /* 5 */, + ( si3000_read_reg( s, SI3000_RXGC1 ) + & ~( SI3000_RXGC1_MIG | SI3000_RXGC1_MIM) /* ~(0x18 | 0x04) = 0xE3 */ ) + | temp ) ; + + } + + return ibmcsi3k_get_mic(s); +} + +/*****************************************************************************/ +/* Set Speaker volume (AltPCM / PCM2) */ +/*****************************************************************************/ +static int ibmcsi3k_set_speaker(struct ibmcsi3k_state *s, int val) { + + if (val < 0) val = 0; + if (val > 100) val = 100; + + if (val == 0) { /* Mute speakers */ + si3000_write_reg( s, SI3000_DAC_VC /* 7 */, + si3000_read_reg( s, SI3000_DAC_VC ) + & ~(SI3000_DAC_VC_SLM | SI3000_DAC_VC_SRM) /* 0xFC */ ); + } + else { /* Set analog attenuation */ + unsigned char temp; + si3000_write_reg( s, SI3000_DAC_VC /* 7 */, + si3000_read_reg( s, SI3000_DAC_VC ) + | (SI3000_DAC_VC_SLM | SI3000_DAC_VC_SRM) /* 3 */ ); /* Unmute speakers */ + + val = (100 - val)/25; + temp = ( si3000_read_reg( s, SI3000_AA /* 9 */ ) + & ~SI3000_AA_SOT /* 0xFC */ ) + | (unsigned char) (val & SI3000_AA_SOT /* 0x03 */) ; + si3000_write_reg( s, SI3000_AA, temp ); + } + + return ibmcsi3k_get_speaker(s); +} + +/*****************************************************************************/ +/* Set Output Programmable Gain / Attenuation volume (OGAIN) */ +/*****************************************************************************/ +static int ibmcsi3k_set_txpga(struct ibmcsi3k_state *s, int val) { + + if (val < 0) val = 0; + if (val > 100) val = 100; + + val = ( ( val * 31 ) / 100 ) << SI3000_DAC_VC_TXG_OFFSET /* 2 */; + + si3000_write_reg( s, SI3000_DAC_VC /* 7 */, + ( si3000_read_reg( s, SI3000_DAC_VC ) + & ~SI3000_DAC_VC_TXG /*0x83*/ ) + | val ); + + return ibmcsi3k_get_txpga(s); +} + +/*****************************************************************************/ +/* Set Input Programmable Gain / Attenuation volume (IGAIN) */ +/*****************************************************************************/ +static int ibmcsi3k_set_rxpga(struct ibmcsi3k_state *s, int val) { + + if (val < 0) val = 0; + if (val > 100) val = 100; + + val = ( (val * 31) / 100 ) << SI3000_ADC_VC_RXG_OFFSET /* 2 */; + + si3000_write_reg( s, SI3000_ADC_VC /* 6 */, + ( si3000_read_reg( s, SI3000_ADC_VC ) + & ~SI3000_ADC_VC_RXG /* 0x83 */ ) + | val ); + + return ibmcsi3k_get_rxpga(s); +} + +/*****************************************************************************/ +/* Set Recording Source */ +/*****************************************************************************/ +static int ibmcsi3k_set_recsrc(struct ibmcsi3k_state *s, int val) { + + if (val & SOUND_MASK_MIC) { /* Mic in active */ + si3000_write_reg( s, SI3000_RXGC1 /* 5 */, + si3000_read_reg( s, SI3000_RXGC1 ) + & ~SI3000_RXGC1_MIM /* 0xFB */ ) ; /* Turn off Mic In Mute bit */ + } + else { /* Mic in mute */ + si3000_write_reg( s, SI3000_RXGC1, + si3000_read_reg( s, SI3000_RXGC1 ) + | SI3000_RXGC1_MIM /* 0x04 */ ) ; /* Turn on Mic In Mute bit */ + } + + if (val & SOUND_MASK_LINE) { /* Line in active */ + si3000_write_reg( s, SI3000_RXGC1, + si3000_read_reg( s, SI3000_RXGC1 ) + & ~SI3000_RXGC1_LIM /* 0xDF */ ) ; /* Turn off Line In Mute bit */ + } + else { /* Line in mute */ + si3000_write_reg( s, SI3000_RXGC1, + si3000_read_reg( s, SI3000_RXGC1 ) + | SI3000_RXGC1_LIM /* 0x20 */ ) ; /* Turn on Line In Mute bit */ + } + + return ibmcsi3k_get_recsrc(s); +} + +/*****************************************************************************/ +/* Set Output Source */ +/*****************************************************************************/ +static int ibmcsi3k_set_outsrc(struct ibmcsi3k_state *s, int val) { + + if (val & SOUND_MASK_VOLUME) { /* Line out active */ + si3000_write_reg( s, SI3000_ADC_VC /* 6 */, + si3000_read_reg( s, SI3000_ADC_VC ) + | SI3000_ADC_VC_LOM /* 0x02 */ ) ; /* Turn on Line Out Mute bit */ + } + else { /* Line out mute */ + si3000_write_reg( s, SI3000_ADC_VC, + si3000_read_reg( s, SI3000_ADC_VC ) + & ~SI3000_ADC_VC_LOM /* 0xFD */ ) ; + } + + if (val & SOUND_MASK_SPEAKER) { /* Speaker L/R active */ + si3000_write_reg( s, SI3000_DAC_VC /* 7 */, + si3000_read_reg( s, SI3000_DAC_VC ) + | ( SI3000_DAC_VC_SLM | SI3000_DAC_VC_SRM ) /* 0x03 */ ) ; + } + else { /* Speaker L/R mute */ + si3000_write_reg( s, SI3000_DAC_VC, + si3000_read_reg( s, SI3000_DAC_VC ) + & ~( SI3000_DAC_VC_SLM | SI3000_DAC_VC_SRM ) /* 0xFC */ ) ; + + } + + return ibmcsi3k_get_outsrc(s); +} + + +/*****************************************************************************/ + + + /************************/ + /************************/ + /* Low level routines */ + /************************/ + /************************/ + +/*****************************************************************************/ +/* Read Si3000 control register */ +/* (handles pause/restart during playback/capture) */ +/*****************************************************************************/ +static unsigned char si3000_read_reg( struct ibmcsi3k_state *s, int reg ) { + unsigned int csi_er, csi_cfg, dma_cr, dma_ct, dma_sa, dma_da, val; + unsigned long timeout, flag; + + if (s->state & IBMCSI_DAC_RUNNING) { +#if defined(USE_SG) + return 0; /* Tricky to halt/restart Scatter/Gather. */ + /* Stubbed out for now. */ +#else + s->state |= IBMCSI_HALT; + + /* Save CSI status */ + csi_er = IBMCSI_READ(CSI0_ER); + csi_cfg = IBMCSI_READ(CSI0_CFG); + + /* Halt CSI first */ + IBMCSI_WRITE(CSI0_ER, 0); + + timeout = jiffies + HZ/25; /* TODO (NTH): fine tune */ + while (jiffies < timeout) { /* Wait for busy flag to go down. */ + if ( !(IBMCSI_READ(CSI0_SR) & CSI_SR_CSIB) ) { + break; + } + } + + asm volatile("sync"); + + /* Save DMA setup */ + dma_cr = mfdcr(IBMCSI_TXDMA_CR); + dma_ct = mfdcr(IBMCSI_TXDMA_CT); + dma_sa = mfdcr(IBMCSI_TXDMA_SA); + dma_da = mfdcr(IBMCSI_TXDMA_DA); + + asm volatile("sync"); /* Paranoia: Make sure mtdcr 's and MMIO writes have all completed */ + + spin_unlock_irqrestore(&s->lock, flag); + /* Pending DMA interrupt (if any) will kick in at this point. */ + + if ( !(IBMCSI_READ(CSI0_SR) & CSI_SR_CSIB) ) { + mtdcr(IBMCSI_TXDMA_CR, 0); + asm volatile("sync"); + + val = si3000_read_reg2(s, reg, 3); + /* Fewer retries to minimize artifact / noise */ + } + + /* Restore CSI / Tx DMA setup */ + s->state &= ~IBMCSI_HALT; + if (dma_ct == 0) { + /* Transfer had completed, go fetch next block. */ + ibmcsi3k_update_ptr(s); + } + else { + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_TXDMA)); + + mtdcr(IBMCSI_TXDMA_CT, dma_ct); + mtdcr(IBMCSI_TXDMA_SA, dma_sa); + mtdcr(IBMCSI_TXDMA_DA, dma_da); + mtdcr(IBMCSI_TXDMA_CR, dma_cr); + + IBMCSI_WRITE(CSI0_CFG, csi_cfg); + + asm volatile ("sync"); + + IBMCSI_WRITE(CSI0_ER, csi_er); + } + +#endif + } + + else if (s->state & IBMCSI_ADC_RUNNING) { + s->state |= IBMCSI_HALT; + + /* Save CSI status */ + csi_er = IBMCSI_READ(CSI0_ER); + csi_cfg = IBMCSI_READ(CSI0_CFG); + + /* Halt CSI first */ + IBMCSI_WRITE(CSI0_ER, 0); + + timeout = jiffies + HZ/25; /* TODO (NTH): fine tune */ + while (jiffies < timeout) { /* Wait for busy flag to go down. */ + if ( !(IBMCSI_READ(CSI0_SR) & CSI_SR_CSIB) ) { + break; + } + } + + asm volatile("sync"); + + /* Save DMA setup */ + dma_cr = mfdcr(IBMCSI_RXDMA_CR); + dma_ct = mfdcr(IBMCSI_RXDMA_CT); + dma_sa = mfdcr(IBMCSI_RXDMA_SA); + dma_da = mfdcr(IBMCSI_RXDMA_DA); + + asm volatile("sync"); /* Paranoia: Make sure mtdcr 's and MMIO writes have all completed */ + + spin_unlock_irqrestore(&s->lock, flag); + /* Pending DMA interrupt (if any) will kick in at this point. */ + + if ( !(IBMCSI_READ(CSI0_SR) & CSI_SR_CSIB) ) { + mtdcr(IBMCSI_RXDMA_CR, 0); + asm volatile("sync"); + + val = si3000_read_reg2(s, reg, 3); + /* Fewer retries to minimize artifact / noise */ + } + + /* Restore CSI / Rx DMA setup */ + s->state &= ~IBMCSI_HALT; + if (dma_ct == 0) { + /* Transfer had completed, go fetch next block. */ + ibmcsi3k_update_ptr(s); + } + else { /* Restart previous transfer */ + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_RXDMA)); + + mtdcr(IBMCSI_RXDMA_CT, dma_ct); + mtdcr(IBMCSI_RXDMA_SA, dma_sa); + mtdcr(IBMCSI_RXDMA_DA, dma_da); + mtdcr(IBMCSI_RXDMA_CR, dma_cr); + + IBMCSI_WRITE(CSI0_CFG, csi_cfg); + + asm volatile ("sync"); + + IBMCSI_WRITE(CSI0_ER, csi_er); + } + + } + + else { + val = si3000_read_reg2( s, reg, 20 ); + /* Can afford a lot of retries until we get it right, if currently not active */ + } + + return val; +} + +/* TODO: Reduce redundancy. */ + +/*****************************************************************************/ +/* Read Si3000 control register - 2nd level */ +/* (handles actual transfer, verification and retry) */ +/*****************************************************************************/ +static unsigned char si3000_read_reg2( struct ibmcsi3k_state *s, int reg, int retrycount) { + unsigned char result1, result2, result3; + int i; + for (i=0; isync_interval); + if (s->timeout) + continue; + result2 = si3000_transfer( s, reg, 1 , 0, s->sync_interval); + if (s->timeout) + continue; + result3 = si3000_transfer( s, reg, 1 , 0, s->sync_interval); + if (s->timeout) + continue; + + if ( (result1 == result2) && (result1 == result3) ) { +#if 0 /* Debug messages */ + if (i) { + printk("OK after %d mismatch(es), result %x\n",i,result1); + } + else { + printk("OK first time, result %x\n", result1); + } +#endif + return result1; + } + } + /* Bad! */ + printk("ibmcsi3k: Si3000 I/O error!\n"); + return 0; + +} + +/*****************************************************************************/ +/* Write Si3000 control register */ +/* (handles pause/restart during playback/capture) */ +/*****************************************************************************/ + +/* Note: write_reg2 uses read_reg to verify, so read must be working first. */ + +static void si3000_write_reg( struct ibmcsi3k_state *s, int reg, unsigned char val) { + unsigned int csi_er, csi_cfg, dma_cr, dma_ct, dma_sa, dma_da; + unsigned long timeout,flag; + + spin_lock_irqsave(&s->lock, flag); + if (s->state & IBMCSI_DAC_RUNNING) { +#if defined(USE_SG) + return; +#else + s->state |= IBMCSI_HALT; + + /* Save CSI status */ + csi_er = IBMCSI_READ(CSI0_ER); + csi_cfg = IBMCSI_READ(CSI0_CFG); + + /* Halt CSI first */ + IBMCSI_WRITE(CSI0_ER, 0); + + timeout = jiffies + HZ/25; /* TODO (NTH): fine tune */ + while (jiffies < timeout) { /* Wait for busy flag to go down. */ + if ( !(IBMCSI_READ(CSI0_SR) & CSI_SR_CSIB) ) { + break; + } + } + + asm volatile("sync"); + + /* Save DMA setup */ + dma_cr = mfdcr(IBMCSI_TXDMA_CR); + dma_ct = mfdcr(IBMCSI_TXDMA_CT); + dma_sa = mfdcr(IBMCSI_TXDMA_SA); + dma_da = mfdcr(IBMCSI_TXDMA_DA); + + asm volatile("sync"); + + spin_unlock_irqrestore(&s->lock, flag); + /* Pending DMA interrupt (if any) will kick in at this point. */ + + if ( !(IBMCSI_READ(CSI0_SR) & CSI_SR_CSIB) ) { + mtdcr(IBMCSI_TXDMA_CR, 0); + asm volatile("sync"); + + /* Write register */ + si3000_write_reg2(s,reg,val,5); + } + + /* Restore CSI / Tx DMA setup */ + s->state &= ~IBMCSI_HALT; + if (dma_ct == 0) { + /* Transfer had completed, go fetch next block. */ + ibmcsi3k_update_ptr(s); + } + else { + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_TXDMA)); + + mtdcr(IBMCSI_TXDMA_CT, dma_ct); + mtdcr(IBMCSI_TXDMA_SA, dma_sa); + mtdcr(IBMCSI_TXDMA_DA, dma_da); + mtdcr(IBMCSI_TXDMA_CR, dma_cr); + + IBMCSI_WRITE(CSI0_CFG, csi_cfg); + + asm volatile ("sync"); + + IBMCSI_WRITE(CSI0_ER, csi_er); + } +#endif + } /* TODO: See if we can support concurrent record/playback */ + else if (s->state & IBMCSI_ADC_RUNNING) { + s->state |= IBMCSI_HALT; + + /* Save CSI status */ + csi_er = IBMCSI_READ(CSI0_ER); + csi_cfg = IBMCSI_READ(CSI0_CFG); + + /* Halt CSI first */ + IBMCSI_WRITE(CSI0_ER, 0); + + timeout = jiffies + HZ/25; /* TODO (NTH): fine tune */ + while (jiffies < timeout) { /* Wait for busy flag to go down. */ + if ( !(IBMCSI_READ(CSI0_SR) & CSI_SR_CSIB) ) { + break; + } + } + + asm volatile("sync"); + + /* Save DMA setup */ + dma_cr = mfdcr(IBMCSI_RXDMA_CR); + dma_ct = mfdcr(IBMCSI_RXDMA_CT); + dma_sa = mfdcr(IBMCSI_RXDMA_SA); + dma_da = mfdcr(IBMCSI_RXDMA_DA); + + asm volatile("sync"); + + spin_unlock_irqrestore(&s->lock, flag); + /* Pending DMA interrupt (if any) will kick in at this point. */ + + if ( !(IBMCSI_READ(CSI0_SR) & CSI_SR_CSIB) ) { + mtdcr(IBMCSI_RXDMA_CR, 0); + asm volatile("sync"); + + /* Write register */ + si3000_write_reg2(s,reg,val,5); + } + + /* Restore CSI / Rx DMA setup */ + s->state &= ~IBMCSI_HALT; + if (dma_ct == 0) { + /* Transfer had completed, go fetch next block. */ + ibmcsi3k_update_ptr(s); + } + else { + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_RXDMA)); + + mtdcr(IBMCSI_RXDMA_CT, dma_ct); + mtdcr(IBMCSI_RXDMA_SA, dma_sa); + mtdcr(IBMCSI_RXDMA_DA, dma_da); + mtdcr(IBMCSI_RXDMA_CR, dma_cr); + + IBMCSI_WRITE(CSI0_CFG, csi_cfg); + + asm volatile ("sync"); + + IBMCSI_WRITE(CSI0_ER, csi_er); + } + + } + else { + spin_unlock_irqrestore(&s->lock, flag); + si3000_write_reg2(s,reg,val,20); + } + +} + + +/*****************************************************************************/ +/* Write Si3000 control register - 2nd level */ +/* (handles actual transfer, verification and retry) */ +/*****************************************************************************/ +static void si3000_write_reg2( struct ibmcsi3k_state *s, int reg, unsigned char val, int retrycount) { + int i; + for (i=0; isync_interval ); /* Write it */ + if (val == si3000_transfer(s, reg, 1, 0, s->sync_interval ) ) /* Verify */ + return; + } + DBG(printk("ibmcsi3k: Error: cannot write/verify Si3000 register %d\n", reg)); +} + +/*****************************************************************************/ +/* Set the Si3000 sampling rate */ +/* */ +/*****************************************************************************/ +static int si3000_set_sampling_rate(struct ibmcsi3k_state *s, int val) { + int current_sampling_rate, i, j, retcode; + unsigned int sync_value; + + /* TODO: currently limited to default sampling rate */ + if (val != IBMCSI_DEFAULT_SAMPLING_RATE) return 0; + + for (j=0; j<5; j++) { /* Arbitrary retry count */ + + /* We need to know the current sampling rate in order to access the Si3000 */ + /* control registers, due to timing requirements of the CSI. */ + /* So we can't simply read the control registers during init. */ + + for (i=0; i< 5; i++) { /* Another arbitrary retry count */ + if ((current_sampling_rate = si3000_get_sampling_rate(s))) break; + } + if (current_sampling_rate == 0) { + printk(KERN_ERR "ibmcsi3k: cannot determine sampling rate \n"); + return 0; + } + + sync_value = ( (256 + 160 - 64) * 1000000 / ( current_sampling_rate * 256 ) ); + /* We need to hit the 160th clock tick in the next frame. */ + /* We sync at 64th clock (see si3000_synchronize), and there are */ + /* 256 clocks per frame. We convert this to microseconds and pass it to */ + /* data transfer handler. */ + + printk("ibmcsi3k: current sampling rate %d Hz, sync delay %d us \n", + current_sampling_rate, sync_value); + + /* The PLL divider must be written first, and the multiplier written back to back. */ + /* The routines are self-correcting, so any residual errors from previous boot will be */ + /* (supposedly) cleared by the first call. */ + + /* TODO: calculate multiplier / divider from given sampling rate. */ + /* Current values are for 11.025 KHz. */ + si3000_transfer(s, SI3000_PLL1D, 0, 47, sync_value); + si3000_transfer(s, SI3000_PLL1M, 0, 244, sync_value); + + udelay(1000); /* Ugly but the CSI may not function correctly while */ + /* si3000 PLL is re-locking and FSYNC is changing. */ + /* PLL lock time is guaranteed to be < 1 ms. */ + + /* Read the sampling rate again to recalculate sync delay value. */ + for (i=0; i<5; i++) { + if ( (current_sampling_rate = si3000_get_sampling_rate(s))) break; + } + if (current_sampling_rate == 0) { + printk(KERN_ERR "ibmcsi3k: cannot determine sampling rate \n"); + return 0; + } + sync_value = ( (256 + 160 - 64) * 1000000 / ( current_sampling_rate * 256 ) ); + printk("ibmcsi3k: updated sampling rate %d Hz, sync delay %d us\n", + current_sampling_rate, sync_value); + + s->sync_interval = sync_value; + if ( ( si3000_read_reg(s, SI3000_PLL1D) == 47 ) && + ( si3000_read_reg(s, SI3000_PLL1M) == 244 ) ) { + printk("ibmcsi3k: sampling rate set successfully.\n"); + retcode = 1; + break; + } + else { + retcode = 0; + } + } + + return retcode; +} + + +/*****************************************************************************/ +/* Read the actual sampling rate of the Si3000 */ +/* */ +/* We do two back to back dummy DMA transfers which end at a fixed */ +/* offset from frame sync signal FSYNC, and measure the time it takes */ +/* for the second transfer to complete. */ +/* */ +/* This is necessary because the Si3000 register access requires */ +/* precise timing alignment w.r.t FSYNC and serial clock ticks. */ +/* */ +/*****************************************************************************/ +static int si3000_get_sampling_rate(struct ibmcsi3k_state *s) { + unsigned long tbl_before, tbl_after, usecs; + int val = 0; + + ibmcsi3k_synchronize(s); /* First sync */ + tbl_before = get_tbl(); /* Read time base register low */ + ibmcsi3k_synchronize(s); /* Second sync */ + tbl_after = get_tbl(); /* Read tbl again */ + + usecs = mulhwu(tb_to_us, tbl_after - tbl_before); /* Get usecs/frame */ + /* This conversion to usec is not very accurate... */ + + if (usecs) { + val = 1000000/usecs; + } + return val; +} + + +/*****************************************************************************/ +/* Transfer data to and from Si3000 (polling mode) */ +/*****************************************************************************/ +static unsigned char si3000_transfer( struct ibmcsi3k_state *s, + int reg, int read , unsigned char val , unsigned int sync_value) { + + unsigned long end_time; + int timeout = 0; + unsigned char command = reg; + + if (read) command |= 0x20; + + /* Prepare line buffer */ + (s->write_line)[0] = 0x00; /* Dummy value */ + (s->write_line)[1] = 0x01; /* Secondary frame request bit */ + (s->write_line)[2] = 0x00; /* Dummy value - ignored */ + (s->write_line)[3] = 0x00; + + /* Register number (lower 5 bits) and bit 6 = read (1) / write (0) */ + (s->write_line)[4] = command; + /* Register value for write */ + (s->write_line)[5] = val; + (s->write_line)[6] = 0x00; + (s->write_line)[7] = 0x00; + + (s->write_line)[8] = 0x00; /* Ditto above */ + (s->write_line)[9] = 0x01; + (s->write_line)[10] = 0x00; + (s->write_line)[11] = 0x00; + + (s->write_line)[12] = command; + (s->write_line)[13] = val; + (s->write_line)[14] = 0x00; + (s->write_line)[15] = 0x00; + + (s->write_line)[16] = 0x00; + (s->write_line)[17] = 0x01; + (s->write_line)[18] = 0x00; + (s->write_line)[19] = 0x00; + + (s->write_line)[20] = command; + (s->write_line)[21] = val; + (s->write_line)[22] = 0x00; + (s->write_line)[23] = 0x00; + + + (s->write_line)[24] = 0x00; + (s->write_line)[25] = 0x00; + (s->write_line)[26] = 0x00; + (s->write_line)[27] = 0x00; + + (s->write_line)[28] = 0x00; + (s->write_line)[29] = 0x00; + (s->write_line)[30] = 0x00; + (s->write_line)[31] = 0x00; + + /* The above code is intentionally redundant. */ + /* Initialization is done byte by byte in the event that */ + /* this routine is ported to drive a little-endian codec chip. */ + + ibmcsi3k_synchronize(s); + + /* Disable CSI */ + IBMCSI_WRITE(CSI0_ER, 0); + + /* Clear CSI overrun / underrun */ + IBMCSI_WRITE(CSI0_SR, CSI_SR_ROD | CSI_SR_TOD); + + /* Disable channels */ + mtdcr(IBMCSI_TXDMA_CR, 0); + mtdcr(IBMCSI_RXDMA_CR, 0); + + /* Clear all Tx DMA channel/1 status register flags - Terminal Count etc. */ + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_TXDMA) | DCRN_DMA_SR_ALL(IBMCSI_RXDMA)); + + /* Clear any pending internal DMA request that may be stuck due to premature termination etc. */ + /* (Writing a '1' to the status register will not clear such a state.) */ + if (mfdcr(IBMCSI_DMA_SR) & (DCRN_DMA_SR_IR(IBMCSI_TXDMA) | DCRN_DMA_SR_IR(IBMCSI_RXDMA)) ) { + mtdcr(IBMCSI_TXDMA_SA, (unsigned) s->dma_write_line + 28); + mtdcr(IBMCSI_TXDMA_DA, CSI0_TBUF); + mtdcr(IBMCSI_RXDMA_SA, CSI0_RBUF); + mtdcr(IBMCSI_RXDMA_DA, (unsigned) s->dma_read_line + 28); + mtdcr(IBMCSI_TXDMA_CT, 2); + mtdcr(IBMCSI_RXDMA_CT, 2); + mtdcr(IBMCSI_TXDMA_CR, IBMCSI_TXDMA_GO_NOI ); /* No int */ + mtdcr(IBMCSI_RXDMA_CR, IBMCSI_RXDMA_GO_NOI ); /* No int */ + + udelay(2); /* Paranoia */ + + mtdcr(IBMCSI_TXDMA_CR, 0); + mtdcr(IBMCSI_RXDMA_CR, 0); + } + + /* Set Tx DMA channel source address */ + mtdcr(IBMCSI_TXDMA_SA, (unsigned)(s->dma_write_line)); + + /* Set Rx DMA channel SOURCE address */ + mtdcr(IBMCSI_RXDMA_SA, CSI0_RBUF); + + /* Set Tx DMA channel count */ + mtdcr(IBMCSI_TXDMA_CT, 2); + + /* Set Rx DMA channel count */ + mtdcr(IBMCSI_RXDMA_CT, 2); + + /* Set Tx DMA channel destination address */ + mtdcr(IBMCSI_TXDMA_DA, CSI0_TBUF); + + /* Set Rx DMA channel DESTINATION address */ + mtdcr(IBMCSI_RXDMA_DA, (unsigned)(s->dma_read_line)); + + /* Enable DMA0 with interrupt disable */ + mtdcr(IBMCSI_TXDMA_CR, IBMCSI_TXDMA_GO_NOI ); + + /* Enable DMA1 with interrupt disable */ + mtdcr(IBMCSI_RXDMA_CR, IBMCSI_RXDMA_GO_NOI ); + + asm volatile ("sync"); /* Sync */ + + /* Synchronize CSI enable to FSYNC */ + +#if 1 + /* udelay( (2560/28) + sync_value + 5 ); */ /* Works if OpenBIOS has set it up */ + udelay(sync_value); + +#ifdef TIMINGDEBUGPROBE + __raw_writel(0x30600000, 0xef600a00); +#endif + + IBMCSI_WRITE(CSI0_CFG, 0x00200000); /* 128 bit frame size */ + IBMCSI_WRITE(CSI0_ER, CSI_ER_ESLOT(0) | + CSI_ER_RXEN | CSI_ER_TXEN | CSI_ER_ECSI ); + /* This must be done between 160th and 224th serial clock ticks */ + +#else /* 256 bit frame size mode */ + udelay(150); + IBMCSI_WRITE(CSI0_CFG, 0); + IBMCSI_WRITE(CSI0_ER, CSI_ER_ESLOT(0) | CSI_ER_ESLOT(8) | + CSI_ER_RXEN | CSI_ER_TXEN | CSI_ER_ECSI ); + /* This method is unsupported, but is known to work after one frame */ +#endif + + end_time = jiffies + HZ/50; + + while ( !(mfdcr(IBMCSI_DMA_SR) & DCRN_DMA_SR_CS(IBMCSI_RXDMA)) ) { + if (jiffies > end_time) { + timeout = 1; + break; + } + } /* Wait until Rx DMA channel Terminal Count is posted or timeout */ + + /* Stop CSI */ + IBMCSI_WRITE(CSI0_ER, 0); + + IBMCSI_WRITE(CSI0_TBUF, 0); /* Reset tx buffer data to prevent noise */ + + /* Stop Tx/Rx DMA */ + mtdcr(IBMCSI_TXDMA_CR, 0); + mtdcr(IBMCSI_RXDMA_CR, 0); + + /* Clear Tx/Rx DMA status */ + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_TXDMA) | DCRN_DMA_SR_ALL(IBMCSI_RXDMA)); + + if (!timeout) { + s->timeout = 0; + return ((s->read_line)[5]); + } + else { + s->timeout = 1; /* Error return */ + return 0; + } + +} + + +/*****************************************************************************/ +/* Synchronize code execution to Si3000 FSYNC signal */ +/* */ +/* This is necessary to precisely synchronize the timing of CSI enable */ +/* for the 128 bits per frame mode, in which the CSI must be enabled */ +/* between the 160th and 224th serial ticks since FSYNC. */ +/* */ +/* This routine does a dummy Rx DMA of 2 samples and polls for DMA */ +/* completion, which should happen immediately following the 64th tick. */ +/* */ +/*****************************************************************************/ +static void ibmcsi3k_synchronize(struct ibmcsi3k_state *s) { + unsigned int timeout; + + IBMCSI_WRITE(CSI0_ER, 0); + IBMCSI_WRITE(CSI0_CFG, 0); + + /* Clear CSI overrun / underrun */ + IBMCSI_WRITE(CSI0_SR, CSI_SR_TOD | CSI_SR_ROD ); + + /* Disable channels */ + mtdcr(IBMCSI_TXDMA_CR, 0); + mtdcr(IBMCSI_RXDMA_CR, 0); + + /* Clear Terminal Count etc. */ + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_TXDMA) | + DCRN_DMA_SR_ALL(IBMCSI_RXDMA) ); + + mtdcr(IBMCSI_RXDMA_SA, CSI0_RBUF); + mtdcr(IBMCSI_RXDMA_DA, (unsigned) s->dma_read_line + 64); + + mtdcr(IBMCSI_RXDMA_CT, 2); /* Do 2 reads for good measure */ + + mtdcr(IBMCSI_RXDMA_CR, IBMCSI_RXDMA_GO_NOI ); /* No interrupts */ + + IBMCSI_WRITE(CSI0_CFG, 0); /* 256 bpf mode */ + + IBMCSI_WRITE(CSI0_ER, CSI_ER_ESLOT(0) | CSI_ER_ESLOT(1) | + CSI_ER_ESLOT(2) | CSI_ER_ESLOT(3) | + CSI_ER_RXEN | CSI_ER_ECSI ); + + timeout = jiffies + HZ/50; + while( !( mfdcr(IBMCSI_DMA_SR) & DCRN_DMA_SR_CS(IBMCSI_RXDMA) ) ) { + if (jiffies > timeout) { + printk("ibmcsi3k: Error: DMA timeout\n"); + break; + } + } /* Read until DMA Channel 1 terminal count is posted or timeout. */ + +#ifdef TIMINGDEBUGPROBE /* Timing probe for debug - toggles TPC VDDX charge */ + __raw_writel(0xB2E54000, 0xef600a00); +#endif + + IBMCSI_WRITE(CSI0_ER, 0); /* Disable CSI */ + + mtdcr(IBMCSI_RXDMA_CR, 0); /* Disable Rx DMA channel */ + +} diff -urN -X bkexcl linux-2.4.22/drivers/sound/ibmcsi3k.h linuxppc-2.4/drivers/sound/ibmcsi3k.h --- linux-2.4.22/drivers/sound/ibmcsi3k.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/sound/ibmcsi3k.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,100 @@ +/* + * ibmcsi3k.h : IBM PowerPC 405LP Codec Serial Interface (CSI) + + * Si3000 voiceband codec driver + * for the 405LP evaluation board + * + * Based on various sound drivers in linux/drivers/sound, including but not limited to + * es1370.c and vwsnd.c, as well as an unfinished driver from IBM Austin Research Lab. + * + * Copyright (C) 2002 IBM Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __SI3000_INC__ +#define __SI3000_INC__ + +#define SI3000_SW_FC 0x00010000 /* Request bit for secondary FSYNC */ + +#define SI3000_CR1 1 /* Control register 1 */ +#define SI3000_CR2 2 /* Control register 2 */ +#define SI3000_PLL1D 3 /* PLL1 Divider N1 (minus 1) */ +#define SI3000_PLL1M 4 /* PLL1 Multiplier M1 (minus 1) */ +#define SI3000_RXGC1 5 /* Rx Gain Control 1 */ +#define SI3000_ADC_VC 6 /* ADC Volume Control */ +#define SI3000_DAC_VC 7 /* DAC Volume Control */ +#define SI3000_SR 8 /* Status Report */ +#define SI3000_AA 9 /* Analog Attenuation */ + +/* Macros for register access */ +#define SI3000_REG_WRITE(a,b) ( ( ((unsigned int)(a)) << 24) | \ + ( ((unsigned int)(b)) << 16 ) ) +#define SI3000_REG_READ(a) ( ( ((unsigned int)(a)) << 24 ) | 0x20000000 ) + +/* Si3000 register bit fields (byte format) */ +#define SI3000_CR1_SR 0x80 /* Software Reset */ +#define SI3000_CR1_SPD 0x10 /* Speaker Drive Power (1=on) */ +#define SI3000_CR1_LPD 0x08 /* Line Driver Power (1=on) */ +#define SI3000_CR1_HPD 0x04 /* Handset Drive Power (1=on) */ +#define SI3000_CR1_MPD 0x02 /* Mic Bias Power (1=OFF) */ +#define SI3000_CR1_CPD 0x01 /* Chip power down (1=OFF) */ + +#define SI3000_CR2_HPD 0x10 /* High Pass Filter (1=DISABLE) */ +#define SI3000_CR2_PLL 0x08 /* PLL divide by 10 (1) / 5 (0) */ +#define SI3000_CR2_DL1 0x04 /* Digital loopback 1 */ +#define SI3000_CR2_DL2 0x02 /* Digital loopback 2 */ +#define SI3000_CR2_AL 0x01 /* Analog loopback */ + +#define SI3000_RXGC1_LIG 0xC0 /* Line In Gain mask */ +#define SI3000_RXGC1_LIG_OFFSET 6 + +#define SI3000_RXGC1_LIG_20DB 0xC0 /* Line In Gain 20dB */ +#define SI3000_RXGC1_LIG_10DB 0x80 /* Line In Gain 10dB */ +#define SI3000_RXGC1_LIG_0DB 0x40 /* Line In Gain 0dB */ +#define SI3000_RXGC1_LIM 0x20 /* Line In Mute (1=mute) */ +#define SI3000_RXGC1_MIG 0x18 /* Mic In Gain mask */ +#define SI3000_RXGC1_MIG_OFFSET 3 +#define SI3000_RXGC1_MIG_30DB 0x18 /* Mic In Gain 30dB */ +#define SI3000_RXGC1_MIG_20DB 0x10 /* Mic In Gain 20dB */ +#define SI3000_RXGC1_MIG_10DB 0x08 /* Mic In Gain 10dB */ +#define SI3000_RXGC1_MIG_0DB 0x00 /* Mic In Gain 0dB */ +#define SI3000_RXGC1_MIM 0x04 /* Mic In Mute (1=mute) */ +#define SI3000_RXGC1_HIM 0x02 /* Handset Input Mute (1=mute) */ +#define SI3000_RXGC1_IIR 0x01 /* Handset Input Mute (1=mute) */ + +#define SI3000_ADC_VC_RXG 0x7C /* RX PGA Gain Control */ +#define SI3000_ADC_VC_RXG_OFFSET 2 +#define SI3000_ADC_VC_RXGC(N) (N << SI3000_ADC_VC_RXG_OFFSET) +#define SI3000_ADC_VC_LOM 0x02 /* Line Out Mute (0=mute) */ +#define SI3000_ADC_VC_LOM_OFFSET 1 +#define SI3000_ADC_VC_HOM 0x01 /* Handset Out Mute (0=mute) */ + +#define SI3000_DAC_VC_TXG 0x7C /* TX PGA Gain Control */ +#define SI3000_DAC_VC_TXG_OFFSET 2 +#define SI3000_DAC_VC_TXGC(N) (N << SI3000_DAC_VC_TXG_OFFSET) +#define SI3000_DAC_VC_SLM 0x02 /* SPKR_L Mute (0=mute) */ +#define SI3000_DAC_VC_SRM 0x01 /* SPKR_R Mute (0=mute) */ + +#define SI3000_SR_SLSC 0x80 /* SPK_L short circuit (1=short) */ +#define SI3000_SR_SRSC 0x40 /* SPK_R short circuit (1=short) */ +#define SI3000_SR_LOSC 0x20 /* Line out short circuit */ + +#define SI3000_AA_LOT 0x0C /* Line Out Attenuation */ +#define SI3000_AA_LOT_OFFSET 2 + +#define SI3000_AA_SOT 0x03 /* Speaker Out Attenuation */ + +#endif diff -urN -X bkexcl linux-2.4.22/drivers/sound/ibmcsiti.c linuxppc-2.4/drivers/sound/ibmcsiti.c --- linux-2.4.22/drivers/sound/ibmcsiti.c 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/sound/ibmcsiti.c 2003-06-04 15:21:24.000000000 +1000 @@ -0,0 +1,3265 @@ +/*****************************************************************************/ +/* + * ibmcsiti.c : IBM PPC 405LP Codec Serial Interface (CSI) + + * Texas Instruments TLV320AIC23 stereo audio codec + * for the Arctic-II reference board + * + * Based on the ibmcsiti driver for IBM PPC 405LP CSI + TLV320AIC23 codec + * + * Copyright (C) 2002 Ken Inoue and David Gibson, IBM Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Supported devices: + * /dev/dsp standard /dev/dsp device, hopefully OSS compatible + * /dev/mixer standard /dev/mixer device, hopefully OSS compatible + * + * TODOs: + * - Integration testing with ViaVoice embedded edition + * - Sampling rate is fixed at 44.1 KHz. + * - Sample format is limited to 16 bit big endian. + * (this is a deviation since an OSS DSP device is supposed to support 8 bit as default.) + * - Drain DAC/ADC + * - Fragment handling + * - MMAP support + * - Split CSI and codec drivers + * - Module parameters + * - Tune retry counts and jiffies + * - Revisit inline functions + * - Write ibmcsi.txt in the Documentation directory + * - Anything else? + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "tlv320aic23.h" + +/*****************************************************************************/ +/* Start #defines that might belong in ibm405lp.h */ + +#define CSI0_IO_BASE 0xEF600900 /* CSI I/O base address */ + +#define CSI0_ER (CSI0_IO_BASE + 0) /* Enable register */ +#define CSI0_CFG (CSI0_IO_BASE + 4) /* Configuration register */ +#define CSI0_SR (CSI0_IO_BASE + 8) /* Status register */ +#define CSI0_TBUF (CSI0_IO_BASE + 0x0C) /* Transmit buffer */ +#define CSI0_RBUF (CSI0_IO_BASE + 0x10) /* Receive buffer */ +#define CSI0_ID (CSI0_IO_BASE + 0x14) /* ID */ +#define CSI0_SCR (CSI0_IO_BASE + 0x18) /* Sleep control register */ + +#define CSI0_EXTENT 28 /* I/O address extent */ +#define CSI_ID_405LP 0x1107 /* CSI core ID (halfword) */ + + +#define CSI_ER_ECSI 0x00000001 /* Enable the CSI */ +#define CSI_ER_RXEN 0x00000002 /* Receive enable */ +#define CSI_ER_TXEN 0x00000004 /* Transmit enable */ + +#define CSI_ER_ESLOT(n) (0x80000000 >> (n)) /* Enable (n)th slot */ + +#define CSI_SR_TOD 0x00000001 /* Transmit overrun */ +#define CSI_SR_ROD 0x00000002 /* Receive overrun */ +#define CSI_SR_CSIB 0x00008000 /* CSI Busy */ + +/************************************************************************/ +/* DMA Channel Control register */ +/* (DCRN_DMA0_CR0 through DCRN_DMA0_CR3) */ +/************************************************************************/ + +#define DCRN_DMA_CR_CE 0x80000000 /* Channel Enable */ +#define DCRN_DMA_CR_CIE 0x40000000 /* Channel Interrupt Enable */ +#define DCRN_DMA_CR_TD 0x20000000 /* Transfer Direction */ +#define DCRN_DMA_CR_PL 0x10000000 /* Peripheral Location */ +#define DCRN_DMA_CR_PW_8 0x00000000 /* 8 bit peripheral width */ +#define DCRN_DMA_CR_PW_16 0x04000000 /* 16 bit peripheral width */ +#define DCRN_DMA_CR_PW_32 0x08000000 /* 32 bit peripheral width */ +#define DCRN_DMA_CR_PW_64 0x0c000000 /* 64 bit peripheral width */ +#define DCRN_DMA_CR_DAI 0x02000000 /* Destination Addr Increment */ +#define DCRN_DMA_CR_SAI 0x01000000 /* Source Address Increment */ +#define DCRN_DMA_CR_BE 0x00800000 /* Buffer Enable */ +#define DCRN_DMA_CR_TM_BUFFERED 0x00000000 /* Buffered Transfer mode */ +#define DCRN_DMA_CR_TM_SW_MEM_TO_MEM 0x00400000 /* Software started mem to mem */ +#define DCRN_DMA_CR_TM_HW_MEM_TO_MEM 0x00600000 /* Hardware paced mem to mem */ +#define DCRN_DMA_CR_PSC_0 0x00000000 /* 0 Peripheral Setup Cycles */ +#define DCRN_DMA_CR_PSC_1 0x00080000 /* 1 Peripheral Setup Cycles */ +#define DCRN_DMA_CR_PSC_2 0x00100000 /* 2 Peripheral Setup Cycles */ +#define DCRN_DMA_CR_PSC_3 0x00180000 /* 3 Peripheral Setup Cycles */ +#define DCRN_DMA_CR_PSC(n) (((n)&0x3)<<19) /* n Peripheral setup cycles */ +#define DCRN_DMA_CR_PWC(n) (((n)&0x3f)<<13) /* n peripheral wait cycles */ +#define DCRN_DMA_CR_PHC(n) (((n)&0x7)<<10) /* n peripheral hold cycles */ +#define DCRN_DMA_CR_ETD 0x00000200 /* EOT/TC Pin Direction */ +#define DCRN_DMA_CR_TCE 0x00000100 /* Terminal Count Enable */ +#define DCRN_DMA_CR_CP_MASK 0x000000C0 /* Channel Priority */ +#define DCRN_DMA_CR_CP(n) (((n)&0x3)<<6) +#define DCRN_DMA_CR_PF 0x00000030 /* Memory read prefetch trans */ +#define DCRN_DMA_CR_PF_1 0x00000000 /* Prefetch 1 dword */ +#define DCRN_DMA_CR_PF_2 0x00000010 /* Prefetch 2 dword */ +#define DCRN_DMA_CR_PF_4 0x00000020 /* Prefetch 4 dword */ +#define DCRN_DMA_CR_PCE 0x00000008 /* Parity check enable */ +#define DCRN_DMA_CR_DEC 0x00000004 /* Address decrement */ + +/************************************************************************/ +/* DMA Status Register */ +/* (Device Control Register bus register DCRN_DMASR) */ +/************************************************************************/ +/* (n) = DMA channel number, 0-3 */ + +#define DCRN_DMA_SR_CS(n) (0x80000000 >>(n)) /* Terminal count status */ +#define DCRN_DMA_SR_TS(n) (0x08000000 >>(n)) /* End Of Transfer status */ +#define DCRN_DMA_SR_RI(n) (0x00800000 >>(n)) /* Error status */ +#define DCRN_DMA_SR_IR(n) (0x00080000 >>(n)) /* Internal DMA request pending */ +#define DCRN_DMA_SR_ER(n) (0x00008000 >>(n)) /* External DMA request pending */ +#define DCRN_DMA_SR_CB(n) (0x00000800 >>(n)) /* Channel Busy */ +#define DCRN_DMA_SR_SG(n) (0x00000080 >>(n)) /* Scatter/gather status */ + +/* Status register bits for the (n)th channel (write to clear) */ +#define DCRN_DMA_SR_ALL(n) (DCRN_DMA_SR_CS(n) | \ + DCRN_DMA_SR_TS(n) | \ + DCRN_DMA_SR_RI(n) | \ + DCRN_DMA_SR_IR(n) | \ + DCRN_DMA_SR_ER(n) | \ + DCRN_DMA_SR_CB(n) | \ + DCRN_DMA_SR_SG(n) ) + +/* DCRN_DMA0_SGC Scatter/Gather Command Register bits */ +#define DCRN_DMA_SGC_SSG0 0x80000000 +#define DCRN_DMA_SGC_SSG1 0x40000000 +#define DCRN_DMA_SGC_SSG2 0x20000000 +#define DCRN_DMA_SGC_SSG3 0x10000000 + +#define DCRN_DMA_SGC_EM0 0x00008000 +#define DCRN_DMA_SGC_EM1 0x00004000 +#define DCRN_DMA_SGC_EM2 0x00002000 +#define DCRN_DMA_SGC_EM3 0x00001000 + +struct dma_sgdt { /* Must be word aligned */ + u32 ccw; /* Channel Control Word */ + u32 srcP; /* Source address (physical) */ + u32 destP; /* Destination address (physical) */ + u32 ctrl; /* MSB = link bit, lower halfword = count */ + /* Other 3 bits unused */ + u32 nextP; /* Next scatter/gather descriptor list physical address */ + /* ------------------------------------- Private use ---------------*/ + struct dma_sgdt *prevV; /* Prev scatter/gather descriptor list virtual address */ + struct dma_sgdt *nextV; /* Next */ + unsigned int dummy; /* Reserved (for 16 byte alignment) */ +}; + +/* End ibm405lp.h candidates */ + + +/*****************************************************************************/ +/* Driver specific defines */ +/*****************************************************************************/ + +/* The DMA channels for the CSI are hardcoded in the 405LP chip, so we + * hardcode them. If a future chip adopts programmable channel + * assignment, I expect access to DMA channels would be handled by a + * separate driver. + */ + +#define IBMCSI_TXDMA 0 /* Transmit from CSI to codec : channel 0 */ +#define IBMCSI_RXDMA 1 /* Receive from codec to CSI : channel 1 */ + +#define IBMCSI_TXDMA_IRQ 5 +#define IBMCSI_RXDMA_IRQ 6 + +#define IBMCSI_DMA_SR DCRN_DMASR + +/* Transmit (playback) DMA registers */ +#define IBMCSI_TXDMA_CR DCRN_DMACR0 +#define IBMCSI_TXDMA_DA DCRN_DMADA0 +#define IBMCSI_TXDMA_SA DCRN_DMASA0 +#define IBMCSI_TXDMA_CT DCRN_DMACT0 + +/* Receive (capture) DMA registers */ +#define IBMCSI_RXDMA_CR DCRN_DMACR1 +#define IBMCSI_RXDMA_DA DCRN_DMADA1 +#define IBMCSI_RXDMA_SA DCRN_DMASA1 +#define IBMCSI_RXDMA_CT DCRN_DMACT1 + + +#define IBMCSI_TXDMA_CONFIG ( /* Channel disabled */ \ + DCRN_DMA_CR_CIE | /* Channel interrupt enabled */ \ + /* Memory to peripheral */ \ + DCRN_DMA_CR_PL | /* Peripheral on OPB */ \ + DCRN_DMA_CR_PW_32 | /* 32 bit wide peripheral */ \ + /* Dest address not incremented */ \ + DCRN_DMA_CR_SAI | /* Source address incremented */ \ + /* Peripheral transfer mode */ \ + /* Peripheral setup cycle 0 */ \ + DCRN_DMA_CR_PWC(2) | /* Peripheral wait cycle 3 */ \ + /* Peripheral hold cycle 0 */ \ + DCRN_DMA_CR_ETD | /* EOTn = TC */ \ + DCRN_DMA_CR_TCE ) /* Terminal count enable */ + +#define IBMCSI_TXDMA_GO (IBMCSI_TXDMA_CONFIG | DCRN_DMA_CR_CE) /* For int */ +#define IBMCSI_TXDMA_GO_NOI (IBMCSI_TXDMA_GO & ~DCRN_DMA_CR_CIE) /* For polling */ + +#define IBMCSI_RXDMA_CONFIG ( /* Channel disabled */ \ + DCRN_DMA_CR_CIE | /* Channel interrupt enabled */ \ + DCRN_DMA_CR_TD | /* Peripheral to memory */ \ + DCRN_DMA_CR_PL | /* Peripheral on OPB */ \ + DCRN_DMA_CR_PW_32 | /* 32 bit wide peripheral */ \ + DCRN_DMA_CR_DAI | /* Dest address incremented */ \ + /* Source address not incremented */ \ + /* Peripheral transfer mode */ \ + /* Peripheral setup cycle 0 */ \ + DCRN_DMA_CR_PWC(2) | /* Peripheral wait cycle 3 */ \ + /* Peripheral hold cycle 0 */ \ + DCRN_DMA_CR_ETD | /* EOTn = TC */ \ + DCRN_DMA_CR_TCE ) /* Terminal count enable */ + +#define IBMCSI_RXDMA_GO (IBMCSI_RXDMA_CONFIG | DCRN_DMA_CR_CE) +#define IBMCSI_RXDMA_GO_NOI (IBMCSI_RXDMA_GO & ~DCRN_DMA_CR_CIE) + +#define IBMCSI_DEFAULT_SAMPLING_RATE 44100 + +#define IBMCSI_TI_CFG 0x00400010 /* 64 bits per frame mode */ + +#define DAC_TIMER_PERIOD (HZ/50) +#define ADC_TIMER_PERIOD (HZ/50) + +#define TX_SG DCRN_ASG0 +#define RX_SG DCRN_ASG1 + +#define TX_SG_ENABLE DCRN_DMA_SGC_SSG0 +#define TX_SG_MASK DCRN_DMA_SGC_EM0 + +#define RX_SG_ENABLE DCRN_DMA_SGC_SSG1 +#define RX_SG_MASK DCRN_DMA_SGC_EM1 + + +/*****************************************************************************/ + +#undef OSS_DOCUMENTED_MIXER_SEMANTICS /* FIXME: does this have any effect? */ + +#define DBG(x) {} +/*#define DBG(x) {x}*/ + +#define IBMCSI_MAGIC 0xB31BCB /* Copied from the Austin Research version */ +/* TODO: verify this value is legitimate */ + +#define DMABUF_DEFAULTORDER (16-PAGE_SHIFT) /* FIXME: check out this value */ +#define DMABUF_MINORDER 1 /* FIXME: ditto */ + +#define IBMCSI_WRITE(reg, val) (__raw_writel(val, reg)) +#define IBMCSI_READ(reg) (__raw_readl(reg)) + +#define VALIDATE_STATE(s) \ +({ \ + if (!(s) || (s)->magic != IBMCSI_MAGIC) { \ + printk(KERN_ERR "ibmcsi: invalid signature (magic) in private data\n");\ + return -ENXIO; \ + } \ +}) + + +/*****************************************************************************/ +/* Static variables, globals and structs */ +/*****************************************************************************/ + +static const unsigned sample_shift[] = {0, 1, 1, 2}; +static LIST_HEAD(devs); + +/* + * Private data structure for the devices supported by this driver + */ +struct ibmcsiti_state { + unsigned int magic; /* Magic signature value for sanity check */ + + unsigned int state; /* Driver state (DAC/ADC running, Halt, etc.) */ + + struct i2c_client *i2c; + + /* For multi-device support; not used for the 405LP */ + struct list_head devs; + + /* DSP device variables */ + int dev_dsp; /* unit number of our registered DSP device */ + int outstereo; /* are we in stereo output mode? */ + + spinlock_t lock; + + struct semaphore dsp_sem; + struct semaphore open_sem; + + mode_t open_mode; + wait_queue_head_t open_wait; + + /* Mixer device variables */ + int dev_mixer; /* unit number of our registered mixer device */ + struct semaphore mix_sem; + + /* Buffers */ + unsigned char *write_line; + dma_addr_t dma_write_line; + unsigned char *read_line; + dma_addr_t dma_read_line; + + /* Control blocks for audio playback (dac) and capture (adc) */ + struct dmabuf { + /* The important ones... */ + void *rawbuf; /* DMA buffer logical address */ + dma_addr_t dmaaddr; /* DMA buffer physical address */ + unsigned hwptr, swptr; /* Offsets from rawbuf for data. HWPTR = DMAC, SWPTR = driver */ + int count; + + wait_queue_head_t wait; + + /* And the rest, inherited from sample drivers... */ + unsigned fragsize; + unsigned dmasize; + unsigned fragsamples; + + unsigned buforder; + unsigned numfrag; + unsigned fragshift; + + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned endcleared:1; + unsigned enabled:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; + + unsigned total_bytes; + unsigned error; /* Over/underrun */ + unsigned sg_count; + } dma_dac, dma_adc; + + /* FIXME: should move the following to dma_dac/dma_adc (trivial) */ + struct timer_list dac_timer; + struct timer_list adc_timer; + struct dma_sgdt *dac_free_sgdt_q; + struct dma_sgdt *adc_free_sgdt_q; + struct dma_sgdt *dac_active_sgdt_q; + struct dma_sgdt *adc_active_sgdt_q; + + struct dma_sgdt *dac_sgdt_lastV; /* Anchors */ + struct dma_sgdt *adc_sgdt_lastV; + + struct dma_sgdt *adc_hw_prev_sgdt; + struct dma_sgdt *adc_sw_prev_sgdt; + + /* Local copy of TI codec settings (the registers being write only) */ + u16 codec_reg[TLV320_REG_EXTENT]; /* the registers are + * actually 9-bits each */ +}; + +/* Driver state flags */ +#define IBMCSI_DAC_RUNNING 0x00010000 +#define IBMCSI_ADC_RUNNING 0x00020000 +#define IBMCSI_HALT 0x00040000 + +/************************************************************************/ +/* Misc function prototypes */ +/************************************************************************/ + +static int __init init_ibmcsiti(void); /* Driver initialization */ +static void __exit cleanup_ibmcsiti(void); /* Driver exit cleanup */ + +static int ibmcsi_i2c_attach_adapter(struct i2c_adapter *); +static int ibmcsi_i2c_detach_client(struct i2c_client *); +static int ibmcsi_i2c_detect_client(struct i2c_adapter *, int address, + unsigned short flags, int kind); +static void ibmcsi_i2c_inc_use(struct i2c_client *); +static void ibmcsi_i2c_dec_use(struct i2c_client *); + +/************************************************************************/ +/* DSP driver function prototypes */ +/************************************************************************/ + +/* Top level */ +static int ibmcsiti_dsp_open(struct inode *inode, struct file *file); +static ssize_t ibmcsiti_dsp_read(struct file *file, char *buffer, + size_t count, loff_t *ppos); +static ssize_t ibmcsiti_dsp_write(struct file *file, const char *buffer, + size_t count, loff_t *ppos); +static unsigned int ibmcsiti_dsp_poll(struct file *file, + struct poll_table_struct *wait); +static int ibmcsiti_dsp_release(struct inode *inode, struct file *file); +static int ibmcsiti_dsp_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +/* Interrupt handlers */ +static void ibmcsiti_dac_interrupt(int irq, void *dev_id, + struct pt_regs *regs); +static void ibmcsiti_adc_interrupt(int irq, void *dev_id, + struct pt_regs *regs); +static void ibmcsi_adc_timer(unsigned long param); +static void ibmcsi_dac_timer(unsigned long param); + +/* Utility routines */ +static unsigned long copy_samples_to_user(char *dest, const char *src, + unsigned nsamples, int stereo); +static unsigned long copy_samples_from_user(char *dest, const char *src, + unsigned nsamples, int stereo); + +static void start_adc(struct ibmcsiti_state *s); +static void start_dac(struct ibmcsiti_state *s); +static int drain_dac(struct ibmcsiti_state *s, int nonblock); +static inline void stop_adc(struct ibmcsiti_state *s); +static inline void stop_dac(struct ibmcsiti_state *s); + +static int ibmcsi_stop_csi_sync(void); + +static inline void dealloc_dmabuf(struct ibmcsiti_state *s, + struct dmabuf *buf); +static int prog_dmabuf(struct ibmcsiti_state *s, struct dmabuf *buf, + unsigned rate, unsigned fmt,unsigned adc_init); +static inline int prog_dmabuf_adc(struct ibmcsiti_state *s); +static inline int prog_dmabuf_dac(struct ibmcsiti_state *s); + +static inline unsigned get_hwptr(struct ibmcsiti_state *s, struct dmabuf *buf, + unsigned channel); +static void ibmcsiti_update_ptr(struct ibmcsiti_state *s); + +static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, + unsigned len, unsigned char c); + +static inline unsigned ld2(unsigned int x); + +/* Scatter/Gather descriptor table maintenance routines */ + +static void init_sgdt_q(struct dma_sgdt *queue, int count); +static struct dma_sgdt *get_sgdt(struct dma_sgdt **queueaddress); +static void free_sgdt(struct dma_sgdt **queueaddress, struct dma_sgdt *dt); +static unsigned int check_sgdt_range(struct ibmcsiti_state *s, + struct dma_sgdt* dt, int count); + +/************************************************************************/ +/* Mixer driver function prototypes */ +/************************************************************************/ + +/* Top level */ +static int ibmcsiti_mixer_open(struct inode *inode, struct file *file); +static int ibmcsiti_mixer_ioctl(struct inode *ioctl, struct file *file, + unsigned int cmd, unsigned long arg); +static int ibmcsiti_mixer_release(struct inode *inode, struct file *file); + +static int mixer_read_ioctl(struct ibmcsiti_state *s, unsigned int nr, + caddr_t arg); +static int mixer_write_ioctl(struct ibmcsiti_state *s, unsigned int nr, + caddr_t arg); + +/* Mixer read routines */ +static int ibmcsiti_get_volume(struct ibmcsiti_state *s) ; +static int ibmcsiti_get_line(struct ibmcsiti_state *s) ; +static int ibmcsiti_get_mic(struct ibmcsiti_state *s) ; +static int ibmcsiti_get_recsrc(struct ibmcsiti_state *s) ; +static int ibmcsiti_get_outsrc(struct ibmcsiti_state *s) ; + +/* Mixer write routines */ +static int ibmcsiti_set_volume(struct ibmcsiti_state *s, int val); +static int ibmcsiti_set_line(struct ibmcsiti_state *s, int val) ; +static int ibmcsiti_set_mic(struct ibmcsiti_state *s, int val) ; +static int ibmcsiti_set_recsrc(struct ibmcsiti_state *s, int val) ; + +/************************************************************************/ +/* Helper functions */ +/************************************************************************/ + +/* TLV320AIC23 control register access routines */ +static u16 tlv320_read_reg(struct ibmcsiti_state *s, int reg); +static void tlv320_write_reg(struct ibmcsiti_state *s, int reg, + u16 val); + +/************************************************************************/ + +MODULE_AUTHOR("David Gibson"); +MODULE_DESCRIPTION("IBM PPC 405LP CSI / TI TLV320AIC23 Audio Driver"); + +/*****************************************************************************/ +/* Initialization tables */ +/*****************************************************************************/ + +static /*const*/ struct file_operations ibmcsiti_audio_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = ibmcsiti_dsp_read, + .write = ibmcsiti_dsp_write, + .poll = ibmcsiti_dsp_poll, + .ioctl = ibmcsiti_dsp_ioctl, + .mmap = NULL, + .open = ibmcsiti_dsp_open, + .release = ibmcsiti_dsp_release, +}; + +static /*const*/ struct file_operations ibmcsiti_mixer_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .ioctl = ibmcsiti_mixer_ioctl, + .open = ibmcsiti_mixer_open, + .release = ibmcsiti_mixer_release, +}; + +static struct i2c_driver ibmcsiti_i2c_driver = { + .name = "IBMCSI+TLV320AIC23 codec", + .id = 0xf314, + .flags = I2C_DF_NOTIFY, + .attach_adapter = ibmcsi_i2c_attach_adapter, + .detach_client = ibmcsi_i2c_detach_client, + .command = NULL, + .inc_use = ibmcsi_i2c_inc_use, + .dec_use = ibmcsi_i2c_dec_use, +}; + +typedef int (*PIF) (struct ibmcsiti_state *, int); + +static struct inittable { + PIF proc; + int val; +} inittable[] __initdata = { + {ibmcsiti_set_recsrc, SOUND_MASK_MIC}, + {ibmcsiti_set_volume, 100 * 35/47}, + {ibmcsiti_set_line, 100}, + {ibmcsiti_set_mic, 100}, +}; + +/*****************************************************************************/ +/* Driver initialization and cleanup */ +/*****************************************************************************/ +static int __init init_ibmcsiti(void) +{ + int ret; + + /* We just register the I2C driver here. The initialization + * of the device proper happens when the I2C layer calls us + * back. */ + printk(KERN_INFO "ibmcsiti: compiled at " __TIME__ " " __DATE__ "\n"); + + ret = i2c_add_driver(&ibmcsiti_i2c_driver); + if (ret) + printk(KERN_ERR "ibmcsiti: i2c_add_driver() failed\n"); + + return ret; +} + +static int ibmcsiti_i2c_init(struct i2c_client *client) +{ + struct ibmcsiti_state *s = client->data; + int ret; + int i; + unsigned long end_time; + void *codec_lines_virt; + dma_addr_t codec_lines; + + printk("ibmcsiti_i2c_init(): addr=0x%x\n", client->addr); + + /* Power up the CSI core during initialization */ + ppc4xx_cpm_fr(IBM_CPM_CSI, 0); + + /* Allocate line buffers */ + codec_lines_virt = consistent_alloc(GFP_DMA, PAGE_SIZE, &codec_lines); + if (! codec_lines_virt) { + printk(KERN_ERR "ibmcsiti: out of memory\n"); + /* FIXME we should turn the CSI off again if init fails */ + ret = -ENOMEM; + goto err_linebuffer; + } + + if (! request_mem_region(CSI0_IO_BASE, CSI0_EXTENT, "ibmcsiti")) { + printk(KERN_ERR "ibmcsiti: I/O ports %x-%x in use\n", + CSI0_IO_BASE, CSI0_EXTENT-1); + return -EBUSY; + } + + /* Check CSI core presence */ + if ( (IBMCSI_READ(CSI0_ID) >> 16) != CSI_ID_405LP) { + printk(KERN_WARNING "ibmcsiti: Unexpected CSI ID %x\n", + IBMCSI_READ(CSI0_ID)); + ret = -ENODEV; + goto err_irq1; + } + + memset(s, 0, sizeof(struct ibmcsiti_state)); + s->i2c = client; + s->magic = IBMCSI_MAGIC; + + init_waitqueue_head(&s->dma_adc.wait); + init_waitqueue_head(&s->dma_dac.wait); + init_waitqueue_head(&s->open_wait); + init_MUTEX(&s->open_sem); + spin_lock_init(&s->lock); + + s->write_line = codec_lines_virt; + s->dma_write_line = codec_lines; + s->read_line = (void *)((char *)codec_lines_virt + 128); + s->dma_read_line = codec_lines + 128; + + /* Interrupts - currently held indefinitely since no one else + * will be using them. Mixer and DSP use the same + * interrupts. */ + + ret = request_irq(IBMCSI_TXDMA_IRQ, ibmcsiti_dac_interrupt, + SA_SHIRQ, "ibmcsiti", s); + if (ret != 0) { + printk(KERN_ERR "ibmcsiti: IRQ %d in use\n", IBMCSI_TXDMA_IRQ); + goto err_irq1; + } + ret = request_irq(IBMCSI_RXDMA_IRQ, ibmcsiti_adc_interrupt, + SA_SHIRQ, "ibmcsiti", s); + if (ret != 0) { + printk(KERN_ERR "ibmcsiti: IRQ %d in use\n",IBMCSI_RXDMA_IRQ); + goto err_irq2; + } + +#if 0 /* FIXME: CSI error interrupts not implemented */ + ret=request_irq(21, ibmcsiti_csi_interrupt, SA_SHIRQ, "ibmcsiti",s); + if (ret) { + printk(KERN_ERR "ibmcsiti: irq 21 in use\n"); + goto err_irq; + } +#endif + + /* register devices */ + ret = s->dev_dsp = register_sound_dsp(&ibmcsiti_audio_fops, -1); + if (ret < 0) { + printk(KERN_ERR "ibmcsiti: Could not register DSP.\n"); + goto err_dev1; + } + + ret = s->dev_mixer = register_sound_mixer(&ibmcsiti_mixer_fops, -1); + if (ret < 0) { + printk(KERN_ERR "ibmcsiti: Could not register mixer.\n"); + goto err_dev2; + } + + /* Initialize the cores / chip: DMA controller, CSI and codec.*/ + /* Turn on the DMA controller */ +#if 0 /* Unnecessary - Bishop */ + end_time = jiffies + HZ/10; + mtdcr(DCRN_CPC0_FR, mfdcr(DCRN_CPC0_FR) & (~CPM_DMA)); + + while (! (mfdcr(DCRN_CPC0_SR) & CPM_DMA) ) { /* DMAC asleep? */ + mfdcr(IBMCSI_DMA_SR); /* Dummy read to wake it up */ + mtdcr(DCRN_CPC0_FR, mfdcr(DCRN_CPC0_FR) & (~CPM_DMA)); /* Turn on DMA Controller */ + asm("isync"); + if (jiffies > end_time) { + printk(KERN_ERR "ibmcsiti: cannot turn on DMA controller \n"); + goto err_dev3; + } + } + + /* Caveat : If you don't do the above and the DMA controller + happens to be turned off, the controller will still respond + to DCR reads/writes and even change state to DRQ Pending / + Channel Active when you try to kick off DMA, but will + adamantly refuse to do any real work. This can happen if the + kernel is configured without PPC405 DMA support, or if a + power management driver / daemon tried to save power. We + therefore power up the DMA controller before doing anything + with it and codec. */ +#endif + + IBMCSI_WRITE(CSI0_ER,0); /* Stop CSI */ + + /* Set up TI codec via direct I2C manipulation. */ + /* The control registers are write-only, so we have to init + * them to a known state. */ + { + int i2c_error = 0; + u16 i2c_data[] = { + TLV320_REG_WRITE(TLV320_RR, 0x0000), /* 0x1e00: Reset chip */ + TLV320_REG_WRITE(TLV320_DIA, 0x0000), /* 0x1200: Deactivate digital interface */ + TLV320_REG_WRITE(TLV320_DAI, 0x0053), /* 0x0e53: Master, MSB on 2nd BCLK, DSP format */ + TLV320_REG_WRITE(TLV320_PDC, 0x0100), /* 0x0d00: Power up everything */ + TLV320_REG_WRITE(TLV320_DIA, 0x0001), /* 0x1201: Activate digital interface */ + TLV320_REG_WRITE(TLV320_LLI, 0x0017), /* 0x0017: Left Line-In volume 0dB */ + TLV320_REG_WRITE(TLV320_RLI, 0x0017), /* 0x0217: Right Line-In volume 0dB */ + TLV320_REG_WRITE(TLV320_LCH, 0x00F9), /* 0x04f9: Left headphone volume 0dB */ + TLV320_REG_WRITE(TLV320_RCH, 0x00F9), /* 0x06f9: Right headphone volume 0dB */ + TLV320_REG_WRITE(TLV320_DAP, 0x0004) /* 0x0a04: DAC enable */ + }; + + for (i=0; i < (sizeof(i2c_data) / sizeof(i2c_data[0])); i++) { + if (i2c_master_send(client, (u8 *)(i2c_data+i), 2) != 2) { + printk(KERN_ERR "ibmcsiti: I2C write error, " + "cannot set up audio chip.\n"); + i2c_error = 1; + break; + } + udelay(1000); + /* Undocumented, but some commands + * apparently require some delay. */ + } + + } + /* End I2C code */ + + /* Initialize shadow registers */ + { + u16 codec_reg_init[TLV320_REG_EXTENT] = {0x0117, + 0x0117, + 0x01f9, + 0x01f9, + 0x0010, + 0x0004, + 0x0100, + 0x0053, + 0x0020, + 0x0001,}; + + for (i=0; icodec_reg[i] = codec_reg_init[i]; + } + + /* Initialize mixer settings (see inittable[] above for + * procedures and values.*/ + for (i=0; i < (sizeof(inittable)/sizeof(inittable[0])); i++) + inittable[i].proc(s, inittable[i].val); + + /* Add to driver list */ + list_add_tail(&s->devs, &devs); + return 0; + + err_dev3: /* Error exit initializing chips */ + unregister_sound_mixer(s->dev_mixer); + err_dev2: /* Error exit registering mixer device */ + unregister_sound_dsp(s->dev_dsp); + err_dev1: + free_irq(IBMCSI_TXDMA_IRQ, s); + err_irq2: + free_irq(IBMCSI_RXDMA_IRQ, s); +#if 0 /* FIXME */ + free_irq(21,s); +#endif + err_irq1: + release_mem_region(CSI0_IO_BASE, CSI0_EXTENT); + err_linebuffer: + consistent_free(codec_lines_virt); + ppc4xx_cpm_fr(IBM_CPM_CSI, 1); /* Turn the core off again */ + + return ret; +} + +static void __exit cleanup_ibmcsiti(void) +{ + struct ibmcsiti_state *s; + static void *codec_lines_virt; + + while (devs.next != &devs) { + s = list_entry(devs.next, struct ibmcsiti_state, devs); + list_del(devs.next); + unregister_sound_mixer(s->dev_mixer); + unregister_sound_dsp(s->dev_dsp); + free_irq(IBMCSI_TXDMA_IRQ, s); + free_irq(IBMCSI_RXDMA_IRQ, s); + release_mem_region(CSI0_IO_BASE, CSI0_EXTENT); + codec_lines_virt = s->write_line; + kfree(s); + consistent_free(codec_lines_virt); + } + +} + +module_init(init_ibmcsiti); +module_exit(cleanup_ibmcsiti); + +/*****************************************************************************/ +/* I2C Callbacks */ +/*****************************************************************************/ + +/* Blech. It's not as bad as PCMCIA, but boy does the i2c + * infrastructure suck... */ + +static unsigned short normal_i2c[] = {0x1a, I2C_CLIENT_END,}; +static unsigned short dummy_i2c_addrlist[] = {I2C_CLIENT_END,}; + +static struct i2c_client_address_data ibmcsi_i2c_address_data = { + .normal_i2c = normal_i2c, + .normal_i2c_range = dummy_i2c_addrlist, + .probe = dummy_i2c_addrlist, + .probe_range = dummy_i2c_addrlist, + .ignore = dummy_i2c_addrlist, + .ignore_range = dummy_i2c_addrlist, + .force = dummy_i2c_addrlist, +}; + +static int ibmcsi_i2c_id; /* = 0 */ + +static int ibmcsi_i2c_attach_adapter(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &ibmcsi_i2c_address_data, + &ibmcsi_i2c_detect_client); +} + +static int ibmcsi_i2c_detach_client(struct i2c_client *client) +{ + printk(KERN_WARNING "ibmcsiti: ibmcsi_i2c_detach_client() called. " + "Don't know how to cope :-(\n"); + return 0; +} + +static void ibmcsi_i2c_inc_use(struct i2c_client *client) +{ + MOD_INC_USE_COUNT; +} + +static void ibmcsi_i2c_dec_use(struct i2c_client *client) +{ + MOD_DEC_USE_COUNT; +} + +static int ibmcsi_i2c_detect_client(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + int err = 0; + struct i2c_client *client; + struct ibmcsiti_state *data; + + printk("ibmcsi_i2c_detect_client(): addr=%x\n", addr); + + if (! i2c_check_functionality(adap, I2C_FUNC_I2C)) { + printk(KERN_ERR "ibmcsiti: I2C bus lacks functionality\n"); + return -ENODEV; + } + + /* FIXME: Check the I2C adapter is the one we expect? */ + + /* This device is write-only, and hence undetectable. We just + * assume we've been called with the right address... (did I + * mention the i2c infrastructure was crap?) */ + + client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct ibmcsiti_state), GFP_KERNEL); + + if (! client) + return -ENOMEM; + + /* This is tricky, but it will set the data to the right value. */ + client->data = client + 1; + data = (struct ibmcsiti_state *)client->data; + + client->addr = addr; + client->adapter = adap; + client->driver = &ibmcsiti_i2c_driver; + client->flags = 0; + strcpy(client->name, "Arctic-2 codec"); + client->id = ibmcsi_i2c_id++; /* FIXME: Racy? */ + + /* Tell the i2c layer a new client has arrived */ + err = i2c_attach_client(client); + if (err) { + printk(KERN_ERR "ibmcsiti: Could not attach I2C client\n"); + goto fail; + } + + /* Now set up the device */ + err = ibmcsiti_i2c_init(client); + if (err) { + i2c_detach_client(client); /* FIXME: ugly, do better */ + goto fail; + } + + return 0; + + fail: + if (client) + kfree(client); + + return err; +} + +/*****************************************************************************/ +/* Part I : DSP device /dev/dsp */ +/*****************************************************************************/ + + +static int ibmcsiti_dsp_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + struct list_head *list; + struct ibmcsiti_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct ibmcsiti_state, devs); + if (!((s->dev_dsp ^ minor) & ~0xf)) + break; + } + + VALIDATE_STATE(s); + + file->private_data = s; + + /* wait for device to become free */ + down(&s->open_sem); + while (s->open_mode & file->f_mode) { + if (file->f_flags & O_NONBLOCK) { + up(&s->open_sem); + return -EBUSY; + } + add_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + up(&s->open_sem); + schedule(); + remove_wait_queue(&s->open_wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + down(&s->open_sem); + } + s->open_mode |= (file->f_mode & (FMODE_READ | FMODE_WRITE)); + spin_lock_irqsave(&s->lock, flags); + + /* FIXME: sampling rate hardcoded */ + + { + struct dma_sgdt *dacp, *adcp; + +#define SGDT_COUNT ((PAGE_SIZE/2)/(sizeof(struct dma_sgdt))) + + if (file->f_mode & FMODE_WRITE) { + s->dac_free_sgdt_q = (struct dma_sgdt *)(s->write_line); + init_sgdt_q(s->dac_free_sgdt_q, SGDT_COUNT); + /* Prepare default (anchor) descriptor for DAC */ + dacp = s->dac_active_sgdt_q = get_sgdt(&(s->dac_free_sgdt_q)); + + dacp->ccw = (unsigned int)IBMCSI_TXDMA_GO_NOI; + + dacp->nextP = virt_to_phys(dacp); /* Loop in on itself */ + + dacp->srcP = virt_to_phys(&(dacp->dummy)); /* Point to dummy data */ + dacp->destP = CSI0_TBUF; + dacp->dummy = 0; /* Dummy neutral data (FIXME: currently assumes signed 16 format) */ + dacp->ctrl = 0x80000001; /* Link, count = 1 */ + s->dac_sgdt_lastV = dacp; + printk("ibmcsi: s = %p dac free_sgdt_q = %p at %p lastV = %p at %p\n", + s, s->dac_free_sgdt_q, &s->dac_free_sgdt_q, s->dac_sgdt_lastV, + &s->dac_sgdt_lastV); + } + + if (file->f_mode & FMODE_READ) { + s->adc_free_sgdt_q = (struct dma_sgdt *)(s->write_line) + SGDT_COUNT; + init_sgdt_q(s->adc_free_sgdt_q, SGDT_COUNT); + +#if 0 + /* Prepare default (anchor) descriptor for ADC */ + adcp = s->adc_active_sgdt_q = get_sgdt(&(s->adc_free_sgdt_q)); + adcp->ccw = (unsigned int)IBMCSI_RXDMA_GO_NOI; + adcp->nextP = virt_to_phys(adcp); + adcp->ctrl = 0x80000001; + + adcp->srcP = CSI0_RBUF; + adcp->destP = virt_to_phys(&(adcp->dummy)); + + s->adc_sgdt_lastV = adcp; +#endif + printk("ibmcsi: s = %p adc free_sgdt_q = %p at %p lastV = %p at %p\n", + s, s->adc_free_sgdt_q, &s->adc_free_sgdt_q, s->adc_sgdt_lastV, + &s->adc_sgdt_lastV); + } + + + } + + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags = s->dma_adc.subdivision = 0; + s->dma_adc.enabled = 1; + /* FIXME: data format hardcoded */ + + if ( !(IBMCSI_READ(CSI0_ER) & 1) ) { /* CSI currently not in use */ + IBMCSI_WRITE(CSI0_ER, 0); /* Clear enable reg, disable CSI */ + IBMCSI_WRITE(CSI0_SR, CSI_SR_TOD | CSI_SR_ROD); /* Clear CSI errors */ + mtdcr(IBMCSI_RXDMA_CR, 0); /* Disable DMA Rx */ + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_RXDMA) | + DCRN_DMA_SR_ALL(IBMCSI_TXDMA) ) ; + /* Clear DMA status bits */ + } /* If currently active, assume it's working OK */ + + init_timer(&s->adc_timer); + s->adc_timer.function = ibmcsi_adc_timer; + s->adc_timer.data = (unsigned long)s; + s->adc_timer.expires = jiffies + ADC_TIMER_PERIOD; + add_timer(&s->adc_timer); + } + + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags + = s->dma_dac.subdivision = 0; + s->dma_dac.enabled = 1; + + /* FIXME: data format hardcoded */ + + s->dma_dac.hwptr = 0; + s->dma_dac.swptr = 0; + s->dma_dac.count = 0; + s->dma_dac.sg_count = 0; + + if ( !(IBMCSI_READ(CSI0_ER) & 1) ) { /* CSI currently not in use */ + IBMCSI_WRITE(CSI0_ER, 0); /* Clear enable reg, disable CSI */ + IBMCSI_WRITE(CSI0_SR, CSI_SR_TOD | CSI_SR_ROD); /* Clear CSI errors */ + mtdcr(IBMCSI_TXDMA_CR, 0); /* Disable DMA Tx */ + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_RXDMA) | + DCRN_DMA_SR_ALL(IBMCSI_TXDMA) ) ; + /* Clear DMA status bits */ + } + + init_timer(&s->dac_timer); + s->dac_timer.function = ibmcsi_dac_timer; + s->dac_timer.data = (unsigned long)s; + s->dac_timer.expires = jiffies + DAC_TIMER_PERIOD; + add_timer(&s->dac_timer); + + DBG(printk("ibmcsi: jiffies %d HZ %d timer %d\n", jiffies, HZ, + s->dac_timer.expires)); + } + + spin_unlock_irqrestore(&s->lock, flags); + up(&s->open_sem); + init_MUTEX(&s->dsp_sem); + + return 0; +} + +static ssize_t ibmcsiti_dsp_read(struct file *file, char *buffer, + size_t count, loff_t *ppos) +{ + struct ibmcsiti_state *s = (struct ibmcsiti_state *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + int cnt; + + VALIDATE_STATE(s); + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_adc.mapped) + return -ENXIO; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + + down(&s->dsp_sem); + + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + goto out; + + add_wait_queue(&s->dma_adc.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&s->lock, flags); + + swptr = s->dma_adc.swptr; + /* Record curent sgdt for this snapshot */ + +#if 0 + if (s->dma_adc.hwptr >= s->dma_adc.swptr) + cnt = s->dma_adc.hwptr - swptr; + else /* HWPTR wrapped */ + cnt = s->dma_adc.dmasize - swptr; + + if (s->dma_adc.count < cnt) + cnt = s->dma_adc.count; + + if (cnt <= 0) + set_current_state(TASK_INTERRUPTIBLE); + + if (cnt > count*2) /* cnt is raw (4 bytes per sample), + * count is cooked (2 bytes per + * sample) */ + cnt = count * 2; + +#else /* Change to handle multiple interrupts before a successful read */ + + cnt = s->dma_adc.count; /* Use the count accumulated + * by interrupt handler */ + + /* cnt is raw (4 bytes per sample), count is cooked (2 + * bytes per sample) */ + if (cnt > count*2) + cnt = count * 2; + + if (cnt + swptr > s->dma_adc.dmasize) + cnt = s->dma_adc.dmasize - swptr; + + if (cnt > 0) { + s->dma_adc.swptr = (swptr + cnt) % s->dma_adc.dmasize; + DBG(printk("%8.8x %8.8x\n",s->dma_adc.swptr, s->dma_adc.hwptr)); + if (s->dma_adc.swptr <= s->dma_adc.hwptr) + s->adc_sw_prev_sgdt = s->adc_hw_prev_sgdt; + s->dma_adc.count -= cnt; + } else { + set_current_state(TASK_INTERRUPTIBLE); + } + +#endif + spin_unlock_irqrestore(&s->lock, flags); + + if (cnt <= 0) { /* No data yet */ + + if (s->dma_adc.enabled) + start_adc(s); + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + up(&s->dsp_sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out; + } + down(&s->dsp_sem); + if (s->dma_adc.mapped) + { + ret = -ENXIO; + goto out; + } + continue; + } + + if (copy_samples_to_user(buffer, s->dma_adc.rawbuf + swptr, + cnt/4, 0)) { + ret = -EFAULT; + goto out; + } + +#if 0 /* Change to handle multiple interrupts before read */ + swptr = (swptr + cnt) % s->dma_adc.dmasize; + spin_lock_irqsave(&s->lock, flags); + s->dma_adc.swptr = swptr; + s->dma_adc.count -= cnt; + spin_unlock_irqrestore(&s->lock, flags); +#endif + + count -= cnt/2; + buffer += cnt/2; + ret += cnt/2; + if (s->dma_adc.enabled) + start_adc(s); + } + out: + up(&s->dsp_sem); + remove_wait_queue(&s->dma_adc.wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + + +static ssize_t ibmcsiti_dsp_write(struct file *file, const char *buffer, + size_t inbytes, loff_t *ppos) +{ + struct ibmcsiti_state *s = file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned swptr; + size_t bytespersample, insamples, outsamples; + long bufspace; + + VALIDATE_STATE(s); + + if (ppos != &file->f_pos) + return -ESPIPE; + if (s->dma_dac.mapped) + return -ENXIO; + if (! access_ok(VERIFY_READ, buffer, inbytes)) + return -EFAULT; + + down(&s->dsp_sem); + + bytespersample = s->outstereo ? 4 : 2; + + insamples = inbytes / bytespersample; + + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) { + printk("DEBUG: ibmcsiti_dsp_write dac ready %d prog_dmabuf retcode %d \n", s->dma_dac.ready, ret); + goto out; + } + + ret = 0; + add_wait_queue(&s->dma_dac.wait, &wait); + while (insamples > 0) { + spin_lock_irqsave(&s->lock, flags); + if (s->dma_dac.count < 0) { + s->dma_dac.count = 0; + s->dma_dac.swptr = s->dma_dac.hwptr; + } + swptr = s->dma_dac.swptr; + bufspace = s->dma_dac.dmasize-swptr; + + /* Truncate to fit in dma buffer */ + if (s->dma_dac.count + bufspace > s->dma_dac.dmasize) + bufspace = s->dma_dac.dmasize - s->dma_dac.count; + + if ((bufspace <= 0) || (s->dac_free_sgdt_q == NULL)) + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&s->lock, flags); + + if ((bufspace <= 0) || (s->dac_free_sgdt_q == NULL)) { + /* No room... */ + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + up(&s->dsp_sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out; + } + down(&s->dsp_sem); + if (s->dma_dac.mapped) { + ret = -ENXIO; + goto out; + } + continue; + } + + /* Always 4 bytes/sample (stereo) in the DMA buffer */ + outsamples = min_t(size_t, insamples, bufspace/4); + + if (copy_samples_from_user(s->dma_dac.rawbuf + swptr, buffer, + outsamples, s->outstereo)) { + ret = -EFAULT; + goto out; + } + + /* FIXME: replace with consistent_sync()? */ + dma_cache_wback_inv((unsigned long)(s->dma_dac.rawbuf + swptr), + outsamples*4); + + swptr = (swptr + outsamples*4) % s->dma_dac.dmasize; /* Wrap */ + spin_lock_irqsave(&s->lock, flags); + s->dma_dac.swptr = swptr; + s->dma_dac.count += outsamples * 4; + s->dma_dac.endcleared = 0; /* Remember to zero clear + * one fragment at the + * end */ + + s->dma_dac.sg_count += outsamples*4; + + spin_unlock_irqrestore(&s->lock, flags); + /* FIXME: broken if odd byte written. Also, format is + * assumed 16 bit */ + insamples -= outsamples; + buffer += outsamples * bytespersample; + ret += outsamples * bytespersample; + + if (s->dma_dac.enabled) { + DBG(printk("bottom start_dac\n")); + DBG(printk("dma_dac.count %d, dma_dac.sg_count %d\n", + s->dma_dac.count, s->dma_dac.sg_count)); + start_dac(s); + DBG(printk("dma_dac.count %d, dma_dac.sg_count %d\n", + s->dma_dac.count, s->dma_dac.sg_count)); + } + + } +out: + up(&s->dsp_sem); + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + + DBG(printk("swptr %8.8x hwptr %8.8x sg_count %d\n", s->dma_dac.swptr, + s->dma_dac.hwptr, s->dma_dac.sg_count);) + + return ret; +} + +static unsigned int ibmcsiti_dsp_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct ibmcsiti_state *s = file->private_data; + unsigned long flags; + unsigned int mask = 0; + + VALIDATE_STATE(s); + if (file->f_mode & FMODE_WRITE) { + if (!s->dma_dac.ready && prog_dmabuf_dac(s)) + return 0; + poll_wait(file, &s->dma_dac.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!s->dma_adc.ready && prog_dmabuf_adc(s)) + return 0; + poll_wait(file, &s->dma_adc.wait, wait); + } + spin_lock_irqsave(&s->lock, flags); + ibmcsiti_update_ptr(s); + if (file->f_mode & FMODE_READ) { + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (s->dma_dac.mapped) { + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)s->dma_dac.dmasize >= s->dma_dac.count + (signed)s->dma_dac.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&s->lock, flags); + return mask; +} + +static int ibmcsiti_dsp_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ibmcsiti_state *s = (struct ibmcsiti_state *)file->private_data; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int count; + int val, ret; + + VALIDATE_STATE(s); + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + printk(KERN_DEBUG "SNDCTL_DSP_SYNC\n"); + if (file->f_mode & FMODE_WRITE) + return drain_dac(s, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + printk(KERN_DEBUG "SNDCTL_DSP_SETDUPLEX\n"); + return 0; + + case SNDCTL_DSP_GETCAPS: + printk(KERN_DEBUG "SNDCTL_DSP_GETCAPS\n"); + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP, (int *)arg); + + case SNDCTL_DSP_RESET: + printk(KERN_DEBUG "SNDCTL_DSP_RESET\n"); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + synchronize_irq(); + s->dma_dac.swptr = s->dma_dac.hwptr = s->dma_dac.count + = s->dma_dac.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + synchronize_irq(); + s->dma_adc.swptr = s->dma_adc.hwptr = s->dma_adc.count + = s->dma_adc.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + if (get_user(val, (int *)arg)) + return -EFAULT; + + printk(KERN_DEBUG "SNDCTL_DSP_SPEED arg=%d\n", val); + + /* FIXME: Hardcoded for now */ + return put_user(IBMCSI_DEFAULT_SAMPLING_RATE, (int *)arg); + + case SNDCTL_DSP_STEREO: + if (get_user(val, (int *)arg)) + return -EFAULT; + + printk(KERN_DEBUG "SNDCTL_DSP_STEREO arg=%d\n", val); + + if (file->f_mode & FMODE_WRITE) { + down(&s->dsp_sem); + if (val) + s->outstereo = 1; + else + s->outstereo = 0; + + if (put_user(s->outstereo, (int *)arg) != 0) + ret = -EFAULT; + up(&s->dsp_sem); + } else { + if (put_user(0, (int *)arg) != 0) + ret = -EFAULT; + } + + return ret; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, (int *)arg)) + return -EFAULT; + + printk(KERN_DEBUG "SNDCTL_DSP_CHANNELS arg=%d\n", val); + + if (file->f_mode & FMODE_WRITE) { + if (val >= 2) { + s->outstereo = 1; + ret = put_user(2, (int *)arg); + } else { + s->outstereo = 0; + ret = put_user(1, (int *)arg); + } + } else { + ret = put_user(1, (int *)arg); + } + + if (ret) + return -EFAULT; + + return 0; + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + printk(KERN_DEBUG "SNDCTL_DSP_GETFMTS\n"); + return put_user(AFMT_S16_BE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + if (get_user(val, (int *)arg)) + return -EFAULT; + printk(KERN_DEBUG "SNDCTL_DSP_SETFMT arg=%d\n", val); + return put_user(AFMT_S16_BE, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + printk(KERN_DEBUG "SNDCTL_DSP_GETTRIGGER\n"); + val = 0; + if ((file->f_mode) & FMODE_READ && (s->state & IBMCSI_ADC_RUNNING)) + val |= PCM_ENABLE_INPUT; + if ((file->f_mode & FMODE_WRITE) && (s->state & IBMCSI_DAC_RUNNING)) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + printk(KERN_DEBUG "SNDCTL_DSP_SETTRIGGER arg=%d\n", val); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!s->dma_adc.ready && (ret = prog_dmabuf_adc(s))) + return ret; + s->dma_adc.enabled = 1; + start_adc(s); + } else { + s->dma_adc.enabled = 0; + stop_adc(s); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!s->dma_dac.ready && (ret = prog_dmabuf_dac(s))) + return ret; + s->dma_dac.enabled = 1; + start_dac(s); + } else { + s->dma_dac.enabled = 0; + stop_dac(s); + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + printk(KERN_DEBUG "SNDCTL_DSP_GETOSPACE\n"); + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ibmcsiti_update_ptr(s); + abinfo.fragsize = s->dma_dac.fragsize; + count = s->dma_dac.count; + if (count < 0) + count = 0; + abinfo.bytes = s->dma_dac.dmasize - count; + abinfo.fragstotal = s->dma_dac.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) + ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + printk(KERN_DEBUG "SNDCTL_DSP_GETISPACE\n"); + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ibmcsiti_update_ptr(s); + abinfo.fragsize = s->dma_adc.fragsize; + count = s->dma_adc.count; + if (count < 0) + count = 0; + abinfo.bytes = count; + abinfo.fragstotal = s->dma_adc.numfrag; + abinfo.fragments = abinfo.bytes >> s->dma_adc.fragshift; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) + ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + printk(KERN_DEBUG "SNDCTL_DSP_NONBLOCK\n"); + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + printk(KERN_DEBUG "SNDCTL_DSP_GETODELAY\n"); + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ibmcsiti_update_ptr(s); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count < 0) + count = 0; + return put_user(count, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + printk(KERN_DEBUG "SNDCTL_DSP_GETIPTR\n"); + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!s->dma_adc.ready && (val = prog_dmabuf_adc(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ibmcsiti_update_ptr(s); + cinfo.bytes = s->dma_adc.total_bytes; + count = s->dma_adc.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_adc.fragshift; + cinfo.ptr = s->dma_adc.hwptr; + if (s->dma_adc.mapped) + s->dma_adc.count &= s->dma_adc.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) + ? -EFAULT : 0; + + case SNDCTL_DSP_GETOPTR: + printk(KERN_DEBUG "SNDCTL_DSP_GETOPTR\n"); + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!s->dma_dac.ready && (val = prog_dmabuf_dac(s)) != 0) + return val; + spin_lock_irqsave(&s->lock, flags); + ibmcsiti_update_ptr(s); + cinfo.bytes = s->dma_dac.total_bytes; + count = s->dma_dac.count; + if (count < 0) + count = 0; + cinfo.blocks = count >> s->dma_dac.fragshift; + cinfo.ptr = s->dma_dac.hwptr; + if (s->dma_dac.mapped) + s->dma_dac.count &= s->dma_dac.fragsize-1; + spin_unlock_irqrestore(&s->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) + ? -EFAULT : 0; + + case SNDCTL_DSP_GETBLKSIZE: + printk(KERN_DEBUG "SNDCTL_DSP_GETBLKSIZE\n"); + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_dac(s))) + return val; + return put_user(s->dma_dac.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_adc(s))) + return val; + return put_user(s->dma_adc.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (int *)arg)) + return -EFAULT; + printk(KERN_DEBUG "SNDCTL_DSP_SETFRAGMENT arg=%d\n", val); + if (file->f_mode & FMODE_READ) { + s->dma_adc.ossfragshift = val & 0xffff; + s->dma_adc.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_adc.ossfragshift < 4) + s->dma_adc.ossfragshift = 4; + if (s->dma_adc.ossfragshift > 15) + s->dma_adc.ossfragshift = 15; + if (s->dma_adc.ossmaxfrags < 4) + s->dma_adc.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + s->dma_dac.ossfragshift = val & 0xffff; + s->dma_dac.ossmaxfrags = (val >> 16) & 0xffff; + if (s->dma_dac.ossfragshift < 4) + s->dma_dac.ossfragshift = 4; + if (s->dma_dac.ossfragshift > 15) + s->dma_dac.ossfragshift = 15; + if (s->dma_dac.ossmaxfrags < 4) + s->dma_dac.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + printk(KERN_DEBUG "SNDCTL_DSP_SUBDIVIDE\n"); + if ((file->f_mode & FMODE_READ && s->dma_adc.subdivision) || + (file->f_mode & FMODE_WRITE && s->dma_dac.subdivision)) + return -EINVAL; + if (get_user(val, (int *)arg)) + return -EFAULT; + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + s->dma_adc.subdivision = val; + if (file->f_mode & FMODE_WRITE) + s->dma_dac.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + printk(KERN_DEBUG "SNDCTL_PCM_READ_RATE\n"); + return put_user(IBMCSI_DEFAULT_SAMPLING_RATE, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + printk(KERN_DEBUG "SNDCTL_PCM_READ_CHANNELS\n"); + return put_user(2, (int *)arg); + + case SOUND_PCM_READ_BITS: + printk(KERN_DEBUG "SNDCTL_PCM_READ_BITS\n"); + return put_user(16,(int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + printk(KERN_DEBUG "SNDCTL_xxx\n"); + return -EOPNOTSUPP; + + } + return -EOPNOTSUPP; +} + +static int ibmcsiti_dsp_release(struct inode *inode, struct file *file) +{ + struct ibmcsiti_state *s = (struct ibmcsiti_state *)file->private_data; + + VALIDATE_STATE(s); + + lock_kernel(); /* FIXME: do we need this? */ + + if (file->f_mode & FMODE_WRITE) + drain_dac(s, file->f_flags & O_NONBLOCK); + + down(&s->open_sem); + if (file->f_mode & FMODE_WRITE) { + stop_dac(s); + del_timer_sync(&s->dac_timer); + synchronize_irq(); + dealloc_dmabuf(s, &s->dma_dac); + } + if (file->f_mode & FMODE_READ) { + stop_adc(s); + del_timer_sync(&s->adc_timer); + dealloc_dmabuf(s, &s->dma_adc); + } + s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + wake_up(&s->open_wait); + + up(&s->open_sem); + unlock_kernel(); + + return 0; +} + + + +/*****************************************************************************/ +/* /dev/dsp suppport routines */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* Start DAC (Digital to Analog Conversion - Audio Playback) */ +/*****************************************************************************/ + +/* Design Notes: + * + * This routine is designed so it can be called safely as many times + * as desired whenever it is necessary to ensure that the DAC process + * is running, conditions permitting. + * + * In scatter/gather mode, we grab a descriptor (if available) and + * enqueue it at the bottom of active descriptor chain. The + * descriptors are configured to form a FIFO queue, not a loop / + * ping-pong, to avoid replaying the same chunk of data. (*) + * + * * Once a chunk of data has been processed, we need to remove it + * from the queue/loop. We have to do such housekeeping on timer + * interrupt; we cannot use DMA EOT interrupt, because the PPC 405 DMA + * core traditionally halts scatter / gather DMA whenever it raises an + * interrupt, until the interrupt is cleared. Since timer interrupts + * are the lowest priority under Linux, they can be masked out under + * stress conditions. Therefore, if we use a looping list / ping-pong + * descriptors, we may not be able to remove an already processed + * chunk of data in time, causing it to be played back again and again + * as the DMAC loops through the list. + * + * When an underrun has occurred, the DMAC will be reading dummy + * neutral data off the last (anchor) descriptor, which loops on + * itself. In this case, we disable the CSI and halt the DMAC + * temporarily, then enqueue the new descriptor and restart the + * scatter/gather process afterwards. + * + * Whether we have reached the anchor descriptor, is determined by + * reading the DMA source address and comparing it with the anchor's + * dummy data address. Since the desriptor address field in the DMAC + * points to the NEXT descriptor to be used, we cannot use it to + * determine whether we are at the anchor (underrun) or just before + * anchor (normal case after the first pass). + * + * To avoid possible race conditions at the descriptor boundary + * (i.e. DMAC going off to the next descriptor after we have read the + * DMAC values), if the current descriptor has only a couple samples + * to go, we wait in a loop until things stabilize. + */ +static void start_dac(struct ibmcsiti_state *s) +{ + unsigned long flags; + struct dma_sgdt *dt; + unsigned int count; + + DBG(printk("start_dac_sg state %8.8x count %d ready %d sgcnt %d hwptr %d\n", + s->state, s->dma_dac.count, s->dma_dac.ready, + s->dma_dac.sg_count, s->dma_dac.hwptr)); + + /* Spinlock held from here to end */ + spin_lock_irqsave(&s->lock, flags); + + if (s->dma_dac.sg_count) { /* There is data that needs to be posted */ + + if (s->dma_dac.hwptr >= s->dma_dac.dmasize) {/* Sanity check */ + s->dma_dac.hwptr = 0; + } + + count = s->dma_dac.sg_count; + if (s->dma_dac.hwptr + count > s->dma_dac.dmasize) { + count = s->dma_dac.dmasize - s->dma_dac.hwptr; + } + + /* 4 bytes per sample, DMA count limited to 64K */ + if (count > 65536*4) + count = 65536*4 ; + + if (count > 4) { /* Got enough data to do DMA */ + /* Get descriptor */ + dt = get_sgdt(&(s->dac_free_sgdt_q)); + + if (dt) { /* Descriptor available */ + DBG(printk("new dt %8.8x ",dt)); + + /* FIXME: Break data into + * fragments. Easiest way for now! */ + + dt->ccw = (unsigned int) (IBMCSI_TXDMA_GO_NOI); + dt->ctrl = 0x80000000 | ((count/4)&0xffff); + dt->srcP = virt_to_phys(s->dma_dac.rawbuf + + s->dma_dac.hwptr); + dt->destP = CSI0_TBUF; + dt->nextP = virt_to_phys(s->dac_sgdt_lastV); + + asm volatile ("sync"); + + DBG(printk("TX SG %8.8x, last %8.8x\n", + mfdcr(TX_SG),s->dac_sgdt_lastV)); + DBG(printk("TX SA %8.8x, count %d \n", + mfdcr(IBMCSI_TXDMA_SA), + mfdcr(IBMCSI_TXDMA_CT))); + DBG(printk("last->phys %8.8lx\n", + s->dac_sgdt_lastV->srcP)); + + if (mfdcr(DCRN_ASGC) & TX_SG_ENABLE) { + unsigned int current_sa = mfdcr(IBMCSI_TXDMA_SA); + if (current_sa == s->dac_sgdt_lastV->srcP) { + s->dac_active_sgdt_q = dt; + stop_dac(s); + udelay(100); + } else if (mfdcr(IBMCSI_TXDMA_CT) <= 2) { + int timeout = 0; + /* No time to safely + * reprogram this + * pass. Wait until + * this pass + * completes. */ + while (current_sa == mfdcr(IBMCSI_TXDMA_SA)) { + timeout++; + if (timeout > 1000000) { + printk("ibmcsi: DMA timeout!!\n"); + break; + } + }; + + if (mfdcr(IBMCSI_TXDMA_SA) == s->dac_sgdt_lastV->srcP) { + stop_dac(s); + udelay(100); + } + } + } else { + /* This gets programmed only if not active */ + s->dac_active_sgdt_q = dt; + } + + /* Insert new dt between last anchor + * and previous data */ + + dt->nextV = s->dac_sgdt_lastV; + dt->prevV = s->dac_sgdt_lastV->prevV; + + if (dt->prevV) { + dt->prevV->nextV = dt; + dt->prevV->nextP = virt_to_phys(dt); + } + + s->dac_sgdt_lastV->prevV = dt; + + if (mfdcr(DCRN_ASGC) & TX_SG_ENABLE) { + if (mfdcr(TX_SG) == virt_to_phys(s->dac_sgdt_lastV) ) { + /* Next descriptor is + * the bottom + * anchor */ + + mtdcr(TX_SG, virt_to_phys(dt)); + /* Replace the next + * descriptor address + * with myself. Not + * sure if this + * works */ + } + } + + asm volatile ("sync"); + + /* Update count and pointer */ + s->dma_dac.sg_count -= count; + s->dma_dac.hwptr += count; + if (s->dma_dac.hwptr >= s->dma_dac.dmasize) + s->dma_dac.hwptr = 0; + } else { /* End if descriptor available */ + printk("0dt\n"); + } + + /* If DMA is not already running, kick it. */ + if (! (mfdcr(DCRN_ASGC) & TX_SG_ENABLE)) { + unsigned int current_csi_er + = IBMCSI_READ(CSI0_ER); + s->state |= IBMCSI_DAC_RUNNING; + + ibmcsi_stop_csi_sync(); /* FIXME: add timeout */ + IBMCSI_WRITE(CSI0_ER, + IBMCSI_READ(CSI0_ER) + & ~CSI_ER_TXEN); + + /* Clear Terminal Count etc. */ + mtdcr(IBMCSI_DMA_SR, + DCRN_DMA_SR_ALL(IBMCSI_TXDMA)); + + /* Clear CSI underrun */ + IBMCSI_WRITE(CSI0_SR, CSI_SR_TOD); + + /* Write address of the first + * scatter/gather descriptor table */ + mtdcr(TX_SG, + virt_to_phys(s->dac_active_sgdt_q)); + + /* Enable scatter/gather */ + mtdcr(DCRN_ASGC, mfdcr(DCRN_ASGC) + | TX_SG_ENABLE | TX_SG_MASK); + + /* Set up CSI config */ + IBMCSI_WRITE(CSI0_CFG, IBMCSI_TI_CFG); + + asm volatile ("sync"); + + /* Start CSI, enable slot 0 and Tx. */ + IBMCSI_WRITE(CSI0_ER, + current_csi_er | CSI_ER_ESLOT(0) + | CSI_ER_ESLOT(1) + | CSI_ER_TXEN | CSI_ER_ECSI); + + udelay(100); + DBG(printk("CSI SA %8.8x, CT %8.8x", + mfdcr(IBMCSI_TXDMA_SA), + mfdcr(IBMCSI_TXDMA_CT))); + DBG(printk("%2.2x %8.8x\n", + IBMCSI_READ(CSI0_SR), + mfdcr(DCRN_DMASR))); + } + } /* End count not zero */ + } + + spin_unlock_irqrestore(&s->lock, flags); +} + +/************************************************************************/ +/* Start ADC (Analog to Digital Conversion - Audio Capture) */ +/************************************************************************/ + +/* Design Notes: + * + * Like its companion start_dac(), this routine is designed to be + * called as many times as desired whenever it is necessary to ensure + * that the ADC process is running, conditions permitting. + * + * Unlike DAC, ADC uses a ring descriptor scheme because we can simply + * let the DMAC overwrite stale data when an overrun occurs. All the + * descriptors in the ADC chain point to the same buffer (the entire + * DMA buffer). In theory it might be enough to use just one + * descriptor that loops on itself, but in this implementation we will + * use redundant multiple descriptors, so that we can detect an + * overrun condition trivially, by comparing the current descriptor + * address with the previous one's. (This is done in the timer int + * handler.) So long as timer interrupts are not masked out for the + * duration of the entire descriptor loop (in which case we are most + * likely in much deeeper trouble than an overrun anyway), we should + * be able to detect an overrun reliably in this manner. + * + * If we want to be truly paranoid, and/or have faith in kernel + * timekeeping functions and incoming audio data rate, we could also + * use timebase and fewer descriptors to do the same; you would still + * have to worry about things like * timebase accuracy (e.g. tb_to_us + * doesn't work with slow timebase clock). + */ + +static void start_adc(struct ibmcsiti_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + + if (! (mfdcr(DCRN_ASGC) & RX_SG_ENABLE)) { /* In hardware we trust */ + unsigned int current_csi_er = IBMCSI_READ(CSI0_ER); + + s->state |= IBMCSI_ADC_RUNNING; + + ibmcsi_stop_csi_sync(); /* FIXME: add timeout */ + IBMCSI_WRITE(CSI0_ER, IBMCSI_READ(CSI0_ER) & ~CSI_ER_RXEN ); + + /* Clear Terminal Count etc. for Rx channel */ + mtdcr(IBMCSI_DMA_SR, DCRN_DMA_SR_ALL(IBMCSI_RXDMA)); + + /* Clear the CSI status flag for Rx */ + IBMCSI_WRITE(CSI0_SR, CSI_SR_ROD); + + /* Write the first scatter/gather descriptor address */ + mtdcr(RX_SG, virt_to_phys(s->adc_active_sgdt_q)); + + s->adc_hw_prev_sgdt = s->adc_active_sgdt_q; + s->adc_sw_prev_sgdt = s->adc_active_sgdt_q; + s->dma_adc.hwptr = 0; /* We will never halt ADC so we can init here */ + s->dma_adc.swptr = 0; /* Otherwise we will have to + * figure out how to restart. */ + + /* Enable scatter/gather DMA */ + mtdcr(DCRN_ASGC, mfdcr(DCRN_ASGC) | RX_SG_ENABLE | RX_SG_MASK); + + /* Set up CSI config */ + IBMCSI_WRITE(CSI0_CFG, IBMCSI_TI_CFG); + + asm volatile("sync"); + + /* Start CSI, enable slot 0 and Rx */ + IBMCSI_WRITE(CSI0_ER, current_csi_er + | CSI_ER_ESLOT(0) | CSI_ER_ESLOT(1) + | CSI_ER_RXEN | CSI_ER_ECSI ); + udelay(100); + } + + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_dac(struct ibmcsiti_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + + /* Stop CSI synchronously - let transfer complete */ + ibmcsi_stop_csi_sync(); + IBMCSI_WRITE(CSI0_ER, IBMCSI_READ(CSI0_ER) & ~CSI_ER_TXEN); + if (IBMCSI_READ(CSI0_ER) & CSI_ER_RXEN) { + IBMCSI_WRITE(CSI0_ER, IBMCSI_READ(CSI0_ER) & ~CSI_ER_RXEN); + } + + mtdcr(DCRN_ASGC,(mfdcr(DCRN_ASGC) | TX_SG_MASK) & ~TX_SG_ENABLE); + + /* Stop Tx DMA channel */ + mtdcr(IBMCSI_TXDMA_CR, + mfdcr(IBMCSI_TXDMA_CR) & ~DCRN_DMA_CR_CE); + + s->state &= ~IBMCSI_DAC_RUNNING; + + if (s->state & IBMCSI_ADC_RUNNING) { /* ADC was running */ + IBMCSI_READ(CSI0_RBUF); /* Dummy read */ + IBMCSI_WRITE(CSI0_SR, CSI_SR_ROD | CSI_SR_TOD); + IBMCSI_WRITE(CSI0_ER, + CSI_ER_ESLOT(0) | CSI_ER_ESLOT(1) + | CSI_ER_RXEN | CSI_ER_ECSI ); + udelay(100); + DBG(printk("%2.2x %8.8x\n", IBMCSI_READ(CSI0_SR), + mfdcr(DCRN_DMASR))); + } + + spin_unlock_irqrestore(&s->lock, flags); +} + +static inline void stop_adc(struct ibmcsiti_state *s) +{ + unsigned long flags; + + spin_lock_irqsave(&s->lock, flags); + + /* Stop CSI synchronously - let transfer complete */ + ibmcsi_stop_csi_sync(); + IBMCSI_WRITE(CSI0_ER, IBMCSI_READ(CSI0_ER) & ~CSI_ER_RXEN); + + mtdcr(DCRN_ASGC,(mfdcr(DCRN_ASGC) | RX_SG_MASK) & ~RX_SG_ENABLE); + + mtdcr(IBMCSI_RXDMA_CR, mfdcr(IBMCSI_RXDMA_CR) & ~DCRN_DMA_CR_CE); + s->state &= ~IBMCSI_ADC_RUNNING; + + if (s->state & IBMCSI_DAC_RUNNING) { /* DAC is still running */ + IBMCSI_WRITE(CSI0_ER, IBMCSI_READ(CSI0_ER) | CSI_ER_ECSI); + } + + spin_unlock_irqrestore(&s->lock, flags); +} + +/* + * Globally stop the CSI core synchronously + */ +static int ibmcsi_stop_csi_sync() +{ + int retcode = 0; + unsigned int tbl_timeout; + unsigned long count = 2000; /* 2ms */ + unsigned int current_csi_er = IBMCSI_READ(CSI0_ER); + + DBG(printk("stop_csi: %8.8x\n", current_csi_er)); + + /* Turn off CSI */ + IBMCSI_WRITE(CSI0_ER, 0); + eieio(); + + /* timebase 1 tick = 1000000 / (HZ * tb_ticks_per_jiffy) usec */ + /* (count) usec = count * HZ * tb_ticks_per_jiffy / 1000000 ticks */ + + tbl_timeout = ( (count*tb_ticks_per_jiffy*HZ) + 999999 ) / 1000000; + DBG(printk("tbl delta %d\n", tbl_timeout)); + + tbl_timeout += get_tbl(); + + while(IBMCSI_READ(CSI0_SR) & CSI_SR_CSIB) { + if (get_tbl() > tbl_timeout) { + printk("ibmcsiti: Error: DMA timeout\n"); + retcode = -1; + break; + } + } /* Wait till CSI Busy turns off or timeout. */ + udelay(1); + + IBMCSI_WRITE(CSI0_ER, current_csi_er & ~CSI_ER_ECSI); + /* Globally disable CSI, preserve whichever channel was in use */ + + return retcode; +} + +/*****************************************************************************/ +/* Allocate / initialize DMA buffer and related variables */ +/*****************************************************************************/ +static int prog_dmabuf(struct ibmcsiti_state *s, struct dmabuf *db, + unsigned rate, unsigned fmt, unsigned adc_init) +{ + int order; + unsigned bytepersec; + unsigned bufs; + + db->hwptr = db->swptr = db->total_bytes = db->count + = db->error = db->endcleared = 0; + + if (! db->rawbuf) { /* DMA buffer not allocated yet, go get it. */ + db->ready = db->mapped = 0; + for (order = DMABUF_DEFAULTORDER; + order >= DMABUF_MINORDER; order--) { + db->rawbuf = (void *)__get_free_pages(GFP_KERNEL | GFP_DMA, + order); + if (db->rawbuf) { + db->dmaaddr = (dma_addr_t)virt_to_phys(db->rawbuf); + /* db->hwptr = db->dmaaddr; */ + break; + } + } + + if (!db->rawbuf) + return -ENOMEM; + + DBG(printk("buforder: %8.8x\n", order)); + + db->buforder = order; + + if (adc_init) { + /* Program the count and address into all + * descriptors in the ring */ + /* Use all the queue for now */ + struct dma_sgdt *pWork = s->adc_free_sgdt_q; + unsigned dma_count = + ((long)PAGE_SIZE << db->buforder) / 4; + + s->adc_active_sgdt_q = pWork; + + while (pWork) { /* Sanity check */ + pWork->ccw = (unsigned int)(IBMCSI_RXDMA_GO_NOI); + pWork->srcP = CSI0_RBUF; + pWork->destP = db->dmaaddr; + pWork->ctrl = dma_count | 0x80000000; + + if (pWork->nextV == NULL) { + /* End of chain reached */ + /* Create a double ring */ + pWork->nextV = s->adc_active_sgdt_q; + s->adc_active_sgdt_q->prevV = pWork; + } + pWork->nextP = virt_to_phys(pWork->nextV); + + pWork = pWork->nextV; + if (pWork == s->adc_active_sgdt_q) + break; + } + } + + } + + /* FIXME: Hardcoded for 11.025 KHz */ + bytepersec = IBMCSI_DEFAULT_SAMPLING_RATE * 4; + + bufs = PAGE_SIZE << db->buforder; + + + /* FIXME: decypher the following code block (from es1370.c) */ + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + + DBG(printk("ibmcsi: fragsize was %d\n", db->fragsize)); + + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->fragsamples = db->fragsize >> sample_shift[fmt]; + db->dmasize = db->numfrag << db->fragshift; + /* End FIXME */ + + if (adc_init) + db->dmasize = ((long)PAGE_SIZE << db->buforder); + + /* FIXME: Sample format hardcoded - assumes 0 is neutral sample. */ + memset(db->rawbuf, 0, db->dmasize); + + db->enabled = 1; + db->ready = 1; + return 0; +} + +/*****************************************************************************/ +/* Prepare DMA buffer for ADC */ +/*****************************************************************************/ +static inline int prog_dmabuf_adc(struct ibmcsiti_state *s) +{ + stop_adc(s); + + /* FIXME: Sampling rate hardcoded at 11.025 KHz. */ + return prog_dmabuf(s, &s->dma_adc, IBMCSI_DEFAULT_SAMPLING_RATE, 0, 1); +} + +/*****************************************************************************/ +/* Prepare DMA buffer for DAC */ +/*****************************************************************************/ +static inline int prog_dmabuf_dac(struct ibmcsiti_state *s) +{ + stop_dac(s); + + /* FIXME: Sampling rate hardcoded at 44.1 KHz. */ + return prog_dmabuf(s, &s->dma_dac, IBMCSI_DEFAULT_SAMPLING_RATE, 0, 0); +} + +static inline void dealloc_dmabuf(struct ibmcsiti_state *s, struct dmabuf *db) +{ + if (db->rawbuf) + free_pages((unsigned long)db->rawbuf, db->buforder); + + db->rawbuf = NULL; + db->mapped = db->ready = 0; +} + + + +/*****************************************************************************/ +/* Procedure get_hwptr: Update DMA controller address offset hwptr */ +/* (returns # of bytes transferred) */ +/*****************************************************************************/ +static inline unsigned get_hwptr(struct ibmcsiti_state *s, struct dmabuf *db, + unsigned channel) +{ + unsigned hwptr, diff; + switch (channel) { + case IBMCSI_TXDMA: /* Tx DMA channel */ + hwptr = (unsigned)mfdcr(IBMCSI_TXDMA_SA) + - (unsigned)(db->dmaaddr); + /* Get source offset for next transfer on Tx + * DMA channel */ + break; + case IBMCSI_RXDMA: /* Rx DMA channel */ + hwptr = (unsigned)mfdcr(IBMCSI_RXDMA_DA) + - (unsigned)(db->dmaaddr); + break; + default:/* not supported */ + return 0; + } + + /* Calculate number of actual data byte transfers */ + diff = hwptr - db->hwptr; + /* The pointer is void * into physical memory; diff here is in bytes */ + + if (hwptr >= db-> dmasize) /* Wrap */ + hwptr = 0; + + if (hwptr < 0) { /* Out of area */ + hwptr = 0; + diff = 0; /* Fix up */ + } + db->hwptr = hwptr; /* Update next transfer start offset */ + return diff; +} + +/*****************************************************************************/ +/* Procedure clear_advance: */ +/* Fill the tail end of buffer with neutral sample data */ +/*****************************************************************************/ +static inline void clear_advance(void *buf, unsigned bsize, unsigned bptr, + unsigned len, unsigned char c) +{ + if (bptr + len > bsize) { + unsigned x = bsize - bptr; + memset(((char *)buf) + bptr, c, x); + bptr = 0; + len -= x; + } + memset(((char *)buf) + bptr, c, len); +} + +/*****************************************************************************/ +/* Update pointers for the next DMA operation. */ +/* * Must be called with spinlock held (FIXME: which one -dwg) */ +/*****************************************************************************/ +static void ibmcsiti_update_ptr(struct ibmcsiti_state *s) +{ + int diff; + + /* update ADC pointer */ + if (s->state & IBMCSI_ADC_RUNNING) { + /* ADC channel (Rx DMA) */ + diff = get_hwptr(s, &s->dma_adc, IBMCSI_RXDMA); + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) { + wake_up(&s->dma_adc.wait); + } else { + DBG(printk("!@ %8.8x %8.8x\n", s->dma_adc.count, + s->dma_adc.fragsize)); + } + + if (!s->dma_adc.mapped) { + if (s->dma_adc.count > (signed)(s->dma_adc.dmasize - ((3 * s->dma_adc.fragsize) >> 1))) { + s->state &= ~IBMCSI_ADC_RUNNING; + s->dma_adc.error++; + } else { + s->state &= ~IBMCSI_ADC_RUNNING; + start_adc(s); + } + } + /* FIXME: Give some thoughts about mmap support */ + } + + /* update DAC pointer */ + if (s->state & IBMCSI_DAC_RUNNING) { + /* DAC channel (Tx DMA) */ + diff = get_hwptr(s, &s->dma_dac, IBMCSI_TXDMA); + + s->dma_dac.total_bytes += diff; + if (s->dma_dac.mapped) { /* mapped = 0 if prepared */ + printk(KERN_ERR "DEBUG: ERR: mapped \n"); + s->dma_dac.count += diff; + if (s->dma_dac.count >= (signed)s->dma_dac.fragsize) + wake_up(&s->dma_dac.wait); + } else { + s->dma_dac.count -= diff; + if (s->dma_dac.count <= 0) { + /* Disable Tx DMA channel */ + mtdcr(IBMCSI_TXDMA_CR, 0); + s->state &= ~IBMCSI_DAC_RUNNING; + s->dma_dac.error++; + } else if ((s->dma_dac.count <= + (signed)s->dma_dac.fragsize) + && !s->dma_dac.endcleared) { + clear_advance(s->dma_dac.rawbuf, + s->dma_dac.dmasize, + s->dma_dac.swptr, + s->dma_dac.fragsize, 0); + s->dma_dac.endcleared = 1; + } + + s->state &= ~IBMCSI_DAC_RUNNING; /* Let start_dac() do it */ + + if (s->dma_dac.count + (signed)s->dma_dac.fragsize + <= (signed)s->dma_dac.dmasize) { + wake_up(&s->dma_dac.wait); + } else + start_dac(s); + + } + } +} + + +/*****************************************************************************/ +/* DAC (Playback) Interrupt Handler */ +/*****************************************************************************/ +static void ibmcsi_dac_timer(unsigned long param) +{ + struct ibmcsiti_state *s = (struct ibmcsiti_state *)param; + struct dma_sgdt *pCurrent_dt, *pNext_dt, *pWork; + struct dma_sgdt *pAnchor = s->dac_sgdt_lastV; + int count = 0; + + if ( (mfdcr(DCRN_ASGC) & TX_SG_ENABLE) /* DAC running */ + && (mfdcr(IBMCSI_TXDMA_CT) <= 4) /* No time to do anything */ + /* 4 samples = a little less than 100us at 44KHz */ + && (mfdcr(IBMCSI_TXDMA_SA) != (pAnchor->srcP)) ) { + udelay(100); /* Let the 4 samples run its course */ + } + + if (mfdcr(DCRN_ASGC) & TX_SG_ENABLE) { /* DAC running */ + DBG(printk("t act %8.8x last %8.8x ", s->dac_active_sgdt_q, + s->dac_sgdt_lastV)); + DBG(printk("t")); + + if (pAnchor->prevV) { /* Data queued */ + /* Next S/G descriptor table */ + pNext_dt = (struct dma_sgdt *) phys_to_virt(mfdcr(TX_SG)); + + if (check_sgdt_range(s, pNext_dt, SGDT_COUNT)) { + pCurrent_dt = pNext_dt->prevV; + + if (pCurrent_dt) { + pWork = pCurrent_dt; + if (pWork->prevV) { + struct dma_sgdt *pTemp = pWork; + pWork = pWork->prevV; + pTemp->prevV = NULL; /* Dequeue previous data */ + + while (pWork) { + count += ((unsigned int)(pWork->ctrl)) & (unsigned int) 0xffff; + pTemp = pWork; + pWork = pWork->prevV; /* Fetch next uplink pointer */ + DBG(printk("count %d free %8.8x ", count, pTemp)); + free_sgdt( &(s->dac_free_sgdt_q), pTemp); + } + } + s->dac_active_sgdt_q = pCurrent_dt; + } + + } + + s->dma_dac.total_bytes += count * 4; + s->dma_dac.count -= count * 4; + if (s->dma_dac.count <= 0) { + + /* stop_dac(s); *//* ??? */ + + s->dma_dac.error++; + } + if (count) { + DBG(printk (" w")); + wake_up(&s->dma_dac.wait); + } + } + } + + mod_timer(&s->dac_timer, jiffies + DAC_TIMER_PERIOD); +} + +static void ibmcsiti_dac_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + printk("ibmcsi: spurious dac int\n"); + return; +} + + +/*****************************************************************************/ +/* ADC (Capture) Interrupt Handlers */ +/*****************************************************************************/ +static void ibmcsi_adc_timer(unsigned long param) +{ + struct ibmcsiti_state *s = (struct ibmcsiti_state *)param; + unsigned long pWork; + struct dma_sgdt *pWorkV; + unsigned long hwptr, diff = 0; + + if ( (mfdcr(DCRN_ASGC) & RX_SG_ENABLE) /* ADC running */ + && (mfdcr(IBMCSI_RXDMA_CT) <= 4 )) { + /* Too close to descriptor changeover point */ + /* 4 samples = a little less than 100us at 44KHz */ + udelay(100); /* Let the 4 samples run its course */ + } + + if (mfdcr(DCRN_ASGC) & RX_SG_ENABLE) { + pWork = mfdcr(RX_SG); + hwptr = mfdcr(IBMCSI_RXDMA_DA); + eieio(); + + while ( (pWork != mfdcr(RX_SG)) + || (hwptr != mfdcr(IBMCSI_RXDMA_DA)) ) { + pWork = mfdcr(RX_SG); + hwptr = mfdcr(IBMCSI_RXDMA_DA); + eieio(); + } /* HACK: Loop until things stabilize */ + + pWorkV = (struct dma_sgdt *) phys_to_virt(pWork); + hwptr = hwptr - (unsigned)s->dma_adc.dmaaddr; + + if (pWorkV == NULL) { + printk("ibmcsiti: INTERNAL ERROR! Bad sgdt@\n"); + stop_adc(s); + } else { + pWorkV = pWorkV->prevV; + if (pWorkV == NULL) { + printk(KERN_ERR "ibmcsiti: INTERNAL ERROR! " + "sgdt ring corrupted\n"); + stop_adc(s); + } else {/* No errors. pWork now has the + * current descriptor address */ + /* The following typically gets done + * in update_ptr(), but since the + * implementation for scatter/gather + * is different enough we'll do it + * here. */ + + DBG(printk("%p\n", pWorkV)); + +/*(Read timing changed to get a stable reading.. hwptr = (unsigned)mfdcr(IBMCSI_RXDMA_DA) - (unsigned)s->dma_adc.dmaaddr; */ + + if (pWorkV == s->adc_sw_prev_sgdt) { + /* Same descriptor as before? */ + /* Normally, software read + * pointer should be less than + * current DMA dest + * address. If not, we have an + * overrun big time - DMAC has + * run through all scatter / + * gather descriptors and came + * back full circle... (With + * the current design this + * should never happen, unless + * you reduce the number of + * sgdt's.) */ + + /* Normal case */ + if (s->dma_adc.swptr <= hwptr) { + /* Must be '<=' not + * '<'. '=' can happen + * if interrupt + * happens when no + * data has yet been + * DMA'ed. */ + if (hwptr >= s->dma_adc.hwptr) + diff = hwptr - s->dma_adc.hwptr; + else { + /* Big time overrun. */ + diff = 0; + /* We are + * using the + * same + * descriptor + * as previous + * task time + * read; hwptr + * will only + * advance, + * unless it + * ran thru + * all the + * descriptor + * chain. */ + } + } else { + /* Overrun case */ + /* We now have buffer + * full of data + * starting at the + * current DMA dest + * address (which is + * the next one to be + * used by DMAC. */ + printk("!0 %08lx %8.8x\n", + hwptr, + s->dma_adc.swptr); + DBG(printk("0 %x %x\n", + IBMCSI_READ(CSI0_SR), + mfdcr(DCRN_DMASR))); + diff = s->dma_adc.dmasize; + s->dma_adc.swptr = hwptr; + } + } else if (pWorkV == s->adc_sw_prev_sgdt->nextV) { + /* Next descriptor from previous */ + /* This time, software read + * pointer should be MORE than + * curent DMA dest address, + * since we wrapped around. If + * not, we are overrun - DMAC + * trampled data between + * software read pointer and + * current DMA address. */ + + if (s->dma_adc.swptr >= hwptr) { + /* Normal case */ + /* Previous hwptr can be anywhere */ + if (s->dma_adc.hwptr >= hwptr) { /*Wrap*/ + diff = s->dma_adc.dmasize - + (s->dma_adc.hwptr - hwptr); + } else { /* Simple advance */ + DBG(printk("!1\n")); /* This is also normal */ + DBG(printk("1 %2.2x %8.8x\n", + IBMCSI_READ(CSI0_SR), + mfdcr(DCRN_DMASR))); + diff = hwptr - s->dma_adc.hwptr; + } + } else { /* Overrun case */ + printk("!2\n"); + DBG(printk("2 %8.8x %8.8x %8.8x %8.8x\n", + IBMCSI_READ(CSI0_SR), + mfdcr(DCRN_DMASR), + IBMCSI_READ(CSI0_ER), + mfdcr(IBMCSI_RXDMA_DA) )); + diff = s->dma_adc.dmasize; +#if 1 + s->dma_adc.swptr = hwptr; +#else + if (hwptr + 4 < s->dma_adc.dmasize) + s->dma_adc.swptr = hwptr+4; + else + s->dma_adc.swptr = 0; +#endif + } + } else { /* None of the above. We are overrun, big time. */ + printk("!3\n"); + DBG(printk("3 %2.2x %8.8x\n", + IBMCSI_READ(CSI0_SR), + mfdcr(DCRN_DMASR))); + + diff = s->dma_adc.dmasize; + s->dma_adc.swptr = hwptr; + + } + + s->dma_adc.hwptr = hwptr; + s->dma_adc.total_bytes += diff; + s->dma_adc.count += diff; + s->adc_hw_prev_sgdt = pWorkV; + + if (s->dma_adc.count > s->dma_adc.dmasize) /* Overrun */ + s->dma_adc.count = s->dma_adc.dmasize; + + if (s->dma_adc.count >= (signed)s->dma_adc.fragsize) { + wake_up(&s->dma_adc.wait); + } + + mod_timer(&s->adc_timer, jiffies + ADC_TIMER_PERIOD); + } + } + } else { + mod_timer(&s->adc_timer, jiffies + ADC_TIMER_PERIOD); + } + /* Any errors, we stop the ADC and refrain from restarting the + * timer. */ +} + +/************************************************************************/ +/* ADC DMA interrupt (straightforward non-scatter/gather DMA) */ +/************************************************************************/ +static void ibmcsiti_adc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + printk("ibmcsi: spurious adc int\n"); + return; +} + +/*****************************************************************************/ +/* Drain DAC (flush playback buffer) */ +/*****************************************************************************/ +static int drain_dac(struct ibmcsiti_state *s, int nonblock) +{ +#if 0 /* FIXME: Drain_dac not implemented yet. Timeout value depends + * on sampling rate. */ + DECLARE_WAITQUEUE(wait, current); + + unsigned long flags; + int count, tmo; + + if (s->dma_dac.mapped || !s->dma_dac.ready) + return 0; + + add_wait_queue(&s->dma_dac.wait, &wait); + for (;;) { + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&s->lock, flags); + count = s->dma_dac.count; + spin_unlock_irqrestore(&s->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * (count + s->dma_dac.fragsize) / 2 + / DAC2_DIVTOSR((s->ctrl & CTRL_PCLKDIV) >> CTRL_SH_PCLKDIV); + tmo >>= sample_shift[(s->sctrl & SCTRL_P2FMT) >> SCTRL_SH_P2FMT]; + if (!schedule_timeout(tmo + 1)) + DBG(printk(KERN_DEBUG "ibmcsiti: DMA timed out??\n");) + } + remove_wait_queue(&s->dma_dac.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +#else + return 0; +#endif +} + +/* Reformat and copy device format data to user format data (for + * capture) + */ +static unsigned long copy_samples_to_user(char *dest, const char *src, + unsigned nsamples, int stereo) +{ + u16 *from = (u16 *)src; + u16 *to = (u16 *)dest; + int i; + + if (stereo) { + /* 16-bit stereo => 4 bytes per sample */ + if (copy_to_user(dest, src, nsamples*4) != 0) + return -EFAULT; + } else { + /* convert stereo to mono as we copy */ + for (i = 0; i < nsamples; i++) { + if (put_user(*from, to) != 0) + return -EFAULT; + + from += 2; /* throw away the right channel info */ + to++; + } + } + + return 0; +} + +/* Reformat and copy user format data to device format data (for + * playback) + */ +static unsigned long copy_samples_from_user(char *dest, const char *src, + unsigned nsamples, int stereo) +{ + u16 *from = (u16 *)src; + u16 *to = (u16 *)dest; + u16 temp; + int i; + + if (stereo) { + /* 16-bit stereo => 4 bytes per sample */ + if (copy_from_user(dest, src, nsamples*4) != 0) + return -EFAULT; + } else { + /* convert mono to stereo as we copy */ + for (i=0; i < nsamples; i++) { + if (get_user(temp, from) != 0) + return -EFAULT; + + *(to++) = temp; + *(to++) = 0; /* Pad right-channel with zero data */ + from++; + } + } + + return 0; +} + +/*****************************************************************************/ +/* Base 2 logarithm (approx.) */ +/*****************************************************************************/ +static inline unsigned ld2(unsigned int x) +{ + unsigned r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/*****************************************************************************/ +/* Scatter/Gather descriptor queue handlers */ +/*****************************************************************************/ +/* FIXME: spinlocks TBD (rely on global spinlocks for now) */ + +/* Initialize logical (virtual address) pool of descriptor tables */ +static void init_sgdt_q(struct dma_sgdt *queue, int count) +{ + int i; + struct dma_sgdt *work = queue; + + work->ctrl = 0; + work->prevV = NULL; /* Terminate backward link */ + for (i=1;i")); + if (*queueaddress == NULL) + return NULL; + + work->prevV = NULL; /* Unlink backward */ + *queueaddress = work->nextV; /* Update queue pointer */ + + if (*queueaddress) + (*queueaddress)->prevV = NULL; + + work->nextV = NULL; /* Unlink forward */ + + DBG(printk("get_sgdt q %8.8x ret %8.8x\n", *queueaddress, work)); + return work; +} + +/* Return a descriptor table to free pool */ +static void free_sgdt(struct dma_sgdt **queueaddress, struct dma_sgdt *dt) +{ + struct dma_sgdt *work = *queueaddress; + DBG(printk("free_sgdt %8.8x queue %8.8x ", dt, work)); + DBG(printk("<")); + + *queueaddress = dt; /* Update queue read pointer */ + dt->prevV = NULL; /* Unlink backward */ + dt->nextV = work; /* Link forward */ + if (work) work->prevV = dt; /* Link backward if non-null */ + + DBG(printk("now %8.8x\n",*queueaddress)); +} + +/* Check the range of descriptor */ +static unsigned int check_sgdt_range(struct ibmcsiti_state *s, + struct dma_sgdt *dt, int count) +{ +#if 1 + return 1; +#else + if ( (struct dma_sgdt *)s->write_line <= dt ) && + (dt <= (struct dma_sgdt *)s->write_line + count) { + return 1; + } + else return 0; +#endif +} + +/************************************************************************/ +/* Part II : Mixer device /dev/mixer */ +/************************************************************************/ + +/* The TLV320AIC23 codec supports the following channels:*/ +/* - Master Volume (Line out; 1.0V RMS) */ +/* - Alt PCM (Speaker; 16-32 ohm impedance mapped to Alt PCM, per AC'97)*/ +/* - Line (Line in) */ +/* - Mic (Mic in) */ +/* Although the chip supports individual left/right channel volume control,*/ +/* both channels will always be set to the same value. */ + + +static int ibmcsiti_mixer_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct list_head *list; + struct ibmcsiti_state *s; + + for (list = devs.next; ; list = list->next) { + if (list == &devs) + return -ENODEV; + s = list_entry(list, struct ibmcsiti_state, devs); + if (s->dev_mixer == minor) + break; + } + VALIDATE_STATE(s); + file->private_data = s; + init_MUTEX(&s->mix_sem); + return 0; +} + +static int ibmcsiti_mixer_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static int ibmcsiti_mixer_ioctl(struct inode *ioctl, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct ibmcsiti_state *s = file->private_data; + const unsigned int nr = _IOC_NR(cmd); + int ret = 0; + + switch (cmd) { + case SOUND_MIXER_INFO: { + struct mixer_info info; + + strncpy(info.id, "IBMCSITI", sizeof(info.id)); + strncpy(info.name, "IBM 405LP CSI+TLV320", sizeof(info.name)); + info.modify_counter = 0; + if (copy_to_user((void *)arg, &info, sizeof(info))) + ret = -EFAULT; + } + break; + + case SOUND_OLD_MIXER_INFO: { + struct _old_mixer_info info; + + strncpy(info.id, "IBMCSITI", sizeof(info.id)); + strncpy(info.name, "IBM 405LP CSI+TLV320", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + ret = -EFAULT; + } + break; + + case OSS_GETVERSION: + ret = put_user(SOUND_VERSION, (int *)arg); + break; + + default: + if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int)) { + ret = -EINVAL; + break; + } + + down(&s->mix_sem); + + if (_SIOC_DIR(cmd) == _SIOC_READ) + ret = mixer_read_ioctl(s, nr, (caddr_t) arg); + else if ( (_SIOC_DIR(cmd) == _SIOC_WRITE) || + (_SIOC_DIR(cmd) == (_SIOC_READ | _SIOC_WRITE) ) ) + ret = mixer_write_ioctl(s, nr, (caddr_t) arg); + else + ret = -EINVAL; + + up(&s->mix_sem); + } + + return ret; +} + +static int mixer_read_ioctl(struct ibmcsiti_state *s, unsigned int nr, + caddr_t arg) +{ + int val = -1; + + switch (nr) { + case SOUND_MIXER_CAPS: + /* Cannot support mixing mic and line in */ + val = SOUND_CAP_EXCL_INPUT; + break; + + case SOUND_MIXER_DEVMASK: + val = (SOUND_MASK_VOLUME | SOUND_MASK_LINE /* Line in/out */ + | SOUND_MASK_MIC ); + break; + + case SOUND_MIXER_STEREODEVS: + val = SOUND_MASK_VOLUME; + break; + + case SOUND_MIXER_OUTMASK: + val = 0 ; /* No routing of inputs to outputs */ + break; + + case SOUND_MIXER_RECMASK: /* Recording devices */ + val = (SOUND_MASK_LINE | SOUND_MASK_MIC); + break; + + case SOUND_MIXER_VOLUME: /* Line out volume */ + val = ibmcsiti_get_volume(s); + break; + + case SOUND_MIXER_LINE: /* Line in volume */ + val = ibmcsiti_get_line(s); + break; + + case SOUND_MIXER_MIC: /* Mic in volume */ + val = ibmcsiti_get_mic(s); + break; + + case SOUND_MIXER_OUTSRC: /* Unmuted output sources */ + val = ibmcsiti_get_outsrc(s); + break; + + case SOUND_MIXER_RECSRC: /* Unmuted recording sources */ + val = ibmcsiti_get_recsrc(s); + break; + default: + return -EINVAL; + } + return put_user(val, (int *) arg); +} + +static int mixer_write_ioctl(struct ibmcsiti_state *s, unsigned int nr, + caddr_t arg) +{ + int val; + + if (get_user(val, (int *) arg) != 0) + return -EFAULT; + + switch (nr) { + case SOUND_MIXER_VOLUME: + val = ibmcsiti_set_volume(s, val); + break; + + case SOUND_MIXER_LINE: + val = ibmcsiti_set_line(s, val>>8); + break; + + case SOUND_MIXER_MIC: + val = ibmcsiti_set_mic(s, val>>8); + break; + + case SOUND_MIXER_RECSRC: + val = ibmcsiti_set_recsrc(s, val); + break; + + case SOUND_MIXER_OUTSRC: + /* FIXME: we only have one output source, but we + * should probably let it be enabled and disabled + * through this */ + val = ibmcsiti_get_outsrc(s); + break; + + default: + return -EINVAL; + } + if (val < 0) + return val; + return put_user(val, (int *) arg); +} + + +/* + * Individual mixer ioctl() routines + */ + +/*Read Headphone/line out volume */ +static int ibmcsiti_get_volume(struct ibmcsiti_state *s) +{ + int val; + u16 lv = tlv320_read_reg(s, TLV320_LCH) & TLV320_LCH_LHV; + u16 rv = tlv320_read_reg(s, TLV320_RCH) & TLV320_RCH_RHV; + u16 mute = tlv320_read_reg(s, TLV320_DAP) & TLV320_DAP_DACM; + + if (mute) { + val = 0; + } else { + /* The volume field is 7-bits, but values 48 and below + * are all treated as muted */ + lv = 100 * (lv - 48) / 79; + rv = 100 * (rv - 48) / 79; + val = lv | (rv << 8); + } + + return val; +} + + +/*****************************************************************************/ +/* Read Line In volume (VOLUME) */ +/*****************************************************************************/ +static int ibmcsiti_get_line(struct ibmcsiti_state *s) +{ + int val; + u16 temp = tlv320_read_reg(s, TLV320_LLI); + /* Left channel input mute bit 7 (1=mute) */ + /* Left channel input volume bits 4:0, 10111 = 0dB. 0-31 */ + /* 00000 = -34.5 dB : low enough to be considered 0... */ + + if (temp & TLV320_LLI_LIM) + val = 0; + else + val = 100 * (temp & TLV320_LLI_LIV) / TLV320_LLI_LIV; + + return val; +} + +/*****************************************************************************/ +/* Read Mic In volume (MIC) */ +/*****************************************************************************/ +static int ibmcsiti_get_mic(struct ibmcsiti_state *s) +{ + int val ; + u16 temp = tlv320_read_reg(s, TLV320_AAP); + + if (temp & TLV320_AAP_MICM) + val = 0; /* Mute */ + else if (temp & TLV320_AAP_MICB) + val = 100; /* 20dB boost */ + else + val = 50; /* 0dB boost */ + + return val; +} + +/*****************************************************************************/ +/* Read Recording Source */ +/*****************************************************************************/ +static int ibmcsiti_get_recsrc(struct ibmcsiti_state *s) +{ + int val; + u16 aap = tlv320_read_reg(s, TLV320_AAP); + + if (aap & TLV320_AAP_INSEL) { + if (aap & TLV320_AAP_MICM) + val = 0; + else + val = SOUND_MASK_MIC; + } else { + val = SOUND_MASK_LINE; + } + return val; +} + +/* Read Output Source */ +static int ibmcsiti_get_outsrc(struct ibmcsiti_state *s) +{ + + int val = 0; + + if (! ibmcsiti_get_volume(s)) + val |= SOUND_MASK_VOLUME; + + return val; +} + +/* Set master (line out / headphone) volume (VOLUME) */ +static int ibmcsiti_set_volume(struct ibmcsiti_state *s, int val) +{ + u16 lv = val & 0xff; + u16 rv = (val >> 8) & 0xff; + u16 dap = tlv320_read_reg(s, TLV320_DAP); + + if ( (lv == 0) && (rv == 0) ) { + /* Mute... */ + tlv320_write_reg(s, TLV320_DAP, dap | TLV320_DAP_DACM); + } else { + /* Clamp: */ + if (lv > 100) + lv = 100; + if (rv > 100) + rv = 100; + + /* Scale: headphone volumes go from 127 (+6dB) to 48 + * (-73dB == mute) everything less is also mute */ + lv = (79 * lv / 100) + 48; + rv = (79 * rv / 100) + 48; + + /* Update: */ + tlv320_write_reg(s, TLV320_LCH, lv); + tlv320_write_reg(s, TLV320_RCH, rv); + tlv320_write_reg(s, TLV320_DAP, dap & ~TLV320_DAP_DACM); + } + + return ibmcsiti_get_volume(s); +} + +/*****************************************************************************/ +/* Set Line In volume (LINE) */ +/*****************************************************************************/ +static int ibmcsiti_set_line(struct ibmcsiti_state *s, int val) { + u16 reg = tlv320_read_reg(s, TLV320_LLI) | TLV320_LLI_LRS; + + if (val <= 0) { + reg |= TLV320_LLI_LIM; /* Mute */ + } else { + if (val > 100) + val = 100; /* clamp volume */ + + /* unmute, clear volume */ + reg &= ~(TLV320_LLI_LIM | TLV320_LLI_LIV); + reg |= val * TLV320_LLI_LIV / 100; /* fill in volume */ + } + tlv320_write_reg(s, TLV320_LLI, reg); + tlv320_write_reg(s, TLV320_RLI, reg); /* For good measure */ + + return ibmcsiti_get_line(s); +} + +/*****************************************************************************/ +/* Set Mic In volume (MIC) */ +/*****************************************************************************/ +static int ibmcsiti_set_mic(struct ibmcsiti_state *s, int val) +{ + u16 reg = tlv320_read_reg(s, TLV320_AAP); + + if (val <= 0) { /* Mute */ + reg |= TLV320_AAP_MICM; + } else { + reg &= ~TLV320_AAP_MICM; /* Unmute */ + + if (val <= 50) /* 0dB boost */ + reg &= ~TLV320_AAP_MICB; + else + reg |= TLV320_AAP_MICB; + } + + tlv320_write_reg(s, TLV320_AAP, reg); + + return ibmcsiti_get_mic(s); +} + +/*****************************************************************************/ +/* Set Recording Source */ +/*****************************************************************************/ +static int ibmcsiti_set_recsrc(struct ibmcsiti_state *s, int val) +{ + if (val & SOUND_MASK_MIC) { + /* Select Mic and unmute it */ + tlv320_write_reg(s, TLV320_AAP, + (tlv320_read_reg(s, TLV320_AAP) + | TLV320_AAP_INSEL) & ~TLV320_AAP_MICM); + + } + if (val & SOUND_MASK_LINE) { + /* Select Line In */ + tlv320_write_reg(s, TLV320_AAP, + (tlv320_read_reg(s, TLV320_AAP) + & ~TLV320_AAP_INSEL) ); + } + if (! (val & (SOUND_MASK_LINE | SOUND_MASK_MIC)) ) { + /* Select Mic and mute it */ + tlv320_write_reg(s, TLV320_AAP, + tlv320_read_reg(s, TLV320_AAP) + | TLV320_AAP_INSEL | TLV320_AAP_MICM); + } + + return ibmcsiti_get_recsrc(s); +} + +/************************************************************************/ +/* Low level codec routines */ +/************************************************************************/ + +static u16 tlv320_read_reg( struct ibmcsiti_state *s, int reg) +{ + if (reg >= TLV320_REG_EXTENT) + return 0xffff; /* as good as any bad value */ + return s->codec_reg[reg]; +} + +static void tlv320_write_reg(struct ibmcsiti_state *s, int reg, u16 val) +{ + u16 temp = TLV320_REG_WRITE(reg, val); + int i; + int err; + + if (reg >= TLV320_REG_EXTENT) + BUG(); + + for (i=0; i<5; i++) { + err = i2c_master_send(s->i2c, (u8 *)&temp, sizeof(temp)); + if (err == sizeof(temp)) { + s->codec_reg[reg] = val; + udelay(100); + return; + } + printk("ibmcsiti: I2C write retry!\n"); + udelay(100); + } + + printk("ibmcsiti: I2C write error.\n"); +} + diff -urN -X bkexcl linux-2.4.22/drivers/sound/tlv320aic23.h linuxppc-2.4/drivers/sound/tlv320aic23.h --- linux-2.4.22/drivers/sound/tlv320aic23.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/sound/tlv320aic23.h 2003-03-14 20:46:44.000000000 +1100 @@ -0,0 +1,90 @@ +/* + * tlv320aic23.h : Defines for Texas Instruments TLV320AIC23 CD quality codec + * Used by ibmcsiti.c sound driver + * + * Copyright (C) 2002, Ken Inoue and David Gibson, IBM Corporation + */ + +#ifndef _TLV320AIC23_H_ +#define _TLV320AIC23_H_ + +#define TLV320_LLI 0x00 /* Left Line In Channel Vol Ctl */ +#define TLV320_RLI 0x01 /* Right Line In Channel V.C. */ +#define TLV320_LCH 0x02 /* Left Channel Headphone V.C. */ +#define TLV320_RCH 0x03 /* Right Channel Headphone V.C. */ +#define TLV320_AAP 0x04 /* Analog Audio Path Control */ +#define TLV320_DAP 0x05 /* Digital Audio Path Control */ +#define TLV320_PDC 0x06 /* Power Down Control */ +#define TLV320_DAI 0x07 /* Digital Audio Interface Fmt */ +#define TLV320_SRC 0x08 /* Sample Rate Control */ +#define TLV320_DIA 0x09 /* Digital Interface Activation */ +#define TLV320_RR 0x0F /* Reset Register */ + +#define TLV320_REG_EXTENT 10 + +/* Macros for register access */ +#define TLV320_REG_WRITE(a,b) ( ( ((u16)(a)) << 9) | \ + ( ((u16)(b)) & 0x01ff ) ) + +/* TLV320AIC23 register bit fields (short format) */ +#define TLV320_LLI_LRS 0x0100 /* L/R line simul. update (1=on)*/ +#define TLV320_LLI_LIM 0x0080 /* Left line input mute (1=mute)*/ +#define TLV320_LLI_LIV 0x001F /* Mask for left line in volume */ +#define TLV320_LLI_LIV_POR 0x17 /* POR default (0dB) */ + +#define TLV320_RLI_RLS 0x0100 /* R/L line simul. update (1=on)*/ +#define TLV320_RLI_RIM 0x0080 /* Right line in mute (1=mute) */ +#define TLV320_RLI_RIV 0x001F /* Mask for right line in vol */ +#define TLV320_RLI_RIV_POR 0x17 /* POR default (0dB) */ + +#define TLV320_LCH_LRS 0x0100 /* L/R headphone simul. update */ +#define TLV320_LCH_LZC 0x0080 /* Zero cross detect (1=on) */ +#define TLV320_LCH_LHV 0x007F /* Mask for left headphone vol */ +#define TLV320_LCH_LHV_POR 0x79 /* POR default (0dB) */ + +#define TLV320_RCH_RLS 0x0100 /* L/R headphone simul. update */ +#define TLV320_RCH_RZC 0x0080 /* Zero cross detect (1=on) */ +#define TLV320_RCH_RHV 0x007F /* Mask for left headphone vol */ +#define TLV320_RCH_RHV_POR 0x79 /* POR default (0dB) */ + +#define TLV320_AAP_STA 0x00C0 /* Mask for sidetone attenuation*/ +#define TLV320_AAP_STE 0x0020 /* Sidetone enable (1=on) */ +#define TLV320_AAP_DAC 0x0010 /* DAC off/on */ +#define TLV320_AAP_BYP 0x0008 /* Bypass */ +#define TLV320_AAP_INSEL 0x0004 /* Input select for ADC */ + /* 0 = Line, 1 = Mic */ +#define TLV320_AAP_MICM 0x0002 /* Mic mute (1=mute) */ +#define TLV320_AAP_MICB 0x0001 /* Mic boost (1=20dB) */ + +#define TLV320_DAP_DACM 0x0008 /* DAC mute (1=mute) */ +#define TLV320_DAP_DEEMP 0x0006 /* Deemphasis 00 disabled, */ + /* 01 32K, 10 44.1K, 11 48KHz */ +#define TLV320_DAP_ADCHP 0x0001 /* ADC high-pass filter (1=on) */ + +#define TLV320_PDC_OFF 0x0080 /* Device power (0=ON, 1=OFF) */ +#define TLV320_PDC_CLK 0x0040 /* Clock power */ +#define TLV320_PDC_OSC 0x0020 /* Osc power */ +#define TLV320_PDC_OUT 0x0010 /* Outputs power */ +#define TLV320_PDC_DAC 0x0008 /* DAC power */ +#define TLV320_PDC_ADC 0x0004 /* ADC power */ +#define TLV320_PDC_MIC 0x0002 /* Mic power */ +#define TLV320_PDC_LINE 0x0001 /* Line in power */ + +#define TLV320_DAI_MS 0x0040 /* Master (1) / slave (0) mode */ +#define TLV320_DAI_LRSWAP 0x0020 /* Left/Right swap (1=swap) */ +#define TLV320_DAI_LRP 0x0010 /* Signal phase */ +#define TLV320_DAI_IWL 0x000C /* Mask for input bit length */ + /* (00 = 16 bit) */ +#define TLV320_DAI_FOR 0x0003 /* Data formats */ + +#define TLV320_SRC_CLKOUT 0x0080 /* Clock output divider */ +#define TLV320_SRC_CLKIN 0x0040 /* Clock input divider */ +#define TLV320_SRC_SR 0x003C /* Sampling rate control mask */ +#define TLV320_SRC_BOSR 0x0002 /* Base oversampling rate */ + /* USB mode: 0 = 250, 1=272fs */ + /* Normal mode: 0 = 256, 1=384fs*/ +#define TLV320_SRC_USB 0x0001 /* USB mode (1) / Normal (0) */ + +#define TLV320_DIA_ACT 0x0001 /* Activate Interface (1=active)*/ + +#endif /* _TLV320AIC23_H_ */ diff -urN -X bkexcl linux-2.4.22/drivers/video/Config.in linuxppc-2.4/drivers/video/Config.in --- linux-2.4.22/drivers/video/Config.in 2003-08-24 09:04:15.000000000 +1000 +++ linuxppc-2.4/drivers/video/Config.in 2003-08-26 11:57:13.000000000 +1000 @@ -75,6 +75,12 @@ bool ' IMS Twin Turbo display support' CONFIG_FB_IMSTT bool ' S3 Trio display support' CONFIG_FB_S3TRIO tristate ' VGA 16-color graphics console' CONFIG_FB_VGA16 + if [ "$CONFIG_XILINX_OCP" = "y" ]; then + tristate ' Xilinx LCD display support' CONFIG_FB_XILINX + if [ "$CONFIG_FB_XILINX" != "n" ]; then + bool ' Rotate display' CONFIG_FB_XILINX_ROTATE + fi + fi fi if [ "$CONFIG_PARISC" = "y" ]; then bool ' Generic STI frame buffer device support' CONFIG_FB_STI @@ -90,6 +96,7 @@ if [ "$ARCH" = "alpha" ]; then tristate ' TGA framebuffer support' CONFIG_FB_TGA fi + dep_tristate ' Silicon Motion Lynx3DM frame buffer support' CONFIG_FB_LYNX $CONFIG_PCI if [ "$CONFIG_X86" = "y" ]; then bool ' VESA VGA graphics console' CONFIG_FB_VESA tristate ' VGA 16-color graphics console' CONFIG_FB_VGA16 @@ -118,6 +125,9 @@ hex ' Framebuffer Base Address' CONFIG_E1355_FB_BASE a8200000 fi fi + if [ "$CONFIG_BEECH" = "y" -o "$CONFIG_ARCTIC2" = "y" ]; then + bool 'IBM Beech LCD Controller Support' CONFIG_FB_IBMLCDC + fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ "$CONFIG_PCI" != "n" ]; then tristate ' Matrox acceleration (EXPERIMENTAL)' CONFIG_FB_MATROX @@ -389,7 +399,8 @@ "$CONFIG_FB_RADEON" = "y" -o "$CONFIG_FB_PVR2" = "y" -o \ "$CONFIG_FB_3DFX" = "y" -o "$CONFIG_FB_SIS" = "y" -o \ "$CONFIG_FB_VOODOO1" = "y" -o "$CONFIG_FB_CYBER2000" = "y" -o \ - "$CONFIG_FB_STI" = "y" -o "$CONFIG_FB_INTEL" = "y" ]; then + "$CONFIG_FB_STI" = "y" -o "$CONFIG_FB_INTEL" = "y" -o \ + "$CONFIG_FB_XILINX" = "y" ]; then define_tristate CONFIG_FBCON_CFB32 y else if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_ATY" = "m" -o \ @@ -403,7 +414,7 @@ "$CONFIG_FB_SGIVW" = "m" -o "$CONFIG_FB_SIS" = "m" -o \ "$CONFIG_FB_PVR2" = "m" -o "$CONFIG_FB_VOODOO1" = "m" -o \ "$CONFIG_FB_CYBER2000" = "m" -o "$CONFIG_FB_STI" = "y" -o \ - "$CONFIG_FB_INTEL" = "m" ]; then + "$CONFIG_FB_INTEL" = "m" -o "$CONFIG_FB_XILINX" = "m" ]; then define_tristate CONFIG_FBCON_CFB32 m fi fi diff -urN -X bkexcl linux-2.4.22/drivers/video/Makefile linuxppc-2.4/drivers/video/Makefile --- linux-2.4.22/drivers/video/Makefile 2003-08-24 09:04:15.000000000 +1000 +++ linuxppc-2.4/drivers/video/Makefile 2003-08-26 11:57:13.000000000 +1000 @@ -68,6 +68,7 @@ obj-$(CONFIG_FB_TRIDENT) += tridentfb.o fbgen.o obj-$(CONFIG_FB_S3TRIO) += S3triofb.o obj-$(CONFIG_FB_TGA) += tgafb.o fbgen.o +obj-$(CONFIG_FB_LYNX) += lynxfb.o obj-$(CONFIG_FB_VESA) += vesafb.o obj-$(CONFIG_FB_VGA16) += vga16fb.o fbcon-vga-planes.o obj-$(CONFIG_FB_VIRGE) += virgefb.o @@ -86,6 +87,8 @@ obj-$(CONFIG_FB_MAXINE) += maxinefb.o obj-$(CONFIG_FB_TX3912) += tx3912fb.o obj-$(CONFIG_FB_AU1100) += au1100fb.o fbgen.o +obj-$(CONFIG_FB_XILINX) += xilinxfb.o fbgen.o +obj-$(CONFIG_FB_IBMLCDC) += ibmlcdfb.o ibmlcds.o fbgen.o subdir-$(CONFIG_STI_CONSOLE) += sti ifeq ($(CONFIG_STI_CONSOLE),y) diff -urN -X bkexcl linux-2.4.22/drivers/video/fbmem.c linuxppc-2.4/drivers/video/fbmem.c --- linux-2.4.22/drivers/video/fbmem.c 2003-08-24 09:04:15.000000000 +1000 +++ linuxppc-2.4/drivers/video/fbmem.c 2003-08-26 11:57:13.000000000 +1000 @@ -142,6 +142,8 @@ extern int pvr2fb_setup(char*); extern int sstfb_init(void); extern int sstfb_setup(char*); +extern int ibmlcdfb_init(void); +extern int ibmlcdfb_setup(char*); static struct { const char *name; @@ -325,6 +327,9 @@ #ifdef CONFIG_FB_AU1100 { "au1100fb", au1100fb_init, au1100fb_setup }, #endif +#ifdef CONFIG_FB_IBMLCDC + { "ibmlcdfb", ibmlcdfb_init, ibmlcdfb_setup }, +#endif /* diff -urN -X bkexcl linux-2.4.22/drivers/video/ibmlcd.h linuxppc-2.4/drivers/video/ibmlcd.h --- linux-2.4.22/drivers/video/ibmlcd.h 1970-01-01 10:00:00.000000000 +1000 +++ linuxppc-2.4/drivers/video/ibmlcd.h 2003-06-04 15:21:24.000000000 +1000 @@ -0,0 +1,460 @@ +/* + * linux/drivers/video/ibmlcd.h -- + * Driver for IBM Liquid Crystal Display Controller + * - original use in PowerPC 405LP embedded platform + * + * 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 + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * David T. Eger + * Matthew Helsley + * Bishop Brock + * August 2001 + * + * March 2002 : Modified for Initial Release + * Bishop Brock, bcbrock@us.ibm.com + */ + +#ifndef __IBMLCDC_H__ +#define __IBMLCDC_H__ + +/* Provide debugging-level ioctl()'s to dump registers directly? */ +/* #define DEBUG_IBMLCD */ + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#endif + +#include