====== General description ====== The ISL38xx family of chips consist of : * an ARM 946E core clocked at 30MHz * a 32kb (SoftMAC chipsets) or 128kb (FullMAC chipsets) RAM for firmware and data storage * a 32kb (SoftMAC chipsets) or 128kb (FullMAC chipsets) Instruction Tightly Coupled Memory (TCM) * a 4kb (SoftMAC chipsets) or 8kb (FullMAC chipsets) Data TCM * a 4kb ROM (8k on the 3887) with embedded debugger * an hardware RC4 and AES cryptography accelerator * a PCI DMA-capable interface (3880 and 3886) * an USB 2.0 interface (3887) * two 16-bit General Purpose Input and Output (GPIO) ports * a timer controlled by an external 32.768kHz (typical) crystal * a baseband processor, BBP for short. This is a complete RF processing circuitry ; signals are just shifted in and from the 2.4GHz/5GHz spectrum by the remaining of the wireless card (typically, based on the ISL3686 Zero-IF transceiver) ====== From the PCI point of view ====== ===== General purpose communication registers ===== There are two such registers : ISL38XX_GEN_PURP_COM_REG_1 (0x0020) and ISL38XX_GEN_PURP_COM_REG_2 (0x0024). Each register allows a bidirectional communication with the firmware, and does not affect the device's operation in any other way. ===== Memory window ===== The ARM memory (except for TCMs, which explains why you read back the compressed firmware when attempting to read at address 0) is available through the so-called "memory window" mechanism. You set the base address of the window in the register ISL38XX_DIR_MEM_BASE_REG (0x0030) ; then the ISL38XX_MEMORY_WINDOW_SIZE (4kb) long area starting from this base address is available read-write starting from address ISL38XX_DIRECT_MEM_WIN (0x1000) in the PCI I/O space. ===== Interrupts ===== ==== Receiving interrupts from the ARM ==== When the ARM wants to send an interrupt, the chipset generates a PCI interrupt (routed to the appropriate endpoint by the NET2280 if you have an USB device) if the corresponding bit is set in the ISL38XX_INT_EN_REG (0x0018) register. When you receive such an interrupt, you have to : * write 0 to ISL38XX_INT_EN_REG to disable further interrupts * read the ISL38XX_INT_IDENT_REG (0x0010) register to know which interrupt you have to handle * write the bit(s) corresponding to the interrupt(s) you have handled in the register ISL38XX_INT_ACK_REG (0x0014) * restore ISL38XX_INT_EN_REG ==== Sending interrupts to the ARM ==== This is pretty simple : just write the bit(s) matching your interrupt request(s) in the ISL38XX_DEV_INT_REG (0x0000) register. ====== Running code on the ARM ====== The ARM 946E is standard and documented at http://www.arm.com. ===== Memory map ===== {{documentation:memory_map.png}} ===== Initialization process ===== With USB version 1 and PCI devices, firmware is loaded through the PCI bus at address 0x00020000, while the ARM core is inhibited. This can be done by setting the memory window to the address 0x00020000 and then loading the firmware by 32 bits chunks (used on PCI cards), or by accessing the hardware registers with the memory window to trigger a DMA transfer from PCI to address 0x00020000 (used on USB cards). With the ISL3887-based devices, the ROM is immediately executed by the ARM on power up and writes the firmware at 0x00020000, reading data directly through the USB port. Then, the ARM takes the reset vector at address 0x00000000. At this time, no TCM is enabled so this is an alias for 0x00020000. The firmware immediately goes to its real start address by setting the program counter in the 0x00020000 RAM bank, and enables the two TCMs using coprocessor 15 (see ARM documentation). If the firmware is compressed, it decompresses itself in the Instruction TCM. If not, it performs a simple data copy from RAM to TCM. This scheme allows faster execution speeds, since TCM is faster than RAM. Then, it allocates arrays in the data TCM. The data stored there can be anything. The stack pointer should be set somewhere in this bank, since it's faster. After this process, you can safely jump to C code in the Instruction TCM. Once the system is running from the TCM, you can safely use the memory bank at 0x00020000 for other purposes (that's why the compressed firmware we read back is corrupted). The SoftMAC firmware uses it for DMA transfers (and the "SoftMAC addresses" are the destination addresses in this bank), because you can't do a DMA transfer involving a TCM. ====== The event subsystem ====== The hardware can request the attention of the ARM through the event subsystem. Each event is assigned its own bit. The hardware can be configured so that a particular event can be ignored, handled as an IRQ or handled as a FIQ (see below). The IRQ or FIQ routine must contain code to clear the event source, otherwise the IRQ or FIQ will be re-trigerred as soon as it terminates. The clearing process is specific to each kind of event. ====== Hardware registers ====== ===== Event handling control (0xc0000000-0xc00001fc) ===== ==== Interrupt control registers (0xc0000000-0xc00000fc) ==== * REG38XX_IRQ_IDENT_MASKED (0xc0000000) * REG38XX_IRQ_IDENT_UNMASKED (0xc0000004) * REG38XX_IRQ_MASK_ENABLE (0xc0000008) * REG38XX_IRQ_MASK_DISABLE (0xc000000c) When an event is generated by the hardware, the appropriate bit is set in REG38XX_DEV_INT_IDENT_UNMASKED. This register is ANDed with a bitmask and the resulting value is put in REG38XX_DEV_INT_IDENT_MASKED. Every time a bit is set in REG38XX_DEV_INT_IDENT_MASKED, the IRQ line is asserted on the ARM, and the core will take the interrupt vector if the CPSR is set up accordingly. Bitmask manipulation : * to set one (or more) bit(s), write them to REG38XX_DEV_INT_MASK_ENABLE * to clear one (or more) bit(s), write them to REG38XX_DEV_INT_MASK_DISABLE * to read the current bitmask, read REG38XX_DEV_INT_MASK_ENABLE ==== Fast interrupt control registers (0xc0000100-0xc00001fc) ==== * REG38XX_FIQ_IDENT_MASKED (0xc0000100) * REG38XX_FIQ_IDENT_UNMASKED (0xc0000104) * REG38XX_FIQ_MASK_ENABLE (0xc0000108) * REG38XX_FIQ_MASK_DISABLE (0xc000010c) These registers work the same as above, except that REG38XX_FIQ_IDENT_MASKED obviously triggers FIQs instead of IRQs. REG38XX_FIQ_IDENT_UNMASKED is a copy of REG38XX_IRQ_IDENT_UNMASKED. ===== System control registers (0xc0000200-0xc000021c) ===== The register REG38XX_MAC_ID (0xc0000200) contains a 32-bit number containing a reference number of the MAC (ie. the ARM CPU plus its peripherals : DMA controller, PCI, timers...) The three least significant bits in the REG38XX_CORE_THROTTLE (0xc0000210) register are used to reduce the ARM core frequency, according to this table : ^ REG38XX_CORE_THROTTLE ^ Core frequency ^ | 000 | 30MHz | | 001 | 30MHz | | 010 | 18MHz | | 011 | 9MHz | | 100 | 4.5MHz | | 101 | 1.8MHz | | 110 | 900kHz | | 111 | 450kHz | ===== Timer registers (0xc0000280-0xc000029c) ===== The register REG38XX_TIMER_VALUE (0xc0000280) can be set to any value, and increments automatically every millisecond. The register at 0xc0000288 can control the timer so that it sends the events 0x000004 and 0x000001 or reset the chipset - a full reset, not only a jump at address 0 (watchdog system ?) - ; this remains to be understood in detail. ===== GPIO registers (0xc0000380-0xc00003bc ; 0xc0000400-0xc000043c) ===== Take all that follows with a grain of salt. Further reading of the firmware clearly indicates that it is much more subtle than this. More subtle in that 1/ it seems that some wires can control different lines (eeprom seems to act on same gpio bits as leds) 2/ it seems that the led may work this way, but this GPIO mechanism is much more general than what we observe with the leds ==== Overview ==== The two 16-bit GPIO ports are controlled by six registers (three each port) : * REG38XX_GPIO1_WRITE (0xc0000380) * REG38XX_GPIO1_READ (0xc0000384) * REG38XX_GPIO1_CONTROL (0xc0000388) * REG38XX_GPIO2_WRITE (0xc0000400) * REG38XX_GPIO2_READ (0xc0000404) * REG38XX_GPIO2_CONTROL (0xc0000408) Writing these registers is done using a bitmask in the 16 most significant bits, and the value in the 16 least significant bits. The bitmask specifies which bits must be changed to the value in the actual 16-bit register. For instance : * writing 0x00010001 to 0xc0000408 sets bit 1 in REG38XX_GPIO2_CONTROL * writing 0x00010000 to 0xc0000408 clears bit 1 in REG38XX_GPIO2_CONTROL * writing 0x00000001 to 0xc0000408 has no effect ==== Using the GPIO port ==== The bits set in REG38XX_GPIOx_CONTROL define which pins should be configured as outputs. Then, you can set or clear these outputs by setting or clearing bits in REG38XX_GPIOx_WRITE, using the scheme above. Reading GPIO is as simple as reading REG38XX_GPIOx_READ. In its least significant bits, it will show the status of the inputs, as well as that of the outputs. Its most significant bits are always 0. ==== Typical GPIO connections ==== * the LEDs are connected to pins 0x4000 and 0x0200 on port 2. If your device has just one LED, it can be controlled by either pin, depending how your card manufacturer designed the schematics. According to the scheme above : * writing 0x42004200 to REG38XX_GPIO2_CONTROL puts the two LEDs in a known state, either on or off. That strangeness is because REG38XX outputs are push-pull, and device manufacturers connect the LEDs between the GPIO and VDD (current sink configuration) or between GPIO and GND (current source configuration). * writing 0x42004200 to REG38XX_GPIO2_WRITE changes the state of the two LEDs * writing 0x42000000 to REG38XX_GPIO2_WRITE puts back the LEDs to their original state * the I2C bus, on which lies the device-specific configuration EEPROM, is connected to pins 0x0001 and 0x0002 (respectively SCL and SDA) of the GPIO port 2. This is very low-level - just see I2C documentations if you want to use this port. * FIXME there are many unknown GPIO connections ===== Hardware AES accelerator (0xc0000500-0xc000057c) ===== FIXME anyone familiar with crypto here ?... This may also be RC4. Repeated reads at 0xc0000510 seem to read some algorithm output, byte per byte. ===== PCI registers (0xc0000900-0xc00009fc) ===== All PCI registers are mapped at address 0xc0000900 plus their PCI address. For instance, if you want to access the PCI register 0x0024 (ISL38XX_GEN_PURP_COM_REG_2), you only have to access the memory location 0xc0000924. ==== General purpose communication registers ==== This is trivial, just read/write 0xc0000920 and 0xc0000924. ==== Interrupts ==== === Receiving interrupts from the PCI bus === The PCI interrupts can be assigned the event ISL38XX_EVENT_HOST (0x000040), by writing the bits matching the interrupt(s) you want to generate this event in the REG38XX_DEV_INT_EN_REG (0xc0000908) register. When a PCI interrupt request occurs, you have to read REG38XX_DEV_INT_REG (0xc0000900) to know what IRQ(s) happened, then you acknowledge it/them, clearing the bit(s) in REG38XX_DEV_INT_REG as well as the event source, by setting the appropriate bit(s) in REG38XX_DEV_INT_ACK_REG (0xc0000904). It is possible to handle several interrupts at the same time. === Sending interrupts to the PCI bus === You only have to set the appropriate bit(s) in the REG38XX_INT_IDENT_REG (0xc0000910) register. ===== DMA transfers (0xc00000f00-0xc0000f3c plus PCI registers) ===== ==== Transferring data from the PCI bus to the ARM ==== The registers used are : * REG38XX_RXDMA_CONTROL (0xc0000f20) * REG38XX_RXDMA_MAXCNT (0xc0000f24) * REG38XX_RXDMA_BASE (0xc0000f28) * REG38XX_RXDMA_STATUS (0xc0000f2c) * REG38XX_RXDMA_CURRENT (0xc0000f30) * REG38XX_RXDMA_REMAIN (0xc0000f34) * REG38XX_DMA_MASTER_ADDRESS (0xc0000960) * REG38XX_DMA_MASTER_LEN (0xc0000964) * REG38XX_DMA_MASTER_CONTROL (0xc0000968) A DMA transfer is triggered by doing the following : * REG38XX_RXDMA_CONTROL must be equal to 1 * write the size of the transfer in bytes to REG38XX_RXDMA_MAXCNT * write the target address (ARM) to REG38XX_RXDMA_BASE * write the source address (PCI) to REG38XX_DMA_MASTER_ADDRESS * write the size of the transfer in 32-bits words to REG38XX_DMA_MASTER_LEN * write 0x4 to REG38XX_DMA_MASTER_CONTROL After a delay (it should be possible to use events, needs reverse engineering), read REG38XX_RXDMA_STATUS. A value of 3 means that the DMA transfer was successful ; but if this register reads 2, no data has been transferred. ==== Transferring data from the ARM to the PCI bus ==== The registers used are : * REG38XX_TXDMA_CONTROL (0xc0000f00) * REG38XX_TXDMA_MAXCNT (0xc0000f04) * REG38XX_TXDMA_BASE (0xc0000f08) * REG38XX_TXDMA_STATUS (0xc0000f0c) * REG38XX_TXDMA_CURRENT (0xc0000f10) * REG38XX_TXDMA_REMAIN (0xc0000f14) * REG38XX_DMA_MASTER_ADDRESS (0xc0000960) * REG38XX_DMA_MASTER_LEN (0xc0000964) * REG38XX_DMA_MASTER_CONTROL (0xc0000968) Same as above, but write 0xc to REG38XX_DMA_MASTER_CONTROL. ===== The BBP ===== The register REG38XX_BBP_ID (0xc0000600) contains the identification number of the BBP. JB, can you add your registers about frequency ? ====== The USB controller ====== * REG38XX_EP_CONTROL (0xc1000220) * REG38XX_EP_STATUS (0xc1000224) * REG38XX_EP_PACKETFRAME (0xc1000228) * REG38XX_EP_MAXPACKETSIZE (0xc100022c) * REG38XX_EP_SETUPTR (0xc1000230) * REG38XX_EP_DESCPTR (0xc1000234) * REG38XX_EP_INT_DISABLE (0xc1000418) ====== The serial EEPROM ====== It is a standard I2C EEPROM (ie. 24C64) connected to the GPIO pins of the 38xx (see above). The first part of the EEPROM is used by the hardware to configure the host interface (USB for the ISL3887, PCI for the other chipsets) with no intervention of the firmware. The remaining space is used by the device manufacturers to store information such as the MAC address and authorized frequencies (according to which country the device is sold in). The whole EEPROM can be read by the firmware. The FullMAC firmware parses it and acts accordingly, whereas the SoftMAC firmware gives a read access to any portion of the EEPROM (see [[The SoftMAC protocol]]) and parsing it is left to the host driver. ====== The ROM ====== * When the device is powered up, it reads the serial EEPROM and configures the host interface (ie. USB IDs for the 3887, PCI IDs for other chipsets) so that firmware can be downloaded from the host. I'm not sure of this seb, i'm almost sure of the opposite : USB IDs and PCI IDs have nothing to do with the bootrom. * Then, the command line interface is enabled. On the 3887, it is used to download the firmware from the USB bus and to launch it, through the "<" and "g" commands * When the firmware crashes, the ROM is executed. On the 3887, it sends a core dump whose contents remain to be understood. On the 3886, it enters command line interface. The command line interface is implemented with the [[UARTPCI protocol]] on the 3886, and directly on endpoints 0x01/0x81 on the 3887. The known ROM commands are : * **r** : shows register status, example : rom > r SVC: r0=0000000b r1=eeff0000 r2=00000028 r3=00000003 r4=80000000 r5=a0001088 r6=a00010a4 r7=0003ff04 r8=c0000000 r9=a0000000 sl=00000000 fp=a000020c ip=c0000000 sp=00027cc8 lr=e00008db pc=e0000068 ss=00000010 cs=80000097 USR: sp=bcb5944f lr=0bfee3bd ABT: sp=c76149df lr=0000561c ss=80000092 UND: sp=a0000598 lr=00004208 ss=00000010 IRQ: sp=a0000484 lr=00005354 ss=2000001b FIQ: r8=c0000000 r9=a0000000 sl=00000001 fp=0000000f ip=80000000 sp=a0000390 lr=00000000 ss=20000092 * **w address value** : writes to memory, example : "w 0xc0000400 0x00300030" * **<** : dowloads firmware * **g** : jumps to firmware * **help** (3887 only) : displays this : rom > help c src dst len - copy d[bw] addr [len] - dump f addr len data... - fill g [addr] - go h - help l val - lines < [[addr] len] - load p [area] - print r - registers s addr len data... - search w[bw] addr data... - write