Developing Lift Monitoring and Snow-Making Control for the MediaEngine

   


This section details the MediaEngine interface hardware design. After the engineers find space in the memory map for the I/O circuit, they configure the CPU registers and test the circuit by using the helloworldbit program. They then develop the MediaEngine liftmon_snowcon device driver, which enables the MediaEngine target board to monitor lifts and control snow-making equipment.

Designing the External Interface Hardware for the MediaEngine

In order for the MediaEngine to monitor lifts and control snow making, it needs to read six inputs and control four outputs. In this design, an output latch and an input buffer connect directly to the MediaEngine's CPU bus. The MediaEngine features external interface connectors that permit buffered access to various CPU signals. The bus expansion header, JP1, provides the signals listed in Table 9.1.

Table 9.1. MediaEngine JP1 Signals
Description Signals
Address lines A0 A15
Data lines D0 D15
Chip select /CS5
I/O line GPIO14
Write enable /WE
Output enable /OE
Data ready RDY
Reset /RST
Power +5V, +3V
Ground GND

Figure 9.1 shows the connections between the output latch (74HC574 Edge Triggered D Flip Flop with three-state outputs), the input buffer (74HC244 Octal Buffer with three-state outputs), and the MediaEngine. Upon reset, GPIO14 is configured as an input pin. A pull-up resistor deasserts the OutputModuleEnable line, which disables the output modules. GPIO14 is then configured as an output pin. It controls the OutputModuleEnable line, which in turn enables or disables the I/O modules. Signals A0, /OE, /WE, and /CS5 control the three to eight line decoder, 74HC138, which performs address and read/write signal decoding.

Figure 9.1. The MediaEngine liftmon_snowcon interface circuit.

graphics/09fig01.gif

Here's the write operation of 1 byte (see Figure 10-17, "Variable Latency I/O Write Timing Diagram," in SA-1110 Developer's Manual1 for more information):

  1. Deassert A0, /OE and /WE.

  2. Set D0 D7.

  3. Assert CS5, this enables the HC138.

  4. Assert /WE. The HC138's Y2 output asserts the HC574's CLK because /WE = C = L, /OE = B = H, and AO = A = L.

  5. Deassert /WE. The HC138's Y2 deasserts HC574 CLK because /WE = C = H, /OE = B = H, and AO = A = L. Data D0 D7 enters HC574's flip-flops.

  6. Deassert /CS5.

Here's the read operation of 1 byte (see Figure 10-16, "Variable Latency I/O Read Timing Diagram," in SA-1110 Developer's Manual1 for more information):

  1. Deassert A0, /OE and /WE.

  2. Assert /CS5, this enables the HC138.

  3. Assert /OE. The HC138's Y4 asserts the HC244's /OE because /WE = C = H, /OE = B = L, and AO = A = L. Data D0 D7 enters CPU.

  4. Deassert /OE. The HC138's Y4 deasserts HC244 /OE because /WE = C = H, /OE = B = H, and AO = A = L.

  5. Deassert /CS5.

Driving the HC138's A signal with A0 permits a spare set of eight inputs and eight outputs to be connected to the MediaEngine bus. GPIO14 asserts the OutputModuleEnable line, which enables the HC574's output buffers. The HC574's output can then turn the output modules on or off. The ready input signal, RDY, slows CPU read and write operations for use with slow memory devices.

TIP

The MediaEngine's ARM clock speed is 200MHz. This design's High-speed CMOS (HC) logic components are fast, but the Project Trailblazer engineers need to configure the CPU to access the interface circuit as slowly as possible because the I/O board trace capacitance could affect performance and reliability.


Finding Space in the Memory Map for the MediaEngine

The MediaEngine has 4MB Boot Flash memory, an Ethernet controller, optional Flash memory, an optional internal modem, an expansion port, and 32MB SDRAM. Table 9.2, which is a reprint of Table 3-1" from the MediaEngine Hardware Reference Manual2, shows device memory mappings. The MediaEngine bus expansion port contains Chip Select 5 (/CS5), which the ARM processor maps to address 0x48000000. Section 2.4, "Memory Map," of the SA-1110 Developer's Manual1, explains that Chip Select 5 is mapped to an 128MB memory block for static-memory or variable-latency I/O devices.

Table 9.2. MediaEngine Memory Map
Address Range Description
0x00000000 0x003FFFFF 4MB Boot Flash
0x08000300 0x0800030F CS8900A Ethernet Controller
0x10000000 0x11000000 16MB Flash (Optional)
0x18000000 0x18000000 Board Control Reg 0
0x19000000 0x19000000 Board Control Reg 1
0x40000000 0x4000001F Internal Modem (Optional)
0x48000000 0x4FFFFFFF Expansion Port
0xC0000000 0xC1FFFFFF 32MB SDRAM

