Developing Lift Monitoring and Snow-Making Control for the RPX-CLLF

   


This section details the phases of the hardware design process for the RPX-CLLF target board. These steps are the same for the MediaEngine and the RPX-CLLF, but architectural differences between these processors warrant detailed coverage for each board.

Designing the External Interface Hardware for the RPX-CLLF

This section details the RPX-CLLF 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. The engineers then develop the RPX-CLLF liftmon_snowcon device driver, which enables the RPX-CLLF target board to monitor lifts and control snow-making equipment.

The RPX-CLLF design offers extensive bus and I/O through two expansion connectors. Table 9.5 shows the CPU signals available for the lift monitoring and snow-making control circuit.

Table 9.5. RPX-CLLF Bus and I/O Expansion Connector Signals
Description Signals
Address lines A0 A31
Data lines D0 D31
Chip select /CS0 /CS7
Write enable /WE1
Output enable /OE (GPL1)
Port A PA0 PA23
Port B PB0 PB31
Port C PC0 PC15
Port D PD3 PD15
Reset /RST
Power +5V, +3V
Ground GND

Figure 9.3 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 RPX-CLLF. PA0 controls the OutputModuleEnable line. The signals A0, /OE, /WE1, and /CS5 control the three to eight line decoder that performs address and read/write signal decoding. Byte read and byte write operations are similar to those for the MediaEngine (see the section "Designing the External Interface Hardware for the MediaEngine," earlier in this chapter). This design's HC logic components are fast, but the Project Trailblazer engineers want to configure the CPU to access this interface circuit as slowly as possible because the I/O board trace capacitance could affect performance and reliability.

Figure 9.3. The RPX-CLLF liftmon_snowcon interface circuit.

graphics/09fig03.gif

Finding Space in the Memory Map for the RPX-CLLF

The RPX-CLLF has 16MB of DRAM and can accommodate 2MB, 4MB, 8MB, or 16MB Flash memory. Table 9.6, which is a reprint of Table 4-2 from Embedded Planet PlanetCore Version 1.4 Boot Loader MPC8xx Manual3, shows device memory mappings.

Table 9.6. RPX-CLLF Memory Map
Chip Enable Start Length Description
N/A 0xFA200000 10000 Internal memory of the CPU
0 0xFC000000 4000000 Flash memory area
1 00000000 Varies RAM area
2 Varies Varies RAM area for second bank
3 0xFA400000 Small BCSR area
4 0xFA000000 Varies NVRAM area

The PlanetCore boot loader itself can also verify memory-map information. Here's its output from the map command:

 PlanetCore Boot Loader v1.02 Copyright 2000 Embedded Planet.  All rights reserved. DRAM available size = 16 MB wvCV DRAM OK Autoboot in 2 seconds. ESC to abort, SPACE or ENTER to go. >map IMMR = 0xFA200000     Address  Mask     Size WP Mach CS0 FC000000 FC000000 Word N  GPCM CS1 00000000 FF000000 Word N  UPMA CS2 (not used) CS3 FA400000 FF7F8000 Word N  GPCM CS4 FA000000 FFFE0000 Byte N  GPCM CS5 (not used) CS6 (not used) CS7 (not used) > 

This output shows that the RPX-CLLF has 16MB of memory, mapped to address 0x00000000 (CS1). This 16MB extends to address 0x01000000, and there's nothing mapped between 0x01000000 and 0xFA000000. Note that CS5 is unused. Traditionally, in embedded 8xx designs, RAM is mapped at address 0x00 and I/O devices are mapped above physical address 0x800000004. That's where the engineers decided to map the lift monitoring and snow-making control interface circuit.

Finding the Register Base Address for the RPX-CLLF

To accommodate a wide variety of designs, the MPC860 CPU uses an internal memory-map register (IMMR) whose memory location can be modified via software control. The PlanetCore Boot Loader output in the preceding section reports that the address of the IMMR is 0xFA200000. Current or future Linux versions may or may not move the IMMR during the boot process. Device driver code written for the PowerPC 8xx processor family should not hard-code the IMMR location as 0xFA200000. Rather, it should use the mfspr function, which finds the IMMR. Here's a line of kernel code from arch/ppc/8xx_io/enet.c using mfspr:

 immap = (immap_t *)(mfspr(IMMR) & 0xFFFF0000); 

