This sample is a minidriver for the Media5200b board. The driver initializes the serial port at a known baud rate and buffers the characters into the data area. Once the system is booted, a program can then read this data area and retrieve the buffered characters. For this implementation, we require:
The prototype for the minidriver handler function is as follows:
int mdriver_handler(int state, void *data);
The state value informs the handler function where in the boot process it's being called from. See the mdriver_add() documentation in the API and Datatypes chapter of this guide. The data parameter is a virtual pointer to the minidriver's allocated data area. For this sample driver, the source code for the handler function would look like:
struct mserial_data { psc_regs *psc; psc_regs *psc_k; uint16_t intr_calls; uint16_t ncalls; uint16_t errors; uint16_t last_count; uint16_t data_len; }; /* * Hardware initialization function which only gets called * the first time the mini-driver is run. * The Baud, Clk and port are hardcoded for this example * baud = 14400 * clk = 132000000 * port = PSC1 */ static psc_regs * init_hw() { /* hardcode to PSC1, baud and clk */ int baud = 14400; int clk = 132000000; paddr64_t psc_base = MGT5200_MBAR_BASE + 0x2000; psc_regs *psc; gpio_std_regs *gpio; if ((psc = (psc_regs *)(startup_memory_map(0xA0,psc_base, PROT_READ|PROT_WRITE|PROT_NOCACHE))) == 0) return (0); if ((gpio = (gpio_std_regs*)(startup_memory_map(0x40,(MGT5200_MBAR_BASE + 0x0b00), PROT_READ|PROT_WRITE|PROT_NOCACHE))) == 0) { startup_memory_unmap((void *)psc); return (0); } baud = ( (clk / baud) + 16 ) / 32 ; /* calculate baud rate */ psc->CR = PSC_CR_TXD | PSC_CR_RXD; psc->SICR = 0x00000000; /* write to SICR: SIM2 = uart mode,dcd does not affect rx */ psc->SR_CSR = 0xdd00; /* write to CSR: RX/TX baud rate from timers */ psc->CTUR = (baud >> 8) & 0xff; /* write to CTUR: divide counter upper byte */ psc->CTLR = baud & 0xff; /* write to CTLR: divide counter lower byte */ psc->CR = 0x20; /* write to CR: reset RX and RXFIFO */ psc->CR = 0x30; /* write to CR: reset TX and TXFIFO */ psc->CR = 0x40; /* write to CR: reset error status */ psc->CR = 0x50; /* write to CR: reset break change int */ psc->CR = 0x10; /* write to CR: reset MR pointer */ psc->IPCR_ACR = 0x00; /* write to ACR: disable state change CTS/ DCD interrupts */ psc->ModeReg = 0x73; /* write to MR1: 8bit data, no parity */ psc->ModeReg = 0x07; /* write to MR2: normal mode,1stop bit */ psc->OP1 = 1; /* Write to OP1: enable RTS to send */ psc->ISR_IMR = 0x0000; /* write to IMR: Mask RxRDY and TxRDY from causing an interrupt */ psc->RFALARM = 511; psc->TFALARM = 0xF8; psc->RFCNTL = 0x04; gpio->port_config = (gpio->port_config & GPIO_PSC1_MASK) | GPIO_PSC1_UART_ENABLE; /* Enable the transmitters and receivers */ psc->CR = 0x05; /* write to CR: enable TX and RX.*/ psc->ISR_IMR = PSC_ISR_RxRDY; startup_memory_unmap((void *)gpio); /* return mapped register pointer */ return (psc); } int mini_serial(int state, void *data) { uint8_t *bp = 0; paddr64_t psc_base = MGT5200_MBAR_BASE + 0x2000; uint16_t count; uint8_t *dptr; struct mserial_data *mdata; mdata = (struct mserial_data *) data; dptr = (uint8_t *) (mdata + 1); if (state == MDRIVER_INTR_ATTACH) { /* disable the serial interrupt */ mdata->psc->ISR_IMR = 0x0000; * make sure there are no characters in the hardware fifo */ count = 0; while (mdata->psc->SR_CSR & PSC_SR_RxRDY) { bp = (uint8_t*) &mdata->psc->RB_TB; dptr[mdata->data_len+count] = *bp; count++; } mdata->last_count = count; count = count + mdata->data_len; mdata->data_len = count; /* reset any error conditions */ if ((mdata->psc->SR_CSR & PSC_SR_ORERR) || (mdata->psc->SR_CSR & PSC_SR_RB) || (mdata->psc->SR_CSR & PSC_SR_FE) || (mdata->psc->SR_CSR & PSC_SR_PE)) { mdata->psc->CR = PSC_CR_RESET_ERR; count = mdata->errors + 1; mdata->errors = count; } return (1); } else if (state == MDRIVER_INIT) { /* the first time called initialize the hardware and do data setup */ mdata->psc = init_hw(); if (mdata->psc == 0) return (1); mdata->intr_calls = 0; mdata->ncalls = 0; mdata->errors = 0; mdata->data_len = 0; } else if (state == MDRIVER_STARTUP_PREPARE) { /* once we are out of startup use callout_io_map */ if ((mdata->psc_k = (psc_regs *)(callout_memory_map(0xA0,psc_base, PROT_READ|PROT_WRITE|PROT_NOCACHE))) == 0) { /* something bad so disable the driver */ mdata->psc->ISR_IMR = 0x0000; return (1); } } /* count the number of times the mini-driver is called */ count = mdata->ncalls + 1; mdata->ncalls = count; if (state == MDRIVER_PROCESS) { /* called because of an interrupt */ count = mdata->intr_calls + 1; mdata->intr_calls = count; } count = 0; while (mdata->psc->SR_CSR & PSC_SR_RxRDY) { bp = (uint8_t*) &mdata->psc->RB_TB; dptr[mdata->data_len+count] = *bp; count++; } mdata->last_count = count; count = count + mdata->data_len; mdata->data_len = count; /* reset any error conditions */ if ((mdata->psc->SR_CSR & PSC_SR_ORERR) || (mdata->psc->SR_CSR & PSC_SR_RB) || (mdata->psc->SR_CSR & PSC_SR_FE) || (mdata->psc->SR_CSR & PSC_SR_PE)) { mdata->psc->CR = PSC_CR_RESET_ERR; count = mdata->errors + 1; mdata->errors = count; } if (state == MDRIVER_STARTUP_FINI) mdata->psc = mdata->psc_k; return (0); }
In this example, the handler function stores call information, so a structure has been created to allow easier access to the data area. The data area is filled with the received characters.
Access to the serial port registers is necessary. At initialization time, the registers are mapped with startup_memory_map(), and the pointer is stored in the data area during the startup phase. The registers are mapped in with startup_memory_map(). Once the startup phase is complete, the handler must use callout_memory_map() to access the registers.
During the MDRIVER_INIT state, the data area is initialized, and the serial hardware is set up; this is called only once. At this time, the startup_memory_map() is called to map in the hardware registers, and the pointer is stored in the data area. This pointer is used for hardware access until the handler is called with a state of STARTUP_MDRIVER_PREPARE. At this time callout_memory_map() can be called and the pointer is stored in the data area, however the startup_memory_map() pointer should still be used. Once the handler is called with a state of MDRIVER_STARTUP_FINI, the handler starts using the pointer returned by callout_memory_map() for all hardware access. This pointer is then used for all further invocations of the minidriver handler.
When the handler is called with a state of MDRIVER_INTR_ATTACH, it disables the device interrupt and returns a value of 1 requesting an exit of the handler.
Once you have written a handler function, you need to register it with startup and allocate the required system RAM for your data area. This can be accomplished with the following functions:
paddr_t alloc_ram(phys_addr, size, alignment); int mdriver_add(name, interrupt, handler, data_paddr, data_size);
Since we are allocating memory and passing an interrupt these functions must be called after RAM is initialized by calling init_raminfo(), and after the interrupt information is added to the system page by calling init_intrinfo(). The main() function of our startup main.c will look like this:
... paddr_t mdrvr_addr; ... /* * Collect information on all free RAM in the system. */ init_raminfo(); // // In a virtual system, we need to initialize the page tables // if(shdr->flags1 & STARTUP_HDR_FLAGS1_VIRTUAL) init_mmu(); /* * The following routines have hardware or system dependencies which * may need to be changed. */ init_intrinfo(); mdrvr_addr = alloc_ram(~0L, 65536, 1); /* grab 64k */ mdriver_add("mini-serial", 0, mini_serial, mdrvr_addr, 65536); ...
In this example, we have allocated 64 KB of memory and registered our minidriver handler function with the name mini-serial.
The minidriver is complete now, and you must rebuild startup and boot image. In order to verify that the minidriver is running properly, you may want to add debug information into the handler function or write an application to read the minidriver data area and print the contents.
The Media5200b board has a single serial port that this minidriver example uses to receive bytes on. In order to test this driver, you should build an OS image that sets up TCP/IP and lets you telnet to the board. Since you'll be using the serial port as a data source; don't run the serial driver and don't put debug printouts or verbosity on procnto in your image. Once you have an image, you should burn it onto the onboard flash where you can boot from it.
Connect a NULL-modem cable between your Media5200b board and a host machine. The minidriver is running at 14400 baud, with no flow control. You must make sure that the host machine's serial port is also configured in this manner. On the host machine, begin sending characters to the serial port. Sample code should look like:
... uint8_t test_data[20] = {"012345678987654321"}; ... if ((fd = open("/dev/ser1", O_RDWR)) == -1) { fprintf(stderr, "Unable to open device: %s (errno=%d\n", device, errno); return (-1); } for (;;) { write(fd, test_data + index, 1); index++; if (index == 19) index = 0; }
This code sends a pattern of characters to the serial port so that the minidriver can capture data.
Now boot the Media5200b board and telnet to it.
Once connected, you can run the sample full driver, mdrvr-serpsc that does the following:
Since the mdrvr-serpsc application is a sample serial driver you don't need to run devc-serpsc driver.
These timings are based on sample boot image with the following properties:
mdriver_max | Number of calls | Average calling interval |
---|---|---|
1 KB | 855 | 514 microseconds |
4 KB | 231 | 2.04 ms |
16 KB | 75 | 8.13 ms |
Boot time | First invocation time |
---|---|
453.2 ms | 1 ms |
Boot time identifies the time from startup to the start of the first application process. The first invocation time is the time from IPL to the first invocation of the minidriver (MDRIVER_INIT). |
This sample is a minidriver for the Biscayne board. The driver initializes the serial port at a known baud rate and buffers the characters into the data area. Once the system is booted, a program can then read this data area and retrieve the buffered characters.
For this implementation we require:
The prototype for the minidriver handler function is as follows:
int mdriver_handler(int state, void *data);
The state value informs the handler function where in the boot process it's being called from. See the mdriver_add() documentation in the API and Datatypes chapter of this guide. The data parameter is a virtual pointer to the minidriver's allocated data area. For this sample driver, the source code for the handler function would look like this:
struct mserial_data { uintptr_t port; uintptr_t port_k; uint16_t intr_calls; uint16_t ncalls; uint16_t errors; uint16_t err; uint16_t last_count; uint16_t data_len; }; void Delay(int n) { unsigned int i; for(i=0;i<n;i++); } uintptr_t init_hw() { int cnt; uint64_t base = SH_SCIF_BASE; int baud = 14400; int clk = 33333333; unsigned port; if ((port = startup_io_map(120, base)) == NULL) return (NULL); cnt = clk / (baud * 64 / 2) - 1; // assumes SCSMR1.CKS = 0 // set clock selection out16(port + SH_SCIF_SCSCR_OFF, 0x0); Delay(1); // Enable reset state of the FIFO register out16(port + SH_SCIF_SCFCR_OFF, SH_SCIF_SCFCR_TFRST | SH_SCIF_SCFCR_RFRST); // data transfer format // 8 bits no parity // CKS = 0 out16(port + SH_SCIF_SCSMR_OFF,0); Delay(1); // baud rate out8(port + SH_SCIF_SCBRR_OFF,cnt); Delay(1); //Disable the reset state of the fifo set_port16(port + SH_SCIF_SCFCR_OFF, SH_SCIF_SCFCR_TFRST | SH_SCIF_SCFCR_RFRST, 0); // FIFO control out16(port + SH_SCIF_SCFCR_OFF + 4, 0x0); Delay(1); out8(port + SH_SCIF_SCFTDR_OFF, 0); Delay(1); out16(port + SH_SCIF_SCSPTR_OFF + 4, 0x41); out16(port + SH_SCIF_SCFCR_OFF, 0x30 | 0x200 | SH_SCIF_SCFCR_MCE); out16(port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_RE | SH_SCIF_SCSCR_TE | SH_SCIF_SCSCR_TIE | SH_SCIF_SCSCR_RIE); Delay(1); out16(port + SH7760_SCIF_SCSPTR_OFF, SH_SCIF_SCSPTR_RTSIO); out16(port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_RE | SH_SCIF_SCSCR_RIE); return (port); } int mini_serial(int state, void *data) { uint8_t *bp = 0; uint16_t count; uint8_t *dptr, c; struct mserial_data *mdata; int num, i; int pending_interrupts; mdata = (struct mserial_data *) data; dptr = (uint8_t *) (mdata + 1); if (state == MDRIVER_INTR_ATTACH) { /* disable the serial interrupt */ set_port16(mdata->port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_RE | SH_SCIF_SCSCR_RIE, 0); return (1); } else if (state == MDRIVER_INIT) { /* the first time called initialize the hardware and do data setup */ mdata->port = init_hw(); if (mdata->port == 0) return (1); mdata->intr_calls = 0; mdata->ncalls = 0; mdata->errors = 0; mdata->data_len = 0; } else if (state == MDRIVER_STARTUP_PREPARE) { /* once we are out of startup use callout_io_map */ if ((mdata->port_k = callout_io_map(0x120, SH_SCIF_BASE)) == 0) { /* something bad so disable the driver */ set_port16(mdata->port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_RE | SH_SCIF_SCSCR_RIE, 0); return (1); } } /* count the number of times the mini-driver is called */ count = mdata->ncalls + 1; mdata->ncalls = count; if (state == MDRIVER_PROCESS) { /* called because of an interrupt */ count = mdata->intr_calls + 1; mdata->intr_calls = count; } /* get the data from the serial port and clear any errors */ // Handle the error messages: Overrun, Framing, Parity pending_interrupts = in16(mdata->port + SH_SCIF_SCFSR_OFF); if (in8(mdata->port + SH7760_SCIF_SCLSR_OFF) & SH_SCIF_SCLSR_ORER) { set_port16(mdata->port + SH7760_SCIF_SCLSR_OFF, SH_SCIF_SCLSR_ORER, 0); } if ((pending_interrupts & SH_SCIF_SCFSR_PER)) { set_port16(mdata->port + SH_SCIF_SCFSR_OFF, SH_SCIF_SCFSR_PER | SH_SCIF_SCFSR_ER | SH_SCIF_SCFSR_DR, 0); } if (pending_interrupts & SH_SCIF_SCFSR_FER) { set_port16(mdata->port + SH_SCIF_SCFSR_OFF, SH_SCIF_SCFSR_FER | SH_SCIF_SCFSR_ER | SH_SCIF_SCFSR_DR, 0); } if (pending_interrupts & SH_SCIF_SCFSR_BRK ) { set_port16(mdata->port + SH_SCIF_SCFSR_OFF, SH_SCIF_SCFSR_BRK | SH_SCIF_SCFSR_DR, 0); } /* grab data */ if (in16(mdata->port + SH7760_SCIF_SCRFDR_OFF) ) { c = in8(mdata->port + SH_SCIF_SCFRDR_OFF); set_port16(mdata->port + SH_SCIF_SCFSR_OFF, SH_SCIF_SCFSR_RDF, 0); dptr[mdata->data_len] = c; mdata->data_len = mdata->data_len + 1; } if (pending_interrupts & (SH_SCIF_SCFSR_TDFE|SH_SCIF_SCFSR_TEND) ) { set_port16(mdata->port + SH_SCIF_SCSCR_OFF, SH_SCIF_SCSCR_TIE,0); } //Clear all the interrupts set_port16(mdata->port + SH7760_SCIF_SCLSR_OFF, SH_SCIF_SCLSR_ORER, 0); set_port16(mdata->port + SH_SCIF_SCFSR_OFF, 0xff, 0); if (state == MDRIVER_STARTUP_FINI) mdata->port = mdata->port_k; return (0); }
In this example, the handler function stores call information, so a structure has been created to allow easier access to the data area. The data area is filled with the received characters.
During the MDRIVER_INIT state, the data area is initialized and the serial hardware is set up; this is called only once. At this time, the startup_io_map() is called to map in the hardware registers, and the pointer is stored in the data area.
This pointer is used for hardware access until the handler is called with a state of STARTUP_MDRIVER_PREPARE. At this time, callout_io_map() is called and the pointer is stored in the data area, however the startup_io_map() pointer should still be used. Once the handler is called with a state of MDRIVER_STARTUP_FINI, the handler starts using the pointer returned by callout_io_map() for all hardware access. This pointer is then used for all further invocations of the minidriver handler.
When the handler is called with a state of MDRIVER_INTR_ATTACH; it disables the device interrupt and returns a value of 1 requesting an exit of the handler.
Once you have written a handler function, you need to register it with startup and allocate the required system RAM for your data area. This is accomplished with the following functions:
paddr_t alloc_ram(phys_addr, size, alignment); int mdriver_add(name, interrupt, handler, data_paddr, data_size);
Since you're allocating memory and passing an interrupt, these functions must be called after RAM is initialized by calling init_raminfo(), and after the interrupt information is added to the system page by calling init_intrinfo(). The main() function of our startup main.c looks like this:
... paddr_t mdrvr_addr; ... /* * Collect information on all free RAM in the system. */ init_raminfo(); // // In a virtual system we need to initialize the page tables // if(shdr->flags1 & STARTUP_HDR_FLAGS1_VIRTUAL) init_mmu(); /* * The following routines have hardware or system dependencies which * may need to be changed. */ init_intrinfo(); mdrvr_addr = alloc_ram(~0L, 65536, 1); /* grab 64k */ mdriver_add("mini-serial", 0, mini_serial, mdrvr_addr, 65536); ...
In this example, you have allocated 64 KB of memory and registered the minidriver handler function with the name mini-serial.
The minidriver is now complete, and you should rebuild startup and the boot image. In order to verify that the minidriver is running properly, you may want to add debug information into the handler function or write an application to read the minidriver data area and print the contents.
The Biscayne board has a debug serial port that this minidriver example uses to receive bytes on. In order to test this driver, you should build an OS image that sets up TCP/IP and lets you telnet to the board. Since you're using the serial port as a data source, don't run the serial driver and don't put debug printouts or verbosity on procnto in your image. Once you have an image, you should burn it onto the onboard flash where you can boot from it.
Connect a NULL-modem cable between your Biscayne board and a host machine. The minidriver is running at 14400 baud, so make sure the host machine's serial port is also configured in this manner. On the host machine, begin sending characters to the serial port. Sample code should look like this:
... uint8_t test_data[20] = {"012345678987654321"}; ... if ((fd = open("/dev/ser1", O_RDWR)) == -1) { fprintf(stderr, "Unable to open device: %s (errno=%d\n", device, errno); return (-1); } for (;;) { write(fd, test_data + index, 1); index++; if (index == 19) index = 0; }
This code sends a pattern of characters to the serial port so that the minidriver can capture data.
Now boot the Biscayne board and telnet to it. Once connected, you can run the sample full driver, mdrvr-sescif that does the following:
Since the mdrvr-serscif application is a sample serial driver, you don't need to run the devc-sersci driver.
These timings are based on sample boot image with the following properties:
mdriver_max | Number of calls | Average calling interval |
---|---|---|
1 KB | 715 | 59 microseconds |
4 KB | 139 | 194 microseconds |
16 KB | 65 | 934 microseconds |
Boot time | first invocation time |
---|---|
636 ms | 1 ms |
Boot time identifies the time from startup to the start of the first application process. The first invocation time is the time from IPL to the first invocation of the minidriver (MDRIVER_INIT). |
This sample is a minidriver for the OMAP 5912 board. The driver initializes the serial port at a known baud rate and buffers the characters into the data area. Once the system is booted, a program can then read this data area and retrieve the buffered characters. For this implementation we require:
The prototype for the minidriver handler function is as follows:
int mdriver_handler(int state, void *data);
The state value informs the handler function where in the boot process it is being called from. See the mdriver_add() documentation in the API and Datatypes chapter of this guide. The data parameter is a virtual pointer to the minidriver's allocated data area. For this sample driver, the source code for the handler function should look like this:
struct mserial_data { uintptr_t port; uintptr_t port_k; uint16_t intr_calls; uint16_t ncalls; uint16_t errors; uint16_t err; uint16_t last_count; uint16_t data_len; }; #ifndef write_omap #define write_omap(__port,__val) out8(__port,__val) #endif #ifndef read_omap #define read_omap(__port) in8(__port) #endif /* * Initialize the UART hardware * base address = 0xfffb0000 * baud rate = 14400 * data = 8 n 1 */ uintptr_t init_hw() { unsigned value = 0; unsigned msr, c; uintptr_t port; uint64_t base = 0xfffb0000; int baud = 14400; if ((port = startup_io_map(OMAP_UART_SIZE, base)) == 0) return (0); // hit LCR with special byte to enable access to the Enhanced Feature Register (EFR) write_omap(port + OMAP_UART_LCR,0xbf); // turn off S/W flow control, enable writes to MCR[7:5], FCR[5:4], and IER[7:4] write_omap(port + OMAP_UART_EFR, 0x10); write_omap(port + OMAP_UART_LCR,0); // set MCR bit 6 to enable access to TCR and TLR registers write_omap(port + OMAP_UART_MCR, 0x40); value = 0x0; // set TCR - RX FIFO - start Rx at 0 bytes, halt at rx fifo value write_omap(port + OMAP_UART_TCR,value >> 4); write_omap(port + OMAP_UART_TLR, value); // disable access to TCR and TLR write_omap(port + OMAP_UART_MCR, 0x00); write_omap(port + OMAP_UART_SCR, 0x00); write_omap(port + OMAP_UART_LCR,0xbf); // disable access to MCR[7:5], FCR[5:4], and IER[7:4], // disable auto flow control and sw flow control write_omap(port + OMAP_UART_EFR, 0x00); // set Divisor Latch Enable - writing something other // than 0xbf to LCR puts it back in "normal" mode write_omap(port + OMAP_UART_LCR, 0x80); // baud and clk value = 48000000 / (16 * baud); write_omap(port + OMAP_UART_DLL, value); write_omap(port + OMAP_UART_DLH, (value >> 8) & 0xff); write_omap(port + OMAP_UART_LCR, 0x13); // turn on DTR, RTS c = read_omap(port + OMAP_UART_MCR); write_omap(port + OMAP_UART_MCR, (c & ~OMAP_MCR_DTR|OMAP_MCR_RTS) | OMAP_MCR_DTR|OMAP_MCR_RTS); // According to the National bug sheet you must wait for the transmit // holding register to be empty. do { } while((read_omap(port + OMAP_UART_LSR) & OMAP_LSR_TXRDY) == 0); // Clean the device so we get a level change on the intr line to the bus. // Enable out2 (gate intr to bus) c = read_omap(port + OMAP_UART_MCR); write_omap(port + OMAP_UART_MCR, (c & ~OMAP_MCR_OUT2) | OMAP_MCR_OUT2); write_omap(port + OMAP_UART_IER, 0); // Disable all interrupts read_omap(port + OMAP_UART_LSR); // Clear Line Status Interrupt read_omap(port + OMAP_UART_RHR); // Clear RX Interrupt read_omap(port + OMAP_UART_THR); // Clear TX Interrupt read_omap(port + OMAP_UART_MSR); // Clear Modem Interrupt // Enable interrupt sources. write_omap(port + OMAP_UART_IER, 0x01); // get current MSR stat msr = read_omap(port + OMAP_UART_MSR); return (port); } int mini_serial(int state, void *data) { uint16_t count; uint8_t *dptr, c; struct mserial_data *mdata; unsigned lsr, iir; mdata = (struct mserial_data *) data; dptr = (uint8_t *) (mdata + 1); if (state == MDRIVER_INTR_ATTACH) { /* disable the serial interrupt */ write_omap(mdata->port + OMAP_UART_IER, 0x00); return (1); } else if (state == MDRIVER_INIT) { if ((mdata->port = init_hw()) == 0) return (1); /* clear the data area counters */ mdata->intr_calls = 0; mdata->ncalls = 0; mdata->errors = 0; mdata->data_len = 0; } else if (state == MDRIVER_STARTUP_PREPARE) { /* once we are out of startup use callout_io_map */ if ((mdata->port_k = callout_io_map(OMAP_UART_SIZE, 0xfffb0000)) == 0) { /* something bad so disable the driver */ write_omap(mdata->port + OMAP_UART_IER, 0x00); return (1); } } /* count the number of times the mini-driver is called */ count = mdata->ncalls + 1; mdata->ncalls = count; if (state == MDRIVER_PROCESS) { /* called because of an interrupt */ count = mdata->intr_calls + 1; mdata->intr_calls = count; iir = read_omap(mdata->port + OMAP_UART_IIR) & 0x07; if (iir == OMAP_II_RX) /* receive interrupt */ { do { dptr[mdata->data_len] = read_omap(mdata->port + OMAP_UART_RHR) & 0xff; mdata->data_len = mdata->data_len + 1; lsr = read_omap(mdata->port + OMAP_UART_LSR); /* check for errors */ if((lsr & (OMAP_LSR_RCV_FIFO | OMAP_LSR_BI|OMAP_LSR_OE| OMAP_LSR_FE|OMAP_LSR_PE)) != 0) { // Read whatever input data happens to be in the buffer to "eat" the // spurious data associated with break, parity error, etc. c = read_omap(mdata->port + OMAP_UART_RHR); c = c; } } while(lsr & OMAP_LSR_RXRDY); } } else { /* poll for data */ while (read_omap(mdata->port + OMAP_UART_LSR) & OMAP_LSR_RXRDY) { dptr[mdata->data_len] = read_omap(mdata->port + OMAP_UART_RHR) & 0xff; mdata->data_len = mdata->data_len + 1; } } if (state == MDRIVER_STARTUP_FINI) mdata->port = mdata->port_k; return (0); }
In this example, our handler function stores call information, so a structure is created to allow easier access to the data area. The data area is filled with the received characters.
During the MDRIVER_INIT state, the data area is initialized and the serial hardware is set up. This is called only once. At this time, startup_io_map() is called to map in the hardware registers, and the pointer is stored in the data area.
This pointer is used for hardware access until the handler is called with a state of STARTUP_MDRIVER_PREPARE. At this time, callout_io_map() is called, and the pointer is stored in the data area, however the startup_io_map() pointer should still be used. Once the handler is called with a state of MDRIVER_STARTUP_FINI, the handler starts using the pointer returned by callout_io_map() for all hardware access. This pointer is then used for all further invocations of the minidriver handler. When the handler is called with a state of MDRIVER_INTR_ATTACH, it disables the device interrupt and returns a value of 1 requesting an exit of the handler.
Once you have written a handler function, you need to register it with startup and allocate the required system RAM for your data area. This is accomplished with the following functions:
paddr_t alloc_ram(phys_addr, size, alignment); int mdriver_add(name, interrupt, handler, data_paddr, data_size);
Since you're allocating memory and passing an interrupt, these functions must be called after RAM is initialized by calling init_raminfo(), and after the interrupt information is added to the system page by calling init_intrinfo(). The main() function of the startup main.c should look like this:
... paddr_t mdrvr_addr; ... init_intrinfo(); init_qtime(); /* allocate 64k of ram for the minidriver's use */ mdrvr_addr = alloc_ram(~0L, 64*1024, 1); /* code to add a sample minidriver which merely does data collection */ mdriver_add("mini-data", 0, mini_data, mdrvr_addr, 64*1024); /* code to add a sample minidriver for a serial port */ /* mdriver_add("mini-serial", 46, mini_serial, mdrvr_addr, 64*1024); */ ...
In this example, you have allocated 64 KB of memory and registered the minidriver handler function with the name mini-serial>
Our minidriver is complete, and you should now rebuild startup and the boot image. In order to verify that the minidriver is running properly, you may want to add debug information to the handler function or write an application to read the minidriver data area and print the contents.
The OMAP 5912 board has a debug serial port that this minidriver example uses to receive bytes on. In order to test this driver, you should build an OS image that sets up TCP/IP and lets you telnet to the board. Since you'll be using the serial port as a data source, don't run the serial driver and don't put debug printouts or verbosity on procnto in your image. Once you have an image, you should burn it onto the onboard flash where you can boot from it.
Connect a NULL-modem cable between your OMAP board and a host machine. The minidriver is running at 14400 baud, so make sure the host machine's serial port is also configured in this manner. On the host machine, begin sending characters to the serial port. Sample code should look like this:
... uint8_t test_data[20] = {"012345678987654321"}; ... if ((fd = open("/dev/ser1", O_RDWR)) == -1) { fprintf(stderr, "Unable to open device: %s (errno=%d\n", device, errno); return (-1); } for (;;) { write(fd, test_data + index, 1); index++; if (index == 19) index = 0; }
This code sends a pattern of characters to the serial port so that the minidriver can capture data.
Now boot the OMAP 5912 board and telnet to it. Once connected, you can run the sample full driver, mdrvr-seromap, which does the following:
Since the mdrvr-seromap application is a sample serial driver, you don't need to run the devc-seromap driver.
These timings are based on a sample boot image with the following properties:
mdriver_max | Number of calls | Average calling interval |
---|---|---|
1 KB | 782 | 93 microseconds |
4 KB | 229 | 352 microseconds |
16 KB | 91 | 1.38 ms |
Boot time | First invocation time |
---|---|
170 ms | 1 ms |
Boot time identifies the time from startup to the start of the first application process. The first invocation time is the time from IPL to the first invocation of the minidriver (MDRIVER_INIT). |