The MediaEngine hardware design makes available chip select 5, /CS5, the expansion port through the JP1 connector. The liftmon_snowcon interface should use /CS5, which is mapped to address 0x48000000.

Finding the Register Base Address for the MediaEngine

The ARM SA-1110 peripheral, system, memory, and expansion on-chip registers are mapped to physical addresses between 0x80000000 and 0xBFFFFFFF (see Section 2.4, "Memory Map," of SA-1110 Developer's Manual1). Here are the register base addresses for memory and general-purpose I/O:

  • Memory interface control registers: 0xA0000000

  • General-purpose I/O control registers: 0x90040000

Configuring the Memory Controller for the MediaEngine

The static memory control register, MSC2 (see Section 10.2.4, "Static Memory Control Registers," of SA-1110 Developer's Manual1), configures control bits for Chip Select 4 and Chip Select 5. MSC2 configures the ROM type, bus width, first access delay, next access delay, and recovery time. Remember that the ARM needs to be configured for the slowest possible configuration of MSC2 for the liftmon_snowcon interface circuit. Table 9.3 shows the MSC2 bit field descriptions and the values used in this design.

Table 9.3. MSC2 Bit Field Descriptions and Design Values
Bits Name Value Description
1..0 RT5 1.0 01 ROM Type: Nonburst variable-latency I/O
2 RBW5 1 ROM bus width: 16 bits
7..3 RDF5 4.0 11111 ROM delay first access: delay set to maximum
12..8 RDN5 4.0 11111 ROM delay next access: delay set to maximum
15..13 RRR5 2.0 111 ROM recovery time: recovery time set to maximum

The liftmon_snowcon interface design requires MSC2 to be 0b1111111111111101 or 0xFFF5.

Assigning the Output Module Enable Signal for the MediaEngine

The MediaEngine bus expansion interface connector offers only GPIO14. Therefore, the Project Trailblazer engineers assigned GPIO14 to control the OutputModuleEnable signal.

Configuring the I/O Port Controller for the MediaEngine

GPIO14 is a general-purpose I/O pin and has an alternate function: It can also be the transmit pin for UART 1. The Project Trailblazer engineers need to configure the ARM I/O port controller registers shown in Table 9.4 for GPIO14 output with no alternate function.

Table 9.4. Additional ARM register settings to make GPIO14 an output pin
Register Description Bit Setting Description
GAFR GPIO Alternate Function Register AF14 = 0 No alternate function
GPDR GPIO Pin Direction Register PD14 = 1 Output

Writing the helloworldbit Testing Device Driver for the MediaEngine

Now that the Project Trailblazer engineers have defined the memory space, and determined the register base addresses, assigned the OutputModuleEnable signal, and determined the configuration of the memory and I/O controllers, it's time for them to write a helloworld program. Instead of printing "Hello world", this program should control signal lines. They decide to write a simple device driver to configure the ARM I/O and memory controllers and then control the GPIO14 and /CS5 signals.

The initialization code for the helloworldbit_mediaengine.c device driver, which is shown in Listing 9.1, configures the ARM's GPDR and GAFR registers for GPIO14 output, and then it sets and clears GPIO14. The code then configures the MSC2 register for Chip Select 5 operation, and then it reads and writes to physical memory address 0x48000000. The code uses the ioremap_nocache function, which returns virtual addresses for the configuration registers. By using a oscilloscope, the Project Trailblazer engineers then verify the proper operation of GPIO14 and /CS5, as shown in Figure 9.2. The 200MHz MediaEngine clock frequency pushes their oscilloscope's capture capability.

Figure 9.2. The oscilloscope output from rmmod helloworldbit_mediaengine, in which the top trace shows GPIO14 and the bottom trace shows /CS5.

graphics/09fig02.gif

Listing 9.1 The helloworldbit_mediaengine.c Device Driver
 /*  * helloworld_bit_mediaengine v1.0 11/03/01  * www.embeddedlinuxinterfacing.com  *  * The original location of this code is  * http://www.embeddedlinuxinterfacing.com/chapters/09/  *  * Copyright (C) 2001 by Craig Hollabaugh  *  * This program is free software; you can redistribute it and/or modify  * it under the terms of the GNU Library 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  * Library General Public License for more details.  *  * You should have received a copy of the GNU Library 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  */ /* helloworld_bit_mediaengine  * This device driver demonstrates SA-1110 I/O and memory controller  * register configuration. GPIO14 is configured as an output pin  * then toggled. CS5 is configured for non burst variable-latency I/O,  * 16 bit bus with longest read/write delays at memory address  * 0x48000000. After memory controller configuration, accesses to  * 0x48000000 result in CS5 assertion. Use this device driver and a  * fast oscilloscope to capture GPIO14 and memory signals. The  * SA-1110 in the MediaEngine runs at 200MHz, memory access timing is  * in the nanosecond range.  */ /* arm-linux-gcc -O2 -D__KERNEL__ -DMODULE -I/usr/src/arm-linux/include \ -c helloworldbit_mediaengine.c \ -o /tftpboot/arm-rootfs/tmp/helloworldbit_mediaengine.o */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> #include <asm/io.h> #define MODULE_VERSION "1.0" #define MODULE_NAME "helloworldbit_mediaengine" /* see section 9.1.1.1 Intel StrongARM SA-1110 Developer's Manual */ #define GPIO                   0x90040000 /* GPIO registers base address */ #define GPLR_OFFSET            0x00 #define GPDR_OFFSET            0x04 #define GPSR_OFFSET            0x08 #define GPCR_OFFSET            0x0C #define GAFR_OFFSET            0x1C #define GPIOLEN                0x20 #define GPIO14                 0x00004000 /* GPIO14 in registers */ #define MEMBASE                0xA0000000 /* mem controller base address */ #define MSC2_OFFSET            0x2C #define MEMLENGTH              0x30 #define EXPANSIONPORT          0x48000000 /* expansion port base address */ #define EXPANSIONPORTLEN       2 unsigned long int gpdr, gafr, msc2; static void *io_base, *gpio_base, *mem_base; /*  * init_helloworldbit  *   This function configures the GPDR, GAFR ARM registers for GPIO14 output  *   Then configures the MCS2 so /CS5 operations are slow as possible  *   Using a scope or logic probe, load and unload this module to check  *   /CS5 and CPIO14 operation  */ static int __init init_helloworldbit(void) {   unsigned char i; /* get the remapped GPIO controller base address register */   gpio_base = ioremap_nocache(GPIO, GPIOLEN);   printk("gpio_base = 0x%08X\n",gpio_base); /* we need to preserve the other bits in gpdr */   gpdr = readl(gpio_base + GPDR_OFFSET);   printk("GPDR      = 0x%08X\n",gpdr); /* we need to preserve the other bits in gafr */   gafr = readl(gpio_base + GAFR_OFFSET);   printk("GAFR      = 0x%08X\n",gafr);   writel(gpdr |  GPIO14, gpio_base + GPDR_OFFSET); /* GPIO14 as output */   writel(gafr & ~GPIO14, gpio_base + GAFR_OFFSET); /* GPIO14 no alt func */   writel(        GPIO14, gpio_base + GPCR_OFFSET); /* clear GPIO14 */   writel(        GPIO14, gpio_base + GPSR_OFFSET); /* set   GPIO14 */ /* get the remapped memory controller base address register */   mem_base = ioremap_nocache(MEMBASE, MEMLENGTH);   printk("mem_base  = 0x%08X\n",mem_base); /* we need to preserve the other bits in msc2 */   msc2 = readl(mem_base + MSC2_OFFSET);   printk("MSC2      = 0x%08X\n",msc2); /* msc2 is for cs5 and cs4, need to mask off the cs3 part */ /* 0xFFF5, non burst SRAM variable, 16 bits, long delays */   writel((msc2 & 0x0000FFFF) | 0xFFF50000, mem_base + MSC2_OFFSET); /* read back the new msc2 value and print it */   msc2 = readl(mem_base + MSC2_OFFSET);   printk("MSC2      = 0x%08X\n",msc2); /* get the remapped expansion port base address */  io_base = ioremap_nocache(EXPANSIONPORT, EXPANSIONPORTLEN);   printk("io_base   = 0x%08X\n",io_base); /* read something from and write a test pattern to the expansion port */   i = readb(io_base);   writeb(0xAA,io_base); /* everything initialized */   printk(KERN_INFO "%s %s initialized\n",MODULE_NAME, MODULE_VERSION);   return 0; } /*  * cleanup_helloworldbit  *   This function restores the GPDR, GAFR, MSC2 registers  */ static void __exit cleanup_helloworldbit(void) {   unsigned char i; /* toggle /CS5 again for testing */   i = readb(io_base);   writeb(0xAA,io_base); /* toggle GPIO14 again for testing */   writel(        GPIO14, gpio_base + GPCR_OFFSET); /* clear GPIO14 */   writel(        GPIO14, gpio_base + GPSR_OFFSET); /* set   GPIO14 */   writel(gpdr, gpio_base + GPDR_OFFSET);  /* restore gpdr */   writel(gafr, gpio_base + GAFR_OFFSET);  /* restore gafr */   writel(msc2, mem_base  + MSC2_OFFSET);  /* restore msc2 */ /* release the ioremaps */   iounmap(mem_base);   iounmap(gpio_base);   iounmap(io_base);   printk(KERN_INFO "%s %s removed\n", MODULE_NAME, MODULE_VERSION); } module_init(init_helloworldbit); module_exit(cleanup_helloworldbit); MODULE_AUTHOR("Craig Hollabaugh"); MODULE_DESCRIPTION("helloworldbit for MediaEngine"); EXPORT_NO_SYMBOLS; 

TIP

If your oscilloscope can't capture fast waveform transitions, use an inexpensive digital-logic probe, such as Radio Shack Model 22-303. Its audible tone feature reveals fast transitions to help with high-speed circuit troubleshooting.


Here's the MediaEngine output from inserting and removing the helloworldbit_mediaengine module:

 bash-2.04# insmod helloworldbit_mediaengine.o gpio_base = 0xC2802000 GPDR      = 0x00000000 GAFR      = 0x00000000 mem_base  = 0xC2804000 MSC2      = 0xFFFCFFFC MSC2      = 0xFFF5FFFC io_base   = 0xC2806000 helloworldbit_mediaengine 1.0 initialized bash-2.04# rmmod helloworldbit_mediaengine helloworldbit_mediaengine 1.0 removed 

This output shows the SA-1110 memory and I/O controller configuration registers accepted the helloworldbit configuration.

Writing the liftmon_snowcon Device Driver for the MediaEngine

All the pieces are in place for the Project Trailblazer engineers to write the MediaEngine version of liftmon_snowcon. The interface circuit design connects to the MediaEngine's bus expansion port. The SA-1110 memory and I/O controllers were properly configured and tested, using the helloworldbit_mediaengine device driver. Using the liftmon_snowcon.c source code, Listing 7.5, as a template, the engineers can replace the parallel port control code with the GPIO14 and /CS5 control code from helloworldbit_mediaengine.c. Here's a quick summary of the changes that the engineers need to make to Listing 7.5:

  1. Port reads should use the readb function instead of inb.

  2. Port writes should use the writeb function instead of outb.

  3. The OutputModuleEnable signal is handled by GPIO14.

Listing 9.2 shows the code for liftmon_snowcon_mediaengine.c.

Listing 9.2 The liftmon_snowcon_mediaengine.c Device Driver
 /*  * liftmon_snowcon_mediaengine v1.0 11/03/01  * www.embeddedlinuxinterfacing.com  *  * The original location of this code is  * http://www.embeddedlinuxinterfacing.com/chapters/09/  *  * Copyright (C) 2001 by Craig Hollabaugh  *  * This program is free software; you can redistribute it and/or modify  * it under the terms of the GNU Library 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  * Library General Public License for more details.  *  * You should have received a copy of the GNU Library 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  */ /* arm-linux-gcc -O2 -D__KERNEL__ -DMODULE -I/usr/src/arm-linux/include \ -c liftmon_snowcon_mediaengine.c \ -o /tftpboot/arm-rootfs/tmp/liftmon_snowcon_mediaengine.o */ /*  * liftmon_snowcon_mediaengine.c is based on procfs_example.c by Erik Mouw.  * For more information, please see, The Linux Kernel Procfs Guide, Erik Mouw  * http://kernelnewbies.org/documents/kdoc/procfs-guide/lkprocfsguide.html  */ /* liftmon_snowcon  * liftmon_snowcon uses read and write system calls to control  * an interface circuit connected the MediaEngine's expansion port.  * This module performs all bit operations for the data bus.  * Bash script need only read and write to /proc entry files to determine  * status or control equipment. In addition, the module's init code asserts  * the OUTPUT_ENABLE signal and its exit code deasserts the OUTPUT_ENABLE  * signal.  *  * This module creates these /proc entries:  * Trailblazer directory  /proc/trailblazer  * Lift Monitoring  *  AC Mains              /proc/trailblazer/liftacmains  *  Motor Controller      /proc/trailblazer/liftmotorcontroller  *  Low Speed Operation   /proc/trailblazer/liftslowspeed  *  High Speed Operation  /proc/trailblazer/lifthighspeed  *  Operator Switch Base  /proc/trailblazer/liftoperatorswitchbase  *  Operator Switch Top   /proc/trailblazer/liftoperatorswitchtop  * Snow-Making Control  *  Water Value 1         /proc/trailblazer/snowwatervalve1  *  Water Value 2         /proc/trailblazer/snowwatervalve2  *  Water Value 3         /proc/trailblazer/snowwatervalve3  *  Heater                /proc/trailblazer/snowheater1  */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/proc_fs.h> #include <asm/uaccess.h> #include <asm/io.h> #define MODULE_VERSION "1.0" #define MODULE_NAME "liftmon_snowcon_mediaengine" /* see section 9.1.1.1 Intel StrongARM SA-1110 Developer's Manual */ #define GPIO                   0x90040000 /* GPIO registers base address */ #define GPLR_OFFSET            0x00 #define GPDR_OFFSET            0x04 #define GPSR_OFFSET            0x08 #define GPCR_OFFSET            0x0C #define GAFR_OFFSET            0x1C #define GPIOLEN                0x20 #define GPIO14                 0x00004000 /* GPIO14 in registers */ #define MEMBASE                0xA0000000 /* mem controller base address */ #define MSC2_OFFSET            0x2C #define MEMLENGTH              0x30 #define EXPANSIONPORT          0x48000000 /* expansion port base address */ #define EXPANSIONPORTLEN       16 unsigned long int gpdr, gafr, msc2; static void *io_base, *gpio_base, *mem_base; /* input bit definitions */ #define LIFTACMAINS            0x01 #define LIFTMOTORCONTROLLER    0x02 #define LIFTSLOWSPEED          0x04 #define LIFTHIGHSPEED          0x08 #define LIFTOPERATORSWITCHBASE 0x10 #define LIFTOPERATORSWITCHTOP  0x20 /* output bit definitions */ #define SNOWWATERVALVE1        0x01 #define SNOWWATERVALVE2        0x02 #define SNOWWATERVALVE3        0x04 #define SNOWHEATER1            0x08 /* define a bitmask, each *_file uses this to determine who it is */ struct liftmon_snowcon_data_t {         unsigned char mask; }; /* snowcondata is the output latch value stored internally. Control  * changes made by user scripts writing to /proc/trailblazer entries  * result in bits being either cleared or set in snowcondata. We  * write snowcondata to the output latch every time a control change  * occurs  */ unsigned char snowcondata; /* this are the data structures that hold the mask. When a /proc  * file is read or written to, the read_proc or write_proc routine  * receives a pointer to this structure  */ struct liftmon_snowcon_data_t liftacmains_data,                               liftmotorcontroller_data,                               liftslowspeed_data,                               lifthighspeed_data,                               liftoperatorswitchbase_data,                               liftoperatorswitchtop_data,                               snowwatervalve1_data,                               snowwatervalve2_data,                               snowwatervalve3_data,                               snowheater1_data; /* These are the pointers to the /proc directory entries */ static struct proc_dir_entry  *tb_dir,                               *liftacmains_file,                               *liftmotorcontroller_file,                               *liftslowspeed_file,                               *lifthighspeed_file,                               *liftoperatorswitchbase_file,                               *liftoperatorswitchtop_file,                               *snowwatervalve1_file,                               *snowwatervalve2_file,                               *snowwatervalve3_file,                               *snowheater1_file; /* proc_read - proc_read_liftmon  * proc_read_liftmon is the callback function that the kernel calls when  * there's a read file operation on these /proc/trailblazer files:  * liftacmains, lifthighspeed, liftmotorcontroller, liftoperatorswitchbase  * liftoperatorswitchtop, and liftslowspeed. The file's data pointer is  * passed in the data parameter. You first cast it to the  * liftmon_snowcon_data_t structure. A read from io_base gets the AC input  * status module. The result is anded with the bitmask value to  * to determine if the particular input module is on or off. Which  * particular input module is defined by which /proc/trailblazer/ file  * is read.  */ static int proc_read_liftmon(char *page, char **start, off_t off, int count,                              int *eof, void *data) {   unsigned int v;   struct liftmon_snowcon_data_t *liftmon_snowcon_data =                                  (struct liftmon_snowcon_data_t *)data;   v = readb(io_base); /* mask the input value based on the mask. Each mask is different depending  * which /proc/trailblazer file was read.  * Electrical note: returning an inverted value because AC power to an input  * module pulls outputs a low and the input buffer, 74244, doesn't invert  */   if (v & liftmon_snowcon_data->mask)     page[0] = '0';   else     page[0] = '1'; /* return 1 which is the length of page */   return 1; } /* proc_write - proc_write_snowcondata  * proc_write_snowcondata is the callback function that the kernel calls  * when there's a write file operation on these /proc/trailblazer files:  * snowheater1, snowwatervalve1, snowwatervalve2 and snowwatervalve3.  * The file's data pointer is passed in the data parameter. You first  * cast it to the liftmon_snowcon_data_t structure. The buffer parameter  * points to the incoming data. If the incoming data is a 1 or a 0,  * a bit in snowcondata is set or cleared. Which bit is defined by which  * /proc/trailblazer file is written to. snowcondata is then written to  * the output latch.  */ static int proc_write_snowcondata(struct file *file, const char *buffer,                                   unsigned long count, void *data) {   struct liftmon_snowcon_data_t *liftmon_snowcon_data =                                  (struct liftmon_snowcon_data_t *)data; /* check if the user wrote a 1 or a 0 the /proc/trailblazer file.    if so, set or clear a bit in snowcondata */   if (buffer[0] == '0')     snowcondata |= liftmon_snowcon_data->mask;   if (buffer[0] == '1')     snowcondata &= ~liftmon_snowcon_data->mask;   writeb(snowcondata, io_base);   return 1; } /* proc_read - proc_read_snowcondata  * proc_read_snowcondata is the callback function that the kernel calls  * when there's a read file operation on these /proc/trailblazer files:  * snowheater1, snowwatervalve1, snowwatervalve2 and snowwatervalve3.  * The file's data pointer is passed in the data parameter. You first  * cast it to the liftmon_snowcon_data_t structure.  Use snowcondata  * anded with the bitmask value to determine if the particular output  * module is on or off. Which particular output module is defined by  * which /proc/trailblazer/ file is read.  */ static int proc_read_snowcondata(char *page, char **start, off_t off, int count,                                  int *eof, void *data) {   struct liftmon_snowcon_data_t *liftmon_snowcon_data =                                  (struct liftmon_snowcon_data_t *)data; /* mask the snowcondata value based on the mask. Each mask is different  * depending which /proc/trailblazer file was read. */   if ( snowcondata & liftmon_snowcon_data->mask )     page[0] = '0';   else     page[0] = '1'; /* return the length */   return 1; } /* init - init_liftmon_snowcon  * init_liftmon_snowcon creates the /proc entry files and obtains  * their pointers. For each file, the fields, data, read_proc,  * write_proc and owner, are filled.  Then the I/O controller is configured  * so GPIO14 is an output and the memory controller works slowly with /CS5.  * It initializes the output modules in the off state then  * completes by writing an entry to the system log using printk.  */ static int __init init_liftmon_snowcon(void) {   int rv = 0; /* Create the trailblazer /proc entry */   tb_dir = proc_mkdir("trailblazer", NULL);   if(tb_dir == NULL) {     rv = -ENOMEM;     goto out;   }   tb_dir->owner = THIS_MODULE; /* Create liftacmains and make it readable by all - 0444 */   liftacmains_file = create_proc_entry("liftacmains", 0444, tb_dir);   if(liftacmains_file == NULL) {     rv = -ENOMEM;     goto no_liftacmains;   }   liftacmains_data.mask = LIFTACMAINS;   liftacmains_file->data = &liftacmains_data;   liftacmains_file->read_proc = &proc_read_liftmon;   liftacmains_file->write_proc = NULL;   liftacmains_file->owner = THIS_MODULE; /* Create liftmotorcontroller and make it readable by all - 0444 */   liftmotorcontroller_file = create_proc_entry("liftmotorcontroller",                                                0444, tb_dir);   if(liftmotorcontroller_file == NULL) {     rv = -ENOMEM;     goto no_liftmotorcontroller;   }   liftmotorcontroller_data.mask = LIFTMOTORCONTROLLER;   liftmotorcontroller_file->data = &liftmotorcontroller_data;   liftmotorcontroller_file->read_proc = &proc_read_liftmon;   liftmotorcontroller_file->write_proc = NULL;   liftmotorcontroller_file->owner = THIS_MODULE; /* Create liftslowspeed and make it readable by all - 0444 */   liftslowspeed_file = create_proc_entry("liftslowspeed", 0444, tb_dir);   if(liftslowspeed_file == NULL) {     rv = -ENOMEM;     goto no_liftslowspeed;   }   liftslowspeed_data.mask = LIFTSLOWSPEED;   liftslowspeed_file->data = &liftslowspeed_data;   liftslowspeed_file->read_proc = &proc_read_liftmon;   liftslowspeed_file->write_proc = NULL;   liftslowspeed_file->owner = THIS_MODULE; /* Create lifthighspeed and make it readable by all - 0444 */   lifthighspeed_file = create_proc_entry("lifthighspeed", 0444, tb_dir);   if(lifthighspeed_file == NULL) {     rv = -ENOMEM;     goto no_lifthighspeed;   }   lifthighspeed_data.mask = LIFTHIGHSPEED;   lifthighspeed_file->data = &lifthighspeed_data;   lifthighspeed_file->read_proc = &proc_read_liftmon;   lifthighspeed_file->write_proc = NULL;   lifthighspeed_file->owner = THIS_MODULE; /* Create liftoperatorswitchbase and make it readable by all - 0444 */   liftoperatorswitchbase_file = create_proc_entry("liftoperatorswitchbase",                                                   0444, tb_dir);   if(liftoperatorswitchbase_file == NULL) {     rv = -ENOMEM;     goto no_liftoperatorswitchbase;   }   liftoperatorswitchbase_data.mask = LIFTOPERATORSWITCHBASE;   liftoperatorswitchbase_file->data = &liftoperatorswitchbase_data;   liftoperatorswitchbase_file->read_proc = &proc_read_liftmon;   liftoperatorswitchbase_file->write_proc = NULL;   liftoperatorswitchbase_file->owner = THIS_MODULE; /* Create liftoperatorswitchtop and make it readable by all - 0444 */   liftoperatorswitchtop_file = create_proc_entry("liftoperatorswitchtop",                                                  0444, tb_dir);   if(liftoperatorswitchtop_file == NULL) {     rv = -ENOMEM;     goto no_liftoperatorswitchtop;   }   liftoperatorswitchtop_data.mask = LIFTOPERATORSWITCHTOP;   liftoperatorswitchtop_file->data = &liftoperatorswitchtop_data;   liftoperatorswitchtop_file->read_proc = &proc_read_liftmon;   liftoperatorswitchtop_file->write_proc = NULL;   liftoperatorswitchtop_file->owner = THIS_MODULE; /* Create snowwatervalve1 and make it root writeable, readable by all-0644 */   snowwatervalve1_file = create_proc_entry("snowwatervalve1", 0644, tb_dir);   if(snowwatervalve1_file == NULL) {     rv = -ENOMEM;     goto no_snowwatervalve1;   }   snowwatervalve1_data.mask = SNOWWATERVALVE1;   snowwatervalve1_file->data = &snowwatervalve1_data;   snowwatervalve1_file->read_proc = &proc_read_snowcondata;   snowwatervalve1_file->write_proc = &proc_write_snowcondata;   snowwatervalve1_file->owner = THIS_MODULE; /* Create snowwatervalve2 and make it root writeable, readable by all-0644 */   snowwatervalve2_file = create_proc_entry("snowwatervalve2", 0644, tb_dir);   if(snowwatervalve2_file == NULL) {     rv = -ENOMEM;     goto no_snowwatervalve2;   }   snowwatervalve2_data.mask = SNOWWATERVALVE2;   snowwatervalve2_file->data = &snowwatervalve2_data;   snowwatervalve2_file->read_proc = &proc_read_snowcondata;   snowwatervalve2_file->write_proc = &proc_write_snowcondata;   snowwatervalve2_file->owner = THIS_MODULE; /* Create snowwatervalve3 and make it root writeable, readable by all-0644 */   snowwatervalve3_file = create_proc_entry("snowwatervalve3", 0644, tb_dir);   if(snowwatervalve3_file == NULL) {     rv = -ENOMEM;     goto no_snowwatervalve3;   }   snowwatervalve3_data.mask = SNOWWATERVALVE3;   snowwatervalve3_file->data = &snowwatervalve3_data;   snowwatervalve3_file->read_proc = &proc_read_snowcondata;   snowwatervalve3_file->write_proc = &proc_write_snowcondata;   snowwatervalve3_file->owner = THIS_MODULE; /* Create snowheater1 and make it root writeable, readable by all-0644 */   snowheater1_file = create_proc_entry("snowheater1", 0644, tb_dir);   if(snowheater1_file == NULL) {     rv = -ENOMEM;     goto no_snowheater1;   }   snowheater1_data.mask = SNOWHEATER1;   snowheater1_file->data = &snowheater1_data;   snowheater1_file->read_proc = &proc_read_snowcondata;   snowheater1_file->write_proc = &proc_write_snowcondata;   snowheater1_file->owner = THIS_MODULE; /* setting up the memory controller to run slowly with /CS5 */ /* get the remapped memory controller base address register */   mem_base = ioremap_nocache(MEMBASE, MEMLENGTH);   printk("mem_base  = 0x%08X\n",mem_base); /* we need to preserve the other bits in msc2 */   msc2 = readl(mem_base + MSC2_OFFSET);   printk("MSC2      = 0x%08X\n",msc2); /* msc2 is for cs5 and cs4, need to mask off the cs3 part */ /* 0xFFF5, non burst SRAM variable, 16 bits, long delays */   writel((msc2 & 0x0000FFFF) | 0xFFF50000, mem_base + MSC2_OFFSET); /* read back the new msc2 value and print it */   msc2 = readl(mem_base + MSC2_OFFSET);   printk("MSC2      = 0x%08X\n",msc2); /* get the remapped GPIO controller base address register */   io_base = ioremap_nocache(EXPANSIONPORT, EXPANSIONPORTLEN);   printk("io_base   = 0x%08X\n",io_base); /* set up the I/O controller for GPIO14 output */ /* get the remapped GPIO controller base address register */   gpio_base = ioremap_nocache(GPIO, GPIOLEN);   printk("gpio_base = 0x%08X\n",gpio_base); /* we need to preserve the other bits in gpdr */   gpdr = readl(gpio_base + GPDR_OFFSET);   printk("GPDR      = 0x%08X\n",gpdr); /* we need to preserve the other bits in gafr */   gafr = readl(gpio_base + GAFR_OFFSET);   printk("GAFR      = 0x%08X\n",gafr); /* initialize snowcondata to 0xFF, all output modules off */ /* output modules invert */   snowcondata = 0xFF;   writeb(snowcondata, io_base); /* turn off all output modules */   writel(GPIO14            , gpio_base + GPSR_OFFSET); /* set GPIO14 */   writel(gpdr | GPIO_GPIO14, gpio_base + GPDR_OFFSET); /* set GPIO14 output*/   writel(GPIO14            , gpio_base + GPCR_OFFSET); /* clear GPIO14 */     /* clearing GPIO14 turns on output buffer controlling output modules */ /* everything initialized */   printk(KERN_INFO "%s %s initialized\n",MODULE_NAME, MODULE_VERSION);   return 0; /* this removes /proc entries if we have an error along the way */ no_snowheater1:   remove_proc_entry("snowheater1", tb_dir); no_snowwatervalve3:   remove_proc_entry("snowwatervalve3", tb_dir); no_snowwatervalve2:   remove_proc_entry("snowwatervalve2", tb_dir); no_snowwatervalve1:   remove_proc_entry("snowwatervalve1", tb_dir); no_liftoperatorswitchtop:   remove_proc_entry("liftoperatorswitchtop", tb_dir); no_liftoperatorswitchbase:   remove_proc_entry("liftoperatorswitchbase", tb_dir); no_lifthighspeed:   remove_proc_entry("lifthighspeed", tb_dir); no_liftslowspeed:   remove_proc_entry("liftslowspeed", tb_dir); no_liftmotorcontroller:   remove_proc_entry("liftmotorcontroller", tb_dir); no_liftacmains:   remove_proc_entry("liftacmains", tb_dir); out:   return rv; } /* exit - cleanup_liftmon_snowcon  * cleanup_liftmon_snowcon turns off the output modules and  * deasserts the OUTPUT_ENABLE signal. It removes the /proc entry files  * prints a message to the system log.  */ static void __exit cleanup_liftmon_snowcon(void) {   writel(gpdr, gpio_base + GPDR_OFFSET); /* restore gpdr */   writel(gafr, gpio_base + GAFR_OFFSET); /* restore gafr */   writel(msc2, mem_base  + MSC2_OFFSET); /* restore msc2 */ /* release the ioremaps */   iounmap(mem_base);   iounmap(gpio_base);   iounmap(io_base); /* removing the /proc entries */   remove_proc_entry("liftacmains", tb_dir);   remove_proc_entry("liftmotorcontroller", tb_dir);   remove_proc_entry("liftslowspeed", tb_dir);   remove_proc_entry("lifthighspeed", tb_dir);   remove_proc_entry("liftoperatorswitchbase", tb_dir);   remove_proc_entry("liftoperatorswitchtop", tb_dir);   remove_proc_entry("snowwatervalve1", tb_dir);   remove_proc_entry("snowwatervalve2", tb_dir);   remove_proc_entry("snowwatervalve3", tb_dir);   remove_proc_entry("snowheater1", tb_dir);   remove_proc_entry("trailblazer", NULL);   printk(KERN_INFO "%s %s removed\n", MODULE_NAME, MODULE_VERSION); } module_init(init_liftmon_snowcon); module_exit(cleanup_liftmon_snowcon); MODULE_AUTHOR("Craig Hollabaugh"); MODULE_DESCRIPTION( "Trailblazer Lift Monitor and Snowmaking Control for MediaEngine"); EXPORT_NO_SYMBOLS; 

The engineers have now attached memory-mapped I/O interface hardware to the SA-1110 processor and written a device driver to access it. That's quite a feat.

The MediaEngine design, with its bus expansion port, only allows the use of GPIO14 and /CS5 signals. Although this seems restrictive for other interface projects, the combination of GPIO14, /CS5, and the data lines is a perfect match for the lift monitoring and snow-making control circuit. You'll see in the next section how the RPX-CLLF, using a PowerPC, offers more connectivity options, coupled with additional complexity.


       
    Top


    Embedded LinuxR. Hardware, Software, and Interfacing
    Embedded LinuxR. Hardware, Software, and Interfacing
    ISBN: N/A
    EAN: N/A
    Year: 2001
    Pages: 103

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net