Counting cycles on the SheevaPlug

The SheevaPlug has a 1200MHz Marvell Kirkwood 88F6281 (ARM926EJ-S) CPU. It runs Ubuntu 9.04.

Create a cpucycles4ns directory. Inside that directory, create Makefile with the following contents:

     KERNELDIR := /usr/src/linux-2.6.22.18
     
     obj-m := cpucycles4ns.o
     
     # default build target (uses kernel build (kbuild) system)
     all:
             $(MAKE) -C $(KERNELDIR) M=`pwd` modules
On the last line, make sure to use a tab rather than eight spaces. Create cpucycles4ns.c with the following contents:
     #include <linux/module.h>
     #include <linux/kernel.h>
     #include <linux/time.h>
     #include <linux/fs.h>
     #include <asm/uaccess.h>
     MODULE_LICENSE("Dual BSD/GPL");
     
     #define KW_REGS_VIRT_BASE              0xfed00000
     #define KW_REGISTER(x)                 (KW_REGS_VIRT_BASE | x)
     
     #define WORD_SWAP(X) (X)
     
     #define KW_REG_WRITE(addr, data)       \
         ((*((volatile unsigned int*)(KW_REGISTER(addr)))) \
                     = ((unsigned int)WORD_SWAP((data))))
     
     #define KW_REG_READ(addr)              \
         WORD_SWAP(((*((volatile unsigned int*)(KW_REGISTER(addr))))))
     
     static int device_open(struct inode *inode,struct file *file)
     {
       try_module_get(THIS_MODULE);
       return 0;
     }
     
     static int device_release(struct inode *inode,struct file *file)
     {
       module_put(THIS_MODULE);
       return 0;
     }
     
     static ssize_t device_read(struct file *filp,char *buffer,size_t length,loff_t *offset)
     {
       unsigned int result[3];
       struct timespec t;
       unsigned int c;
       if (length < sizeof result) return -EIO;
       c = KW_REG_READ(0x20314);
       getnstimeofday(&t);
       result[0] = -6 * c;
       result[1] = t.tv_nsec;
       result[2] = t.tv_sec;
       copy_to_user(buffer,result,sizeof result);
       return sizeof result;
     }
     
     #define DEVICE_NAME "cpucycles4ns"
     
     static struct file_operations fops = {
       .read = device_read,
       .open = device_open,
       .release = device_release
     } ;
     
     static int major;
     
     static int cpucycles4ns_init(void)
     {
       unsigned int cycles1;
       unsigned int cycles2;
       printk(KERN_INFO DEVICE_NAME " starting\n");
       printk(KERN_INFO DEVICE_NAME " creating device\n");
       major = register_chrdev(0,DEVICE_NAME,&fops);
       if (major < 0) {
         printk(KERN_INFO DEVICE_NAME " failed with %d\n",major);
         return major;
       }
       printk(KERN_INFO DEVICE_NAME " suggests mknod /dev/" DEVICE_NAME " c %d 0\n",major);
       printk(KERN_INFO DEVICE_NAME " enabling cycle counter\n");
     
       // KW_REG_WRITE(0x20310,0xffffffff);
       // KW_REG_WRITE(0x20314,0xffffffff);
       // KW_REG_WRITE(0x20300,KW_REG_READ(0x20300) | 3);
       cycles1 = KW_REG_READ(0x20314);
       cycles2 = KW_REG_READ(0x20314);
     
       printk(KERN_INFO DEVICE_NAME " %u %u\n",cycles1,cycles2);
       return 0;
     }
     
     static void cpucycles4ns_exit(void)
     {
       unsigned int cycles1;
       unsigned int cycles2;
     
       printk(KERN_INFO DEVICE_NAME " removing device\n");
       unregister_chrdev(major,DEVICE_NAME);
       printk(KERN_INFO DEVICE_NAME " disabling cycle counter\n");
     
       // KW_REG_WRITE(0x20300,KW_REG_READ(0x20300) & ~3);
       cycles1 = KW_REG_READ(0x20314);
       cycles2 = KW_REG_READ(0x20314);
     
       printk(KERN_INFO DEVICE_NAME " %u %u\n",cycles1,cycles2);
       printk(KERN_INFO DEVICE_NAME " stopping\n");
     }
     
     module_init(cpucycles4ns_init);
     module_exit(cpucycles4ns_exit);
Type
     make
to create cpucycles4ns.ko. As root, type
     insmod cpucycles4ns.ko
     dmesg | grep cpucycles4ns
so that the kernel runs cpucycles4ns.ko. This should print cpucycles4ns starting and a suggested command such as mknod /dev/cpucycles4ns c 253 0. Run that command:
     mknod /dev/cpucycles4ns c 253 0

Now user programs can read the Feroceon cycle counter for high-resolution timings. In particular, SUPERCOP (through its dev4ns module) will automatically use the Feroceon cycle counter.

Rebooting will turn off access to the cycle counter. That probably isn't what you want, so edit /etc/rc.local to do the same insmod, but with a full path to cpucycles4ns.ko.