By using IMMR combined with table offsets, the Project Trailblazer engineers can configure all the MPC860 registers.

Configuring the Memory Controller for the RPX-CLLF

The MPC860 features a sophisticated memory controller that is capable of interfacing to SRAM, EPROM, Flash EPROM, regular DRAM devices, self-refresh DRAM devices, extended-data-output DRAM devices, synchronous DRAM devices, and other peripherals. It performs this interfacing through either its general-purpose chip-select machine (GPCM) or a user-programmable machine (UPM). The interface circuit in Figure 9.3 interfaces with the GPCM because it supports nonburstable memory-mapped peripherals. (See Chapter 16, "Memory Controller," in MPC860 User Manual5 for more information.)

NOTE

The PowerPC bit definitions are reversed from those of Intel. With the PowerPC, in a 32-bit word, bit 0 is the most significant bit.


The Project Trailblazer engineers are using Chip Select 5, so they need to configure the Base Register (BR5) and Option Register (OR5) for that chip select. Table 9.7 describes BR5's fields.

Table 9.7. BR5 Bit Field Descriptions and Design Values
Bits Name Value Description
0 16 BA 0x80 Base address: 0x80000000
17 19 AT 000 Address type: no access limits
20 21 PS 01 Port size: 01 is 8-bit port size
22 PARE 0 Parity enable: 0 is disabled
23 WP 0 Write protect: 0 is disabled, read and writes allowed
24 25 MS 00 Machine select: 00 is GPCM
26 30  00000 Reserved
31 V 1 Valid: 1 means BR5 and OR5 contents are valid

The liftmon_snowcon interface requires BR5 to be 0b1000.0000.0000.0000.0000.0100.0000.0001 which equals 0x80000401.

Table 9.8 describes the OR5's fields.

Table 9.8. OR5 Bit Field Descriptions and Design Values
Bits Name Value Description
0 16 AM 0x80 Address mask: 0x80000000
17 19 ATM 000 Address type mask: 000 means no protection desired
20 CSNT 1 Chip select negation time: 1 provides extended hold times
21 22 ACS 11 Address to chip-select setup: 111 provides slowest signals
23 BIH 1 Burst inhibit: 1 means bank doesn't support bursting
24 27 SCY 1111 Select cycle length: 1111 provides slowest signals
28 SETA 0 Select external transfer acknowledgement: 0 means memory doesn't provide acknowledgement
29 TRLX 1 Timing relaxed: 1 means timing provides slowest signals
30 EHTR 1 Extended hold time read: 1 means slowest signals
31  0 Reserved

The liftmon_snowcon interface requires OR5 to be 0b1000.0000.0000.0000.0000.1111.1111.0110, which equals 0x80000FF6.

Assigning the Output Module Enable Signal for the RPX-CLLF

The RPX-CLLF bus expansion interface connector offers connections to four CPU I/O ports, A through D. Each port is capable of driving the OutputModuleEnable signal. Some port pins support an alternate function of an internal peripheral. The Project Trailblazer engineers identified that Port A Pin 0's alternate function wasn't being used. They selected PA0 to drive the OutputModuleEnable signal.

Configuring the I/O Port Controller for the RPX-CLLF

The Project Trailblazer engineers need to configure the RPX-CLLF I/O port controller registers shown in Table 9.9 for PA0 output with no alternate function. (See Section 34.2, "Parallel I/O Ports," of MPC860 User Manual5 for more general information.)

Table 9.9. Additional PowerPC Register Settings to Make PA0 an Output Pin
Register Description Bit Setting Description
PAPAR Port A Pin Assignment Register PAPARDD0 = 0 General I/O
PADIR Port A Data Direction Register PADIRDR0 = 1 Selects output

Writing the helloworldbit Testing Device Driver for the RPX-CLLF

Now that the Project Trailblazer engineers have defined the memory space, determined the register base addresses, assigned the OutputModuleEnable signal, and know the configuration of the memory and I/O controllers, they need to write a helloworld program. They write a simple device driver to configure the MPC860 memory and I/O controllers and then control the /CS5 and PA0 signals.

The initialization code for the helloworldbit_rpxcllf.c device driver, which is shown in Listing 9.3, configures the MPC860's PAPAR and PADIR for PA0 output, and then it sets and clears PA0. The code then configures Chip Select 5's BR5 and OR5 registers, and then it reads and writes to physical memory address 0x80000000. By using an oscilloscope, the Project Trailblazer engineers then verify the proper operation of PA0 and /CS5, as shown in Figure 9.4.

Figure 9.4. The scope output from rmmod helloworldbit_rpxcllf, in which the top trace shows PA0 and the bottom trace shows /CS5.

graphics/09fig04.gif

Listing 9.3 The helloworldbit_rpxcllf.c Device Driver
 /*  * helloworld_bit_rpxcllf 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_rpxcllf  * This device driver demonstrates MPC860 I/O and memory controller  * register configuration. PA0 is configured as an output pin  * then toggled. CS5 is configured for non burst variable-latency I/O,  * 8 bus with longest read/write delays at memory address  * 0x80000000. After memory controller configuration, accesses to  * 0x8000000 result in CS5 assertion. Use this device driver and a  * fast oscilloscope to capture PA0 and memory signals.  */ /* powerpc-linux-gcc -O2 -D__KERNEL__ -DMODULE -I/usr/src/powerpc-linux/include \ -c helloworldbit_rpxcllf.c \ -o /tftpboot/powerpc-rootfs/tmp/helloworldbit_rpxcllf.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> #include <asm/8xx_immap.h> #define MODULE_VERSION "1.0" #define MODULE_NAME "helloworldbit_rpxcllf" volatile immap_t *immap; static void *io_base; /* references  * see section 34.2 Port A MPC860 PowerQUICC User's Manual  *    section 16.5 General-Purpose Chip-Select Machine (GPCM)  */ #define EXPANSIONPORT          0x80000000 #define EXPANSIONPORTLEN       16 #define PA0                    0x8000 /* PA0 bit in registers */ /*  * init_helloworldbit  * This function configures the PAPAR and PADIR MPC860 registers for PA0  * output.  Then configures the BR5 and OR5 so /CS5 operations are slow  * as possible Using a scope or logic probe, load and unload this module  * to check /CS5 and PA0 operation  */ static int __init init_helloworldbit(void) {   unsigned char i; /* get immap value */   immap = (immap_t *)(mfspr(IMMR) & 0xFFFF0000); /* sets up CS5 for memory at 0x80000000, MPC man Table 16.4.1 BRx Register */   immap->im_memctl.memc_br5 = 0x80000401; /* sets up CS5 for memory at 0x80000000, MPC man Table 16.4.2 ORx Register */   immap->im_memctl.memc_or5 = 0x80000FF6; /* CS5 slowest */   io_base = ioremap_nocache(EXPANSIONPORT, EXPANSIONPORTLEN);   i = readb(io_base);    /* toggle CS5 and RD */   writeb(0xAA, io_base); /* toggle CS5 and WR */   immap->im_ioport.iop_papar &= ~PA0; /* set PA0 to general I/O */   immap->im_ioport.iop_padir |=  PA0; /* set PA0 as output */   immap->im_ioport.iop_padat &= ~PA0; /* clear PA0, toggle for bit test */   immap->im_ioport.iop_padat |=  PA0; /* set   PA0, toggle for bit test */ /* print out register values */   printk("immr    = 0x%08X\n",immap);   printk("io_base = 0x%08X\n", io_base);   printk("BR5     = 0x%08X\n",immap->im_memctl.memc_br5);   printk("OR5     = 0x%08X\n",immap->im_memctl.memc_or5);   printk("PAPAR   = 0x%04X\n",immap->im_ioport.iop_papar);   printk("PADIR   = 0x%04X\n",immap->im_ioport.iop_padir); /* everything initialized */   printk(KERN_INFO "%s %s initialized\n",MODULE_NAME, MODULE_VERSION);   return 0; } /*  * cleanup_helloworldbit  *   This function makes PA0 an input and releases io_base;  */ static void __exit cleanup_helloworldbit(void) {   unsigned char i;   /* toggle /CS5 again for testing */   i = readb(io_base);    /* toggle CS5 and RD */   writeb(0xAA, io_base); /* toggle CS5 and WR */   immap->im_ioport.iop_padat &= ~PA0; /* clear PA0, toggle for bit test */   immap->im_ioport.iop_padat |=  PA0; /* set   PA0, toggle for bit test */   immap->im_ioport.iop_padir &= ~PA0; /* set PA0 as input */ /* release the ioremap */   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 RPX-CLLF"); EXPORT_NO_SYMBOLS; 

TIP

There's a slight difference between the register configuration of helloworldbit_rpxcllf.c and that of helloworldbit_mediaengine.c. The RPX-CLLF version uses a register structure whereas the MediaEngine version uses offsets from a register base address. The PowerPC programmers developed the immap structure to make coding easier and less error prone (see /usr/src/powerpc-linux/include/asm-ppc/8xx_immap.h). helloworldbit_rpxcllf.c uses the immap register structure instead of defining offsets from the base register address, as in helloworldbit_mediaengine.c.


Here's the output from inserting and removing helloworldbit_rpxcllf.o:

 bash-2.04# insmod helloworldbit_rpxcllf.o io_base = 0xC2002000 immr    = 0xFA200000 BR5     = 0x80000401 OR5     = 0x80000FF6 PAPAR   = 0x0AFF PADIR   = 0x8000 helloworldbit_rpxcllf 1.0 initialized bash-2.04# rmmod helloworldbit_rpxcllf helloworldbit_rpxcllf 1.0 removed 

This output shows that the MPC860 memory and I/O controller configuration registers accepted the helloworldbit configuration.

Writing the liftmon_snowcon Device Driver for the RPX-CLLF

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

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

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

  3. The OutputModuleEnable signal is handled by PA0.

Listing 9.4 shows the code for liftmon_snowcon rpx-cllf.c.

Listing 9.4 The liftmon_snowcon_rpx-cllf.c Device Driver
 /*  * liftmon_snowcon_rpx-cllf 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  */ /* powerpc-linux-gcc -O2 -D__KERNEL__ -DMODULE -I/usr/src/powerpc-linux/include \ -c liftmon_snowcon_rpxcllf.c \ -o /tftpboot/powerpc-rootfs/tmp/liftmon_snowcon_rpxcllf.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 RPX-CLLF's bus and expansion ports.  * 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  */ /* references  * see section 34.2 Port A MPC860 PowerQUICC User's Manual  *     section 16.5 General-Purpose Chip-Select Machine (GPCM)  */ #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> #include <asm/8xx_immap.h> #define MODULE_VERSION "1.0" #define MODULE_NAME "liftmon_snowcon_cllf" volatile immap_t *immap; static void *io_base; #define EXPANSIONPORT          0x80000000 #define EXPANSIONPORTLEN       16 #define PA0                    0x8000 /* 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; 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; 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 + 1);   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 PA0 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; /* get the register base address */   immap = (immap_t *)(mfspr(IMMR) & 0xFFFF0000); /* set CS5 for memory at 0x80000000, see 16.4.1 Base Registers (BRx)  * and 16.4.2 Option Registers (ORx) for slow operation  */   immap->im_memctl.memc_br5 = 0x80000401;   immap->im_memctl.memc_or5 = 0x80000FF6; /* get the remapped GPIO controller base address register */   io_base = ioremap_nocache(EXPANSIONPORT, EXPANSIONPORTLEN); /* initialize snowcondata to 0xFF, all output modules off */ /* output modules invert */   snowcondata = 0xFF;   writeb(snowcondata, io_base);   immap->im_ioport.iop_papar &= ~PA0; /* setting PA0 to general I/O */   immap->im_ioport.iop_padir |=  PA0; /* make PA0 an output         */   immap->im_ioport.iop_padat &= ~PA0; /* enable the 74HC574 output  */   printk("immr    = 0x%08X\n",immap);   printk("io_base = 0x%08X\n", io_base);   printk("BR5     = 0x%08X\n",immap->im_memctl.memc_br5);   printk("OR5     = 0x%08X\n",immap->im_memctl.memc_or5);   printk("PAPAR   = 0x%04X\n",immap->im_ioport.iop_papar);   printk("PADIR   = 0x%04X\n",immap->im_ioport.iop_padir); /* 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 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) {   immap->im_ioport.iop_padir &= ~PA0; /* make PA0 an input */ /* release the ioremap */   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 RPX-CLLF"); EXPORT_NO_SYMBOLS; 

The engineers have now attached memory-mapped I/O interface hardware to the MPC860 processor and written a device driver to access it.


       
    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