aboutsummaryrefslogtreecommitdiffstats
diff options
authorGreg Kroah-Hartman <gregkh@suse.de>2007-11-09 20:25:30 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2007-11-09 20:25:30 -0800
commitb4aae55f4adabb204f2d172edd8e484a4f53bc53 (patch)
treee9c38b37fc63dc1e92daad0a24488f33c1746dbf
parentc4812d8a8cb6b5a1aa44e0433304c644323c0983 (diff)
downloadpatches-b4aae55f4adabb204f2d172edd8e484a4f53bc53.tar.gz
added chumby drivers (need different arch to build).
-rw-r--r--chumby_drivers.patch2060
-rw-r--r--series1
2 files changed, 2061 insertions, 0 deletions
diff --git a/chumby_drivers.patch b/chumby_drivers.patch
new file mode 100644
index 00000000000000..c5272a3b3d8843
--- /dev/null
+++ b/chumby_drivers.patch
@@ -0,0 +1,2060 @@
+From: <bunnie@chumby.com>
+Subject: chumby char drivers
+
+Need some cleanups before they can go into mainline (coding style, proc
+abuse, etc.) but it's good to get them out there to start with...
+
+---
+ drivers/char/Makefile | 9
+ drivers/char/chumby_accel.c | 994 +++++++++++++++++++++++++++++++++++++++++++
+ drivers/char/chumby_accel.h | 134 +++++
+ drivers/char/chumby_sense1.c | 461 +++++++++++++++++++
+ drivers/char/chumby_sense1.h | 97 ++++
+ drivers/char/chumby_timer.c | 273 +++++++++++
+ drivers/char/chumby_timer.h | 49 ++
+ 7 files changed, 2017 insertions(+)
+
+--- a/drivers/char/Makefile
++++ b/drivers/char/Makefile
+@@ -9,6 +9,15 @@ FONTMAPFILE = cp437.uni
+
+ obj-y += mem.o random.o tty_io.o n_tty.o tty_ioctl.o
+
++obj-m := chumby_sense1.o
++chumby-sense1-objs := chumby_sense1.o
++
++obj-m += chumby_accel.o
++chumby-accel-objs := chumby_accel.o
++
++obj-m += chumby_timer.o
++chumby-timer-objs := chumby_timer.o
++
+ obj-$(CONFIG_LEGACY_PTYS) += pty.o
+ obj-$(CONFIG_UNIX98_PTYS) += pty.o
+ obj-y += misc.o
+--- /dev/null
++++ b/drivers/char/chumby_accel.c
+@@ -0,0 +1,994 @@
++/*
++ chumby_accel.c
++ bunnie -- March 2007 -- 2.0 -- port to Ironforge
++ bunnie -- April 2007 -- 2.1 -- fixed grange to 2500
++
++ This file is part of the chumby accelerometer driver in the linux kernel.
++ Copyright (c) Chumby Industries, 2007
++
++ The accelerometer driver is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ The accelerometer driver 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 General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with the Chumby; if not, write to the Free Software
++ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++*/
++
++#define ACCEL_VERSION "2.1-Kionix-Ironforge"
++#define DCID_VERSION "1.0-Atmel-25080A-Ironforge"
++
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++
++#include <linux/kernel.h> /* printk() */
++#include <linux/slab.h> /* kmalloc() */
++#include <linux/fs.h> /* everything... */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/proc_fs.h>
++#include <linux/fcntl.h> /* O_ACCMODE */
++#include <linux/seq_file.h>
++#include <linux/cdev.h>
++
++#include <asm/io.h>
++#include <asm/system.h> /* cli(), *_flags */
++#include <asm/uaccess.h> /* copy_*_user */
++
++#include <linux/miscdevice.h>
++#include <linux/ioport.h>
++#include <linux/poll.h>
++#include <linux/sysctl.h>
++#include <linux/spinlock.h>
++#include <linux/delay.h>
++#include <linux/rtc.h>
++
++#include <linux/timer.h>
++
++#include <asm/arch/imx-regs.h>
++
++#include "chumby_accel.h"
++
++/*
++ * basic parameters
++ */
++
++int accel_major = 0; // dynamic allocation
++int accel_minor = 0;
++int accel_nr_devs = 1;
++
++int dcid_major = 0; // dynamic allocation
++int dcid_minor = 0;
++int dcid_nr_devs = 1;
++
++module_param(accel_major, int, S_IRUGO);
++module_param(accel_minor, int, S_IRUGO);
++module_param(accel_nr_devs, int, S_IRUGO);
++
++module_param(dcid_major, int, S_IRUGO);
++module_param(dcid_minor, int, S_IRUGO);
++module_param(dcid_nr_devs, int, S_IRUGO);
++
++MODULE_AUTHOR("bunnie@chumby.com");
++MODULE_LICENSE("GPL");
++
++#define ACCEL_DEFAULT_THRESH 0xC0 // absolute value, +/- off of avg to trigger accel
++
++// static data
++static unsigned long accel_status = 0; /* bitmapped status byte. */
++static unsigned long dcid_status = 0; /* bitmapped status byte. */
++static int gDone = 0;
++static unsigned int gOuchval = 375;
++static unsigned int gBreakval = 825;
++static unsigned int gAccelThresh = ACCEL_DEFAULT_THRESH;
++
++static unsigned int impactHint = 0;
++static unsigned int impactTime = 0;
++static unsigned int impactVector[3] = {0,0,0};
++
++static unsigned char gDCvers[VERS_LEN] = "unsupported\n";
++static unsigned char gDCserial[VERS_LEN] = "unsupported\n";
++//static unsigned char gMBvers[VERS_LEN] = "unsupported\n"; // get these from CP directly...
++//static unsigned char gMBserial[VERS_LEN] = "unsupported\n";
++
++static int recDelay = 0;
++
++static int gRomBusy = 0;
++
++#define IMPCT_HINT_OUCH 1
++#define IMPCT_HINT_BREAK 2
++
++/*
++ * Accel sensor data logs, tracked by tasks that are scheduled by the
++ * task scheduler
++ */
++
++#define ACCEL_INTERVAL 1 // jiffies elapsed between recordings
++
++#define FIXEDPOINT_NORM 1000
++
++#define MAX_ACCEL_DATA 16
++
++#define NOT_MOVED 0
++#define MOVED 1
++
++#define CLK_DIVIDER ((PCDR1 & PCDR1_PERDIV2_MASK) >> PCDR1_PERDIV2_POS)
++#define BASE_FREQ 350
++#define TARGET_FREQ 5
++
++#define EASY_DEBUGGING 0 // causes read calls to spit out ASCII text instead of binary records
++
++struct acceldata {
++ int index; // always points at the next FREE location
++ unsigned short *dataLogX; // initialize to buffer of proper length
++ unsigned short *dataLogY; // initialize to buffer of proper length
++ unsigned short *dataLogZ; // initialize to buffer of proper length
++ unsigned short runningAvgX;
++ unsigned short runningAvgY;
++ unsigned short runningAvgZ;
++ unsigned int logTotalX; // shortcut for maintaining runningAvg
++ unsigned int logTotalY; // shortcut for maintaining runningAvg
++ unsigned int logTotalZ; // shortcut for maintaining runningAvg
++ unsigned short threshold;
++ unsigned char moved;
++ struct timer_list timer;
++ struct cdev *accel_cdev;
++} acceltask_data;
++
++struct dciddata {
++ struct cdev *dcid_cdev;
++} dcidtask_data;
++
++#define USE_SYSCTL 1
++
++#define SPI_CHAN 0
++
++#define ACCEL_BLOCKING 0 // turn off blocking read on accel sensor
++
++#if USE_SYSCTL
++# define CHUMACCEL_DEBUG( flag, fmt, args... ) do { if ( gDebug ## flag ) printk( "%s: " fmt, __FUNCTION__ , ## args ); } while (0)
++#else
++# define CHUMACCEL_DEBUG( flag, fmt, args... ) printk( "%s: " fmt, __FUNCTION__ , ## args )
++#endif
++
++#if USE_SYSCTL
++# define CHUMDCID_DEBUG( flag, fmt, args... ) do { if ( gDebug ## flag ) printk( "%s: " fmt, __FUNCTION__ , ## args ); } while (0)
++#else
++# define CHUMDCID_DEBUG( flag, fmt, args... ) printk( "%s: " fmt, __FUNCTION__ , ## args )
++#endif
++
++// function protos
++static int chumby_dcid_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg);
++static int chumby_accel_open(struct inode *inode, struct file *file);
++static int chumby_dcid_open(struct inode *inode, struct file *file);
++static int chumby_accel_release(struct inode *inode, struct file *file);
++static int chumby_dcid_release(struct inode *inode, struct file *file);
++static int accel_read_proc(char *page, char **start, off_t off,
++ int count, int *eof, void *data);
++static int dcid_read_proc(char *page, char **start, off_t off,
++ int count, int *eof, void *data);
++static ssize_t chumby_accel_read(struct file *file, char *buf,
++ size_t count, loff_t *ppos);
++//static ssize_t chumby_accel_write(struct file *file, const unsigned char *buf,
++// size_t count, loff_t *ppos );
++//static unsigned int chumby_accel_poll(struct file * filp, struct poll_table_struct * wait);
++static unsigned char readEEPROM(unsigned int addr);
++
++#if USE_SYSCTL
++
++// proc debug structure
++static int gDebugTrace = 0;
++static int gDebugIoctl = 0;
++static int gDebugError = 1;
++static int gEmpty = -1;
++
++// create /proc/sys/debug-trace, etc...which can be written to set behavior of globals
++// in this driver on the fly
++static struct ctl_table_header *gSysCtlHeader;
++static struct ctl_table_header *gSysCtlHeader_dcid;
++static struct ctl_table gSysCtlChumaccel[] =
++{
++ { CTL_CHUMACCEL_DEBUG_TRACE, "dbg-trace", &gDebugTrace, sizeof( int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMACCEL_DEBUG_IOCTL, "dbg-ioctl", &gDebugIoctl, sizeof( int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMACCEL_DEBUG_ERROR, "dbg-error", &gDebugError, sizeof( int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMACCEL_OUCHVAL, "touchThresh", &gOuchval, sizeof( unsigned int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMACCEL_BREAKVAL, "painThresh", &gBreakval, sizeof( unsigned int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMACCEL_ROMACT, "romBusy", &gRomBusy, sizeof( unsigned int ), 0444, NULL, &proc_dointvec },
++ { 0 }
++};
++// these are all read-only
++// for now these are also all deprecated!
++static struct ctl_table gSysCtlChumdcid[] =
++{
++ // placeholder
++ { CTL_CHUMDCID_EMPTY, "empty", &gEmpty, sizeof( unsigned int ), 0444, NULL, &proc_dointvec },
++ // { CTL_CHUMDCID_VERS, "dc_version", gDCvers, sizeof( unsigned char ) * VERS_LEN, 0444, NULL, &proc_dointvec },
++ // { CTL_CHUMDCID_SERIAL, "dc_serial", gDCserial, sizeof( unsigned char ) * VERS_LEN, 0444, NULL, &proc_dointvec },
++ // { CTL_CHUMDCID_CORE_VERS, "mb_version", gMBvers, sizeof( unsigned char ) * VERS_LEN, 0444, NULL, &proc_dointvec },
++ // { CTL_CHUMDCID_CORE_SERIAL, "mb_serial", gMBserial, sizeof( unsigned char ) * VERS_LEN, 0444, NULL, &proc_dointvec },
++ { 0 }
++};
++
++static struct ctl_table gSysCtl[] =
++{
++ { CTL_CHUMACCEL, "accel", NULL, 0, 0555, gSysCtlChumaccel },
++ { 0 }
++};
++
++static struct ctl_table gSysCtl_dcid[] =
++{
++ { CTL_CHUMACCEL, "versions", NULL, 0, 0555, gSysCtlChumdcid },
++ { 0 }
++};
++#endif // USE_SYSCTL
++
++// map into the generic driver infrastructure
++static struct file_operations accel_fops = {
++ owner: THIS_MODULE,
++ //llseek: chumby_accel_llseek,
++ read: chumby_accel_read,
++ // poll: chumby_accel_poll,
++ // ioctl: chumby_accel_ioctl,
++ open: chumby_accel_open,
++ release: chumby_accel_release,
++ // write: chumby_accel_write,
++ // fasync: chubmy_accel_fasync,
++};
++
++// map into the generic driver infrastructure
++static struct file_operations dcid_fops = {
++ owner: THIS_MODULE,
++ ioctl: chumby_dcid_ioctl,
++ open: chumby_dcid_open,
++ release: chumby_dcid_release,
++};
++
++///////////// code /////////////
++
++static int spi_tx_fifo_empty(void)
++{ return (SSP_INT_REG(SPI_CHAN) & SSP_INT_TE);}
++
++static int spi_rx_fifo_data_ready(void)
++{ return (SSP_INT_REG(SPI_CHAN) & SSP_INT_RR);}
++
++static unsigned int spi_exchange_data(unsigned int dataTx) {
++ while(!spi_tx_fifo_empty());
++
++ SSP_TX_REG(SPI_CHAN) = dataTx; // transfer data
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_XCH; // exchange data
++
++ while(!spi_rx_fifo_data_ready());
++
++ return SSP_RX_REG(SPI_CHAN);
++}
++
++static unsigned int getX(void) {
++ unsigned int sampled;
++ ACCEL_SEL_CS3;
++ ACCEL_SEL_CS2;
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(8);
++ sampled = spi_exchange_data( (unsigned int) ADC_CHSEL(ADC_XCH) );
++ udelay(50);
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(16);
++ sampled = spi_exchange_data( (unsigned int) ADC_CHSEL(ADC_XCH) );
++ ACCEL_SEL_CS3;
++
++ sampled >>= 4;
++ sampled &= 0xFFF;
++ return sampled;
++}
++static unsigned int getY(void) {
++ unsigned int sampled;
++ ACCEL_SEL_CS3;
++ ACCEL_SEL_CS2;
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(8);
++ sampled = spi_exchange_data( (unsigned int) ADC_CHSEL(ADC_YCH) );
++ udelay(50);
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(16);
++ sampled = spi_exchange_data( (unsigned int) ADC_CHSEL(ADC_YCH) );
++ ACCEL_SEL_CS3;
++
++ sampled >>= 4;
++ sampled &= 0xFFF;
++ return sampled;
++}
++static unsigned int getZ(void) {
++ unsigned int sampled;
++ ACCEL_SEL_CS3;
++ ACCEL_SEL_CS2;
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(8);
++ sampled = spi_exchange_data( (unsigned int) ADC_CHSEL(ADC_ZCH) );
++ udelay(50);
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(16);
++ sampled = spi_exchange_data( (unsigned int) ADC_CHSEL(ADC_ZCH) );
++ ACCEL_SEL_CS3;
++
++ sampled >>= 4;
++ sampled &= 0xFFF;
++ return sampled;
++}
++
++static int chumby_accel_release(struct inode *inode, struct file *file) {
++ CHUMACCEL_DEBUG( Trace, "Top of release.\n" );
++ accel_status &= ~ACCEL_IS_OPEN;
++ return 0;
++}
++
++static int chumby_dcid_release(struct inode *inode, struct file *file) {
++ CHUMDCID_DEBUG( Trace, "Top of release.\n" );
++ dcid_status &= ~DCID_IS_OPEN;
++ return 0;
++}
++
++static int accel_proc_output (char *buf) {
++ int printlen = 0;
++
++ // insert proc debugging output here
++ printlen = sprintf(buf, "Chumby accelerometer driver version %s (bunnie@chumby.com)\n", ACCEL_VERSION );
++ if( gDebugTrace )
++ printlen += sprintf(buf, " Debug trace is enabled.\n" );
++ else
++ printlen += sprintf(buf, " Debug trace is disabled.\n" );
++
++ return(printlen);
++}
++
++static int dcid_proc_output (char *buf) {
++ int printlen = 0;
++
++ printlen = sprintf(buf, "DCversion: %s\nDCserial: %s\n", gDCvers, gDCserial );
++
++ return(printlen);
++}
++
++static int accel_read_proc(char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ int len = accel_proc_output (page);
++ if (len <= off+count) *eof = 1;
++ *start = page + off;
++ len -= off;
++ if (len>count) len = count;
++ if (len<0) len = 0;
++ return len;
++}
++
++static int dcid_read_proc(char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ int len = dcid_proc_output (page);
++ if (len <= off+count) *eof = 1;
++ *start = page + off;
++ len -= off;
++ if (len>count) len = count;
++ if (len<0) len = 0;
++ return len;
++}
++
++static int chumby_accel_open(struct inode *inode, struct file *file) {
++ // make sure we're not opened twice
++ if( accel_status & ACCEL_IS_OPEN)
++ return -EBUSY;
++
++ accel_status |= ACCEL_IS_OPEN;
++ return(0);
++}
++
++static int chumby_dcid_open(struct inode *inode, struct file *file) {
++ // make sure we're not opened twice
++ if( dcid_status & DCID_IS_OPEN)
++ return -EBUSY;
++
++ dcid_status |= DCID_IS_OPEN;
++ return(0);
++}
++
++void accel_task_record(unsigned long ptr) {
++ struct acceldata *data = (struct acceldata *)ptr;
++ unsigned int sampledX, sampledY, sampledZ;
++ int differenceX, differenceY, differenceZ;
++
++ if( gRomBusy == 0 ) { // make sure that ROM isn't using the SPI bus
++ sampledX = getX();
++ sampledY = getY();
++ sampledZ = getZ();
++
++ ////// record data here
++ // remove stale sample from running total bucket
++ data->logTotalX -= data->dataLogX[data->index];
++ data->logTotalY -= data->dataLogY[data->index];
++ data->logTotalZ -= data->dataLogZ[data->index];
++
++ // commit the new data
++ data->dataLogX[data->index] = sampledX;
++ data->dataLogY[data->index] = sampledY;
++ data->dataLogZ[data->index] = sampledZ;
++ data->logTotalX += data->dataLogX[data->index]; // add new sample to running total bucket
++ data->logTotalY += data->dataLogY[data->index];
++ data->logTotalZ += data->dataLogZ[data->index];
++ // update indices
++ data->index++;
++ data->index %= MAX_ACCEL_DATA; // reset modulus
++
++ // process a little bit of data
++ data->runningAvgX = data->logTotalX / MAX_ACCEL_DATA;
++ data->runningAvgY = data->logTotalY / MAX_ACCEL_DATA;
++ data->runningAvgZ = data->logTotalZ / MAX_ACCEL_DATA;
++
++ differenceX = sampledX - data->runningAvgX;
++ differenceY = sampledY - data->runningAvgY;
++ differenceZ = sampledZ - data->runningAvgZ;
++ differenceX = (differenceX < 0) ? (-differenceX) : differenceX;
++ differenceY = (differenceY < 0) ? (-differenceY) : differenceY;
++ differenceZ = (differenceZ < 0) ? (-differenceZ) : differenceZ;
++ if( (differenceX > gAccelThresh) || (differenceY > gAccelThresh) || (differenceZ > gAccelThresh) ) {
++ CHUMACCEL_DEBUG( Trace, "Got accel event.\n" );
++ }
++ if( (differenceX > gOuchval) || (differenceY > gOuchval) || (differenceZ > gOuchval) ) {
++ if( (differenceX > gBreakval) || (differenceY > gBreakval) || (differenceZ > gBreakval) ) {
++ impactHint = IMPCT_HINT_BREAK;
++ } else {
++ impactHint = IMPCT_HINT_OUCH;
++ }
++ impactTime = jiffies;
++ impactVector[0] = sampledX;
++ impactVector[1] = sampledY;
++ impactVector[2] = sampledZ;
++ }
++ }
++
++ recDelay++; // legacy code to keep track of recording delays, keep around in case we want to use it again
++
++ // now handle retasking
++ if( !gDone ) { // requires the done un-set to wait a few hundred ms to flush out the last timer added
++ data->timer.expires += ACCEL_INTERVAL; // next jiffie interval!
++ add_timer( &data->timer );
++ }
++}
++
++/*
++ this driver handles the following sensors:
++
++ * accelerometer (kionix)
++
++*/
++
++static int __init chumby_accel_init(void) {
++ dev_t dev = 0;
++ unsigned int sampledX, sampledY, sampledZ;
++ int result, err;
++ char temp[DCID_SERIAL_LEN + 1];
++ int i;
++
++ acceltask_data.accel_cdev = cdev_alloc();
++
++ // insert all device specific hardware initializations here
++ printk( "Chumby accelerometer driver version %s initializing (bunnie@chumby.com)...\n", ACCEL_VERSION );
++
++ /*
++ * Get a range of minor numbers to work with, asking for a dynamic
++ * major unless directed otherwise at load time.
++ */
++ if (accel_major) {
++ dev = MKDEV(accel_major, accel_minor);
++ result = register_chrdev_region(dev, accel_nr_devs, "accel");
++ } else {
++ result = alloc_chrdev_region(&dev, accel_minor, accel_nr_devs,
++ "accel");
++ accel_major = MAJOR(dev);
++ }
++ if (result < 0) {
++ printk(KERN_WARNING "accel: can't get major %d\n", accel_major);
++ return result;
++ }
++
++ create_proc_read_entry ("accel", 0, 0, accel_read_proc, NULL);
++
++ cdev_init(acceltask_data.accel_cdev, &accel_fops);
++ acceltask_data.accel_cdev->owner = THIS_MODULE;
++ acceltask_data.accel_cdev->ops = &accel_fops;
++ err = cdev_add (acceltask_data.accel_cdev, dev, 1);
++ /* Fail gracefully if need be */
++ if (err)
++ printk(KERN_NOTICE "Error %d adding accel device\n", err);
++
++ // hardware init
++ // map GPIO ports appropriately
++ // CSPI1_SS1 F16 CSPI1_SS1 (GPIO ) PD27 out 1 ** not controlled by CSPI
++ // CSPI1_SS0 F19 CSPI1_SS0 (GPIO ) PD28 out 1 ** not controlled by CSPI
++ // CSPI1_SCLK H10 CSPI1_SCLK(primary) PD29 out 1
++ // CSPI1_MISO F17 CSPI1_MISO(primary) PD30 in 0
++ // CSPI1_MOSI J12 CSPI1_MOSI(primary) PD31 out 1
++ // ** note that SS0,1 is decoded on the sensor card to 4 individual chipselects
++ // so these values have to be manually controlled by the software
++
++ imx_gpio_mode( GPIO_PORTD | 27 | GPIO_OUT | GPIO_GPIO );
++ imx_gpio_mode( GPIO_PORTD | 28 | GPIO_OUT | GPIO_GPIO );
++ ACCEL_SEL_CS3;
++
++ imx_gpio_mode( GPIO_PORTD | 29 | GPIO_OUT | GPIO_PF );
++ imx_gpio_mode( GPIO_PORTD | 30 | GPIO_IN | GPIO_PF );
++ imx_gpio_mode( GPIO_PORTD | 31 | GPIO_OUT | GPIO_PF );
++
++ // turn on the clock
++ PCCR0 |= PCCR0_CSPI1_EN;
++
++ // setup the SPI interfaces
++ // reset the interface
++ SSP_RESET_REG(SPI_CHAN) = 0x00000001;
++ udelay(200); //wait
++
++ // use 32.768kHz clock speed, with 0 clock insertion
++ SSP_PER_REG(SPI_CHAN) = 0x00008000;
++
++ // init the hardware config register
++ // 31-24 00000000
++ // 23 0 BURST off
++ // 22 0 SDHC off
++ // 21 0 SWAP off
++ // 20-19 00 SS asserted
++ // 18-14 00000 ((BASE_FREQ/CLK_DIVIDER) / (TARGET_FREQ) >> 1)
++ // 13-12 00 DRCTL ignore ready
++ // 11 1 MODE = master
++ // 10 1* SPIEN enable interface (toggle to reset Tx FIFO)
++ // 9 0 XCH exchange off for now
++ // 8 0 SSPOL is active low
++ // 7 1 SSCTL CS toggles between bursts
++ // 6 1 PHA phase 1 operation (falling edge)
++ // 5 1 POL (active 1 idle)
++ // 4-0 01111 BITCOUNT 16-bit transfer for Kionix
++ SSP_CTRL_REG(SPI_CHAN) = 0;
++ SSP_CTRL_REG(SPI_CHAN) |= (SSP_MODE_MASTER | SSP_ENABLE | SSP_SS_PULSE | SSP_PHA1 | SSP_POL1 | SSP_WS(16) );
++ SSP_CTRL_REG(SPI_CHAN) |= (((BASE_FREQ/CLK_DIVIDER) / (TARGET_FREQ) >> 1) + 1) << 14;
++ SSP_CTRL_REG(SPI_CHAN) &= ~SSP_MODE_MASTER; // reset fifo
++ // printk( "accel clock init: base freq %d, clk_divider %d, target_freq %d, calc div %d\n", BASE_FREQ, CLK_DIVIDER, TARGET_FREQ, ((BASE_FREQ/CLK_DIVIDER) / (TARGET_FREQ) >> 1) + 1 );
++
++ udelay(100); //wait
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(16);
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_MODE_MASTER; // reset fifo
++
++ // write the accelerometer configuration word
++ ACCEL_SEL_CS3;
++ ACCEL_SEL_CS2;
++ spi_exchange_data( (unsigned int) 0x0404 ); // set power mode to ON
++ ACCEL_SEL_CS3;
++
++ ///// initialize periodic task queue
++ CHUMACCEL_DEBUG( Trace, "got to point 1\n" );
++ acceltask_data.index = 0;
++ acceltask_data.dataLogX = kmalloc( sizeof (unsigned short) * MAX_ACCEL_DATA, GFP_KERNEL );
++ acceltask_data.dataLogY = kmalloc( sizeof (unsigned short) * MAX_ACCEL_DATA, GFP_KERNEL );
++ acceltask_data.dataLogZ = kmalloc( sizeof (unsigned short) * MAX_ACCEL_DATA, GFP_KERNEL );
++ CHUMACCEL_DEBUG( Trace, "got to point 2\n" );
++ if( (acceltask_data.dataLogX == NULL) || (acceltask_data.dataLogY == NULL) || (acceltask_data.dataLogZ == NULL) ) {
++ CHUMACCEL_DEBUG( Error, "Could not allocate acceltask_data structure, I see a panic soon...\n" );
++ // todo: write code to handle this condition gracefully. for now, we just report and crash.
++ // PS: it's very unlikely this will happen--we'd have to run out of memory at driver init time
++ // which means we have bigger problems in reality.
++ }
++ CHUMACCEL_DEBUG( Trace, "got to point 3\n" );
++ sampledX = getX();
++ sampledY = getY();
++ sampledZ = getZ();
++ CHUMACCEL_DEBUG( Trace, "got to point 4\n" );
++ acceltask_data.logTotalX = 0;
++ acceltask_data.logTotalY = 0;
++ acceltask_data.logTotalZ = 0;
++ for( i = 0; i < MAX_ACCEL_DATA; i++ ) {
++ acceltask_data.dataLogX[i] = sampledX;
++ acceltask_data.dataLogY[i] = sampledY;
++ acceltask_data.dataLogZ[i] = sampledZ;
++ acceltask_data.logTotalX += acceltask_data.dataLogX[i];
++ acceltask_data.logTotalY += acceltask_data.dataLogY[i];
++ acceltask_data.logTotalZ += acceltask_data.dataLogZ[i];
++ }
++ CHUMACCEL_DEBUG( Trace, "got to point 5\n" );
++ acceltask_data.runningAvgX = acceltask_data.logTotalX / MAX_ACCEL_DATA;
++ acceltask_data.runningAvgY = acceltask_data.logTotalY / MAX_ACCEL_DATA;
++ acceltask_data.runningAvgZ = acceltask_data.logTotalZ / MAX_ACCEL_DATA;
++ acceltask_data.threshold = ACCEL_DEFAULT_THRESH;
++ acceltask_data.moved = NOT_MOVED;
++ CHUMACCEL_DEBUG( Trace, "got to point 6\n" );
++
++ ///// initialize periodic task queue
++ init_timer( &acceltask_data.timer );
++ acceltask_data.timer.data = (unsigned long) &acceltask_data;
++ acceltask_data.timer.function = accel_task_record;
++ acceltask_data.timer.expires = jiffies + ACCEL_INTERVAL;
++ add_timer( &(acceltask_data.timer) );
++
++ // populate proc entries
++#if USE_SYSCTL
++ gSysCtlHeader = register_sysctl_table( gSysCtl, 0 );
++ if ( gSysCtlHeader != NULL ) {
++ gSysCtlHeader->ctl_table->child->de->owner = THIS_MODULE;
++ }
++#endif
++
++ // insert all device specific hardware initializations here
++ printk( "Chumby DC ID EEPROM driver version %s initializing (bunnie@chumby.com)...\n", DCID_VERSION );
++ dcidtask_data.dcid_cdev = cdev_alloc();
++
++ /*
++ * Get a range of minor numbers to work with, asking for a dynamic
++ * major unless directed otherwise at load time.
++ */
++ if (dcid_major) {
++ dev = MKDEV(dcid_major, dcid_minor);
++ result = register_chrdev_region(dev, dcid_nr_devs, "dcid");
++ } else {
++ result = alloc_chrdev_region(&dev, dcid_minor, dcid_nr_devs,
++ "dcid");
++ dcid_major = MAJOR(dev);
++ }
++ if (result < 0) {
++ printk(KERN_WARNING "dcid: can't get major %d\n", dcid_major);
++ return result;
++ }
++
++ create_proc_read_entry ("DCversion", 0, 0, dcid_read_proc, NULL);
++
++ cdev_init(dcidtask_data.dcid_cdev, &dcid_fops);
++ dcidtask_data.dcid_cdev->owner = THIS_MODULE;
++ dcidtask_data.dcid_cdev->ops = &dcid_fops;
++ err = cdev_add (dcidtask_data.dcid_cdev, dev, 1);
++ /* Fail gracefully if need be */
++ if (err)
++ printk(KERN_NOTICE "Error %d adding dcid device\n", err);
++
++
++ // now populate the DCID read-only fields
++ for( i = 0; i < DCID_REV_LEN; i++ ) {
++ temp[i] = readEEPROM( DCID_REV_LOC + i );
++ }
++ sprintf( gDCvers, "%02X.%02X.%02X.%02X", temp[0], temp[1], temp[2], temp[3] );
++ for( i = 0; i < DCID_SERIAL_LEN; i++ ) {
++ temp[i] = readEEPROM( DCID_SERIAL_LOC + i );
++ }
++ temp[i] = '\0';
++ sprintf( gDCserial, "%s", temp );
++
++ // populate some proc entries
++#if USE_SYSCTL
++ gSysCtlHeader_dcid = register_sysctl_table( gSysCtl_dcid, 0 );
++ if ( gSysCtlHeader_dcid != NULL ) {
++ gSysCtlHeader_dcid->ctl_table->child->de->owner = THIS_MODULE;
++ }
++#endif
++
++ gRomBusy = 0; // make sure the task can get its data...
++
++ return (0);
++}
++
++static ssize_t chumby_accel_read(struct file *file, char *buf,
++ size_t count, loff_t *ppos) {
++ size_t retlen = 0;
++ unsigned int sampledX, sampledY, sampledZ;
++ struct accelReadData rd;
++
++ CHUMACCEL_DEBUG( Trace, "Top of read.\n" );
++
++ if(acceltask_data.index > 0) {
++ sampledX = acceltask_data.dataLogX[acceltask_data.index - 1];
++ sampledY = acceltask_data.dataLogY[acceltask_data.index - 1];
++ sampledZ = acceltask_data.dataLogZ[acceltask_data.index - 1];
++ } else {
++ sampledX = acceltask_data.dataLogX[MAX_ACCEL_DATA - 1];
++ sampledY = acceltask_data.dataLogY[MAX_ACCEL_DATA - 1];
++ sampledZ = acceltask_data.dataLogZ[MAX_ACCEL_DATA - 1];
++ }
++ CHUMACCEL_DEBUG( Trace, "Got values %04X:%04X:%04X.\n", sampledX, sampledY, sampledZ );
++#if EASY_DEBUGGING // easy debugging
++ char retval[200];
++ retlen += sprintf( retval, "%04X:%04X:%04X|%04X:%04X:%04X\n",
++ acceltask_data.runningAvgX, acceltask_data.runningAvgY,
++ acceltask_data.runningAvgZ, sampledX, sampledY, sampledZ );
++
++ retlen++; //+1 for null character
++
++ CHUMACCEL_DEBUG( Trace, "Returning %d bytes\n", retlen );
++ int remaining = copy_to_user(buf,retval,retlen);
++ if(remaining != 0) {
++ CHUMACCEL_DEBUG( Trace, "Copy failed with %d bytes remaining\n", remaining );
++ return -EFAULT;
++ }
++ CHUMACCEL_DEBUG( Trace, "Successfully copied\n" );
++#else
++ rd.version = ACCEL_VERSION_NUM;
++ rd.timestamp = jiffies;
++ rd.inst[0] = sampledX;
++ rd.inst[1] = sampledY;
++ rd.inst[2] = sampledZ;
++ rd.avg[0] = acceltask_data.runningAvgX;
++ rd.avg[1] = acceltask_data.runningAvgY;
++ rd.avg[2] = acceltask_data.runningAvgZ;
++ rd.impact[0] = impactVector[0];
++ rd.impact[1] = impactVector[1];
++ rd.impact[2] = impactVector[2];
++ rd.impactTime = impactTime;
++ rd.impactHint = impactHint;
++ rd.gRange = GRANGE;
++ retlen += sizeof(struct accelReadData);
++ if(copy_to_user(buf, &rd, retlen))
++ return -EFAULT;
++#endif
++ acceltask_data.moved = NOT_MOVED;
++
++ return(retlen);
++}
++
++// ASSUME gRomBusy set before calling this
++void issueWRDI(void) {
++ ACCEL_SEL_CS3;
++ ndelay(50);
++ ACCEL_SEL_CS1; // this is the SPI rom chip select code
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(8);
++ spi_exchange_data( SPIROM_WRDI_CMD ); // disable write latch to prevent spurious writes on power state changes
++ ACCEL_SEL_CS3;
++}
++
++// ASSUME gRomBusy set before calling this
++void issueWREN(void) {
++ ACCEL_SEL_CS3;
++ ndelay(50); // min pulse time for CS
++ ACCEL_SEL_CS1; // this is the SPI rom chip select code
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(8);
++ spi_exchange_data( SPIROM_WREN_CMD ); // issue WREN command
++ ACCEL_SEL_CS3;
++}
++
++// ASSUME gRomBusy set before calling this
++void waitEEPROMbusy(void) {
++ unsigned int retdat = 0;
++
++ do { // this is ugly...should I yield somewhere? I'm also totally stomping the accelerometer.
++ ACCEL_SEL_CS3;
++ ndelay(50);
++ ACCEL_SEL_CS1; // this is the SPI rom chip select code
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(16);
++ retdat = spi_exchange_data( SPIROM_RDSR_CMD << 8 ); // check status register
++ ACCEL_SEL_CS3;
++ } while( retdat & 0x1 );
++
++}
++
++static unsigned char readEEPROM(unsigned int addr) {
++ unsigned int retdat;
++
++ gRomBusy = 1; // this shuts off the kernel task timer from using the SPI bus
++ do {
++ ACCEL_SEL_CS3;
++ ndelay(50);
++ ACCEL_SEL_CS1; // this is the SPI rom chip select code
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(16);
++ retdat = spi_exchange_data( SPIROM_RDSR_CMD << 8 ); // check status register
++ ACCEL_SEL_CS3;
++ } while( retdat & 0x1 );
++
++ ACCEL_SEL_CS3;
++ ndelay(50);
++ ACCEL_SEL_CS1;
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(32);
++ retdat = spi_exchange_data( ((SPIROM_READ_CMD & 0xFF) << 24) |
++ ((addr & 0xFFFF) << 8) );
++ ACCEL_SEL_CS3;
++ gRomBusy = 0; // this shuts off the kernel task timer from using the SPI bus
++
++ return( (unsigned char) retdat & 0xFF );
++}
++
++
++static int chumby_dcid_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
++ unsigned long arg) {
++ struct eeprom_data dciddat;
++ unsigned int retdat;
++ int err;
++
++ // safety code
++ CHUMACCEL_DEBUG( Trace, "type: '%c' cmd: 0x%x\n", _IOC_TYPE( cmd ), _IOC_NR( cmd ));
++
++ if (( _IOC_TYPE( cmd ) != ACCEL_IOCTL_MAGIC )
++ || ( _IOC_NR( cmd ) < ACCEL_CMD_FIRST )
++ || ( _IOC_NR( cmd ) >= ACCEL_CMD_LAST )) {
++ // Since we emulate some of the parallel port commands, we need to allow
++ // those as well.
++ return -ENOTTY;
++ }
++
++ // Note that _IOC_DIR Read/Write is from the perspective of userland. access_ok
++ // is from the perspective of kernelland.
++
++ err = 0;
++ if (( _IOC_DIR( cmd ) & _IOC_READ ) != 0 ) {
++ err |= !access_ok( VERIFY_WRITE, (void *)arg, _IOC_SIZE( cmd ));
++ }
++ if (( _IOC_DIR( cmd ) & _IOC_WRITE ) != 0 ) {
++ err |= !access_ok( VERIFY_READ, (void *)arg, _IOC_SIZE( cmd ));
++ }
++ if ( err ) {
++ CHUMACCEL_DEBUG( Error, "arg pointer is invalid\n" );
++ return -EFAULT;
++ }
++ // end safety code
++
++ err = 0;
++ switch (cmd) {
++ case ACCEL_IOCTL_SETROM:
++ if( copy_from_user(&dciddat, (void *)arg, sizeof dciddat) )
++ return -EFAULT;
++
++ gRomBusy = 1; // this shuts off the kernel task timer from using the SPI bus
++ waitEEPROMbusy();
++ issueWREN();
++
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(32);
++ ACCEL_SEL_CS3;
++ ndelay(50);
++ ACCEL_SEL_CS1;
++ spi_exchange_data( ((SPIROM_WRITE_CMD & 0xFF) << 24) | // write the data
++ ((dciddat.address & 0xFFFF) << 8) |
++ (dciddat.data & 0xFF) );
++ ACCEL_SEL_CS3;
++
++ waitEEPROMbusy();
++ issueWRDI();
++
++ ACCEL_SEL_CS3;
++ ndelay(50);
++ ACCEL_SEL_CS1;
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(32);
++ retdat = spi_exchange_data( ((SPIROM_READ_CMD & 0xFF) << 24) | // read it back
++ ((dciddat.address & 0xFFFF) << 8) );
++ ACCEL_SEL_CS3;
++
++ CHUMDCID_DEBUG( Ioctl, "Got %02X from %04X\n", retdat & 0xFF, dciddat.address & 0xFFFF );
++ dciddat.data = (unsigned char) retdat & 0xFF;
++ gRomBusy = 0; // restores kernel task timer's ability to use SPI bus
++
++ err = copy_to_user((void *)arg, &dciddat, sizeof dciddat) ? -EFAULT : 0;
++ break;
++ case ACCEL_IOCTL_READROM:
++ if( copy_from_user(&dciddat, (void *)arg, sizeof dciddat) )
++ return -EFAULT;
++ gRomBusy = 1; // this shuts off the kernel task timer from using the SPI bus
++
++ waitEEPROMbusy();
++
++ ACCEL_SEL_CS3;
++ ndelay(50);
++ ACCEL_SEL_CS1;
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(32);
++ retdat = spi_exchange_data( ((SPIROM_READ_CMD & 0xFF) << 24) |
++ ((dciddat.address & 0xFFFF) << 8) );
++ ACCEL_SEL_CS3;
++
++ gRomBusy = 0; // restores kernel task timer's ability to use SPI bus
++ dciddat.data = (unsigned char) retdat & 0xFF;
++
++ err = copy_to_user((void *)arg, &dciddat, sizeof dciddat) ? -EFAULT : 0;
++ break;
++
++ case ACCEL_IOCTL_READSTAT:
++ gRomBusy = 1; // this shuts off the kernel task timer from using the SPI bus
++
++ ACCEL_SEL_CS3;
++ ndelay(50);
++ ACCEL_SEL_CS1; // this is the SPI rom chip select code
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(16);
++ retdat = spi_exchange_data( SPIROM_RDSR_CMD << 8 ); // check status register
++ ACCEL_SEL_CS3;
++
++ dciddat.data = (unsigned char) retdat & 0xFF;
++
++ err = copy_to_user((void *)arg, &dciddat, sizeof dciddat) ? -EFAULT : 0;
++ gRomBusy = 0; // restores kernel task timer's ability to use SPI bus
++ break;
++
++ case ACCEL_IOCTL_LOCKROM:
++ gRomBusy = 1; // this shuts off the kernel task timer from using the SPI bus
++ waitEEPROMbusy();
++ issueWREN();
++
++ ACCEL_SEL_CS3;
++ ndelay(50);
++ ACCEL_SEL_CS1;
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(16); // WRSR operation
++ spi_exchange_data((SPIROM_WRSR_CMD & 0xFF << 8) |
++ 0x04); // just write protect addresses 0x300-0x3FF
++ ACCEL_SEL_CS3;
++
++ waitEEPROMbusy();
++ issueWRDI();
++ err = 0;
++ gRomBusy = 0; // restores kernel task timer's ability to use SPI bus
++ break;
++
++ case ACCEL_IOCTL_UNLOCKROM:
++ gRomBusy = 1; // this shuts off the kernel task timer from using the SPI bus
++ waitEEPROMbusy();
++ issueWREN();
++
++ ACCEL_SEL_CS3;
++ ndelay(50);
++ ACCEL_SEL_CS1;
++ SSP_CTRL_REG(SPI_CHAN) &= 0xFFFFFFE0;
++ SSP_CTRL_REG(SPI_CHAN) |= SSP_WS(16); // WRSR operation
++ spi_exchange_data((SPIROM_WRSR_CMD & 0xFF << 8) |
++ 0x00); // unlock everything
++ ACCEL_SEL_CS3;
++
++ waitEEPROMbusy();
++ issueWRDI();
++ err = 0;
++ gRomBusy = 0; // restores kernel task timer's ability to use SPI bus
++ break;
++
++ default:
++ return -EINVAL;
++ }
++
++ return err;
++
++}
++
++
++static void __exit chumby_accel_exit(void)
++{
++ dev_t devno = MKDEV(accel_major, accel_minor);
++
++ CHUMACCEL_DEBUG( Trace, "Top of exit.\n" );
++ // set global flag that we're out of here and force a call to remove ourselves
++ gDone = 1;
++ del_timer( &(acceltask_data.timer) ); // delete our kernel task
++
++ mdelay(200); // wait to dequeue self; if kernel panics on rmmod try adding 100 to this value
++
++ // insert all cleanup stuff here
++ cdev_del( acceltask_data.accel_cdev );
++ remove_proc_entry( "accel", NULL );
++ unregister_chrdev_region(devno, accel_nr_devs);
++
++ devno = MKDEV(dcid_major, dcid_minor);
++ cdev_del( dcidtask_data.dcid_cdev );
++ unregister_chrdev_region(devno, dcid_nr_devs);
++
++#if USE_SYSCTL
++ if ( gSysCtlHeader != NULL ) {
++ unregister_sysctl_table( gSysCtlHeader );
++ }
++ if ( gSysCtlHeader_dcid != NULL ) {
++ unregister_sysctl_table( gSysCtlHeader_dcid );
++ }
++#endif
++
++}
++
++// entry and exit mappings
++module_init(chumby_accel_init);
++module_exit(chumby_accel_exit);
+--- /dev/null
++++ b/drivers/char/chumby_accel.h
+@@ -0,0 +1,134 @@
++/*
++ chumby_accel.h
++ bunnie -- March 2007 -- 2.0 -- port to Ironforge
++
++ This file is part of the chumby accelerometer driver in the linux kernel.
++ Copyright (c) Chumby Industries, 2007
++
++ The accelerometer driver is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ The accelerometer driver 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 General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with the Chumby; if not, write to the Free Software
++ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++*/
++
++#include <linux/ioctl.h>
++
++#define ADC_CHSEL(x) (x)
++#define ADC_XCH 0
++#define ADC_YCH 2
++#define ADC_ZCH 1
++
++// always transition through CS3 because we can update both bits at once
++// and spurious transitions on CS3 are "harmless" by design
++#define CSPI_SS0_PIN 28
++#define CSPI_SS1_PIN 27
++
++#define ACCEL_SEL_CS3 imx_gpio_write( GPIO_PORTD | CSPI_SS1_PIN, 1 ); imx_gpio_write( GPIO_PORTD | CSPI_SS0_PIN, 1 );
++#define ACCEL_SEL_CS2 imx_gpio_write( GPIO_PORTD | CSPI_SS1_PIN, 1 ); imx_gpio_write( GPIO_PORTD | CSPI_SS0_PIN, 0 );
++#define ACCEL_SEL_CS1 imx_gpio_write( GPIO_PORTD | CSPI_SS1_PIN, 0 ); imx_gpio_write( GPIO_PORTD | CSPI_SS0_PIN, 1 );
++#define ACCEL_SEL_CS0 imx_gpio_write( GPIO_PORTD | CSPI_SS1_PIN, 0 ); imx_gpio_write( GPIO_PORTD | CSPI_SS0_PIN, 0 );
++
++#define ACCEL_IS_OPEN 0x1
++#define DCID_IS_OPEN 0x1
++
++#define ACCEL_IOCTL_MAGIC 'C'
++
++#define SPIROM_WREN_CMD 0x06
++#define SPIROM_WRDI_CMD 0x04
++#define SPIROM_RDSR_CMD 0x05
++#define SPIROM_WRSR_CMD 0x01
++#define SPIROM_READ_CMD 0x03
++#define SPIROM_WRITE_CMD 0x02
++
++#define DCID_REV_LOC 0x300
++#define DCID_REV_LEN 4
++
++#define DCID_SERIAL_LOC 0x304
++#define DCID_SERIAL_LEN 12 // serial number length in bytes
++
++#define VERS_LEN (DCID_SERIAL_LEN + 1) // space for the null character
++
++
++/**
++ * Defines for each of the commands. Note that since we want to reduce
++ * the possibility that a user mode program gets out of sync with a given
++ * driver, we explicitly assign a value to each enumeration. This makes
++ * it more difficult to stick new ioctl's in the middle of the list.
++ */
++
++typedef enum
++ {
++ ACCEL_CMD_FIRST = 0x80, // just a placeholder, not a command
++
++ ACCEL_CMD_SETROM = 0x80,
++ ACCEL_CMD_READROM = 0x81,
++ ACCEL_CMD_LOCKROM = 0x82,
++ ACCEL_CMD_UNLOCKROM = 0x83,
++ ACCEL_CMD_READSTAT = 0x84,
++
++ /* Insert new ioctls here */
++
++ ACCEL_CMD_LAST, // another placeholder
++} ACCEL_CMD;
++
++/*
++ * Our ioctl commands
++ */
++
++#define ACCEL_IOCTL_SETROM _IOWR( ACCEL_IOCTL_MAGIC, ACCEL_CMD_SETROM, int ) // arg is int
++#define ACCEL_IOCTL_READROM _IOWR( ACCEL_IOCTL_MAGIC, ACCEL_CMD_READROM, int ) // arg is int
++#define ACCEL_IOCTL_READSTAT _IOWR( ACCEL_IOCTL_MAGIC, ACCEL_CMD_READSTAT, int ) // arg is int
++#define ACCEL_IOCTL_LOCKROM _IOWR( ACCEL_IOCTL_MAGIC, ACCEL_CMD_LOCKROM, int )
++#define ACCEL_IOCTL_UNLOCKROM _IOWR( ACCEL_IOCTL_MAGIC, ACCEL_CMD_UNLOCKROM, int )
++
++/*
++ * The following are for entries in /proc/sys/chumbend
++ */
++#define CTL_CHUMACCEL 0x4348554F /* 'CHUP' in hex form */
++
++enum
++{
++ CTL_CHUMACCEL_DEBUG_TRACE = 101,
++ CTL_CHUMACCEL_DEBUG_IOCTL = 102,
++ CTL_CHUMACCEL_DEBUG_ERROR = 103,
++ CTL_CHUMACCEL_PWMVAL = 104,
++ CTL_CHUMACCEL_OUCHVAL = 105,
++ CTL_CHUMACCEL_BREAKVAL = 106,
++ CTL_CHUMACCEL_CLEAR = 107,
++ CTL_CHUMACCEL_ROMACT = 108,
++ CTL_CHUMDCID_VERS = 109,
++ CTL_CHUMDCID_SERIAL = 110,
++ CTL_CHUMDCID_CORE_VERS = 111,
++ CTL_CHUMDCID_CORE_SERIAL = 112,
++ CTL_CHUMDCID_EMPTY = 113,
++};
++
++// data structures
++//#define ACCEL_VERSION_NUM 1 // experimental katamari version
++#define ACCEL_VERSION_NUM 3 // ironforge release with Kionix accelerometer
++
++#define GRANGE 2500 // kionix is +/- 2g with reporting range up to 2.5g
++struct accelReadData {
++ unsigned int version;
++ unsigned int timestamp;
++ unsigned int inst[3]; // x, y, z
++ unsigned int avg[3];
++ unsigned int impact[3]; // values for the last impact peak acceleration
++ unsigned int impactTime;
++ unsigned int impactHint;
++ unsigned int gRange; // g range in milli-g's
++};
++
++struct eeprom_data {
++ unsigned int address; // address of read or write
++ unsigned char data; // data to be written (or read data upon return)
++};
+--- /dev/null
++++ b/drivers/char/chumby_sense1.c
+@@ -0,0 +1,461 @@
++/*
++ chumby_sense1.c
++ bunnie -- June 2006 -- 1.4 -- linux 2.4.20
++ bunnie -- March 2007 -- 2.0 -- port to Ironforge linux 2.6.16
++
++ This file is part of the chumby sensor suite driver in the linux kernel.
++ Copyright (c) Chumby Industries, 2007
++
++ The sensor suite driver is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ The sensor suite driver 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 General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with the Chumby; if not, write to the Free Software
++ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++*/
++
++#define SENSE1_VERSION "2.0-Ironforge"
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++
++#include <linux/kernel.h> /* printk() */
++#include <linux/slab.h> /* kmalloc() */
++#include <linux/fs.h> /* everything... */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/proc_fs.h>
++#include <linux/fcntl.h> /* O_ACCMODE */
++#include <linux/seq_file.h>
++#include <linux/cdev.h>
++
++#include <asm/io.h>
++#include <asm/system.h> /* cli(), *_flags */
++#include <asm/uaccess.h> /* copy_*_user */
++
++#include <linux/miscdevice.h>
++#include <linux/ioport.h>
++#include <linux/poll.h>
++#include <linux/sysctl.h>
++#include <linux/spinlock.h>
++#include <linux/delay.h>
++#include <linux/rtc.h>
++
++#include <linux/timer.h>
++
++#include <asm/arch/imx-regs.h>
++
++#include "chumby_sense1.h"
++
++/*
++ * basic parameters
++ */
++
++int sense1_major = 0; // dynamic allocation
++int sense1_minor = 0;
++int sense1_nr_devs = 1;
++
++module_param(sense1_major, int, S_IRUGO);
++module_param(sense1_minor, int, S_IRUGO);
++module_param(sense1_nr_devs, int, S_IRUGO);
++
++MODULE_AUTHOR("bunnie@chumby.com");
++MODULE_LICENSE("GPL");
++
++// static data
++static int gDone = 0;
++static unsigned long sense1_status = 0; /* bitmapped status byte. */
++
++/*
++ * Sense1 sensor data logs, tracked by tasks that are scheduled by the
++ * task scheduler
++ */
++
++#define FIXEDPOINT_NORM 1000
++
++struct sense1data {
++ unsigned char bent;
++ unsigned char isDebounce;
++ int lastTransitionState;
++ unsigned long lastTransitionTime;
++ unsigned long currentTransitionTime;
++ struct timer_list timer;
++ struct cdev *sense1_cdev;
++} sense1task_data;
++
++#define USE_SYSCTL 1
++
++#define SENSE1_BLOCKING 0 // turn off blocking read on sense1 sensor
++
++#if 1
++# if USE_SYSCTL
++# define CHUMSENSE1_DEBUG( flag, fmt, args... ) do { if ( gDebug ## flag ) printk( "%s: " fmt, __FUNCTION__ , ## args ); } while (0)
++# else
++# define CHUMSENSE1_DEBUG( flag, fmt, args... ) printk( "%s: " fmt, __FUNCTION__ , ## args )
++# endif
++#else
++# define CHUMSENSE1_DEBUG( flag, fmt, args... )
++#endif
++
++// function protos
++static int chumby_sense1_open(struct inode *inode, struct file *file);
++static int chumby_sense1_release(struct inode *inode, struct file *file);
++static int sense1_read_proc(char *page, char **start, off_t off,
++ int count, int *eof, void *data);
++static ssize_t chumby_sense1_read(struct file *file, char *buf,
++ size_t count, loff_t *ppos);
++//static ssize_t chumby_sense1_write(struct file *file, const unsigned char *buf,
++// size_t count, loff_t *ppos );
++//static unsigned int chumby_sense1_poll(struct file * filp, struct poll_table_struct * wait);
++
++#if USE_SYSCTL
++
++// proc debug structure
++static int gDebugTrace = 0;
++static int gDebugIoctl = 0;
++static int gDebugError = 1;
++static unsigned int gSpkMute = 0;
++static int gDCmillivolts = -1;
++static int gBTmillivolts = -1;
++static unsigned int gDimLevel = 0;
++static unsigned int gHPin = 0;
++static unsigned int gDebounce = 10;
++static unsigned int gResetSerial = 0;
++
++// create /proc/sys/debug-trace, etc...which can be written to set behavior of globals
++// in this driver on the fly
++static struct ctl_table_header *gSysCtlHeader;
++static struct ctl_table gSysCtlChumsense1[] =
++{
++ { CTL_CHUMSENSE1_DEBUG_TRACE, "dbg-trace", &gDebugTrace, sizeof( int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMSENSE1_DEBUG_IOCTL, "dbg-ioctl", &gDebugIoctl, sizeof( int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMSENSE1_DEBUG_ERROR, "dbg-error", &gDebugError, sizeof( int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMSENSE1_SPKMUTE, "spkmute", &gSpkMute, sizeof( unsigned int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMSENSE1_DIMLEVEL, "dimlevel", &gDimLevel, sizeof( unsigned int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMSENSE1_DCPOWER, "DCvolts", &gDCmillivolts, sizeof( unsigned int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMSENSE1_BTPOWER, "BTvolts", &gBTmillivolts, sizeof( unsigned int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMSENSE1_HPIN, "hpin", &gHPin, sizeof( unsigned int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMSENSE1_DEBOUNCE, "debounce", &gDebounce, sizeof( unsigned int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMSENSE1_RESETSERIAL, "resetSerial", &gResetSerial, sizeof( unsigned int ), 0644, NULL, &proc_dointvec },
++ { 0 }
++};
++
++static struct ctl_table gSysCtl[] =
++{
++ { CTL_CHUMSENSE1, "sense1", NULL, 0, 0555, gSysCtlChumsense1 },
++ { 0 }
++};
++#endif // USE_SYSCTL
++
++// map into the generic driver infrastructure
++static struct file_operations sense1_fops = {
++ owner: THIS_MODULE,
++ // llseek: chumby_sense1_llseek,
++ read: chumby_sense1_read,
++ // poll: chumby_sense1_poll,
++ // ioctl: chumby_sense1_ioctl,
++ open: chumby_sense1_open,
++ release: chumby_sense1_release,
++ // write: chumby_sense1_write,
++ // fasync: chubmy_sense1_fasync,
++};
++
++///////////// code /////////////
++
++static int chumby_sense1_release(struct inode *inode, struct file *file) {
++ CHUMSENSE1_DEBUG( Trace, "Top of release.\n" );
++ sense1_status &= ~SENSE1_IS_OPEN;
++ return 0;
++}
++
++static int sense1_proc_output (char *buf) {
++ int printlen = 0;
++
++ // insert proc debugging output here
++ printlen = sprintf(buf, "Chumby sense1 driver version %s (bunnie@chumby.com)\n", SENSE1_VERSION );
++ buf += printlen;
++ if( gDebugTrace )
++ printlen += sprintf(buf, " Debug trace is enabled.\n" );
++ else
++ printlen += sprintf(buf, " Debug trace is disabled.\n" );
++
++ return(printlen);
++}
++
++static int sense1_read_proc(char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ int len = sense1_proc_output (page);
++ if (len <= off+count) *eof = 1;
++ *start = page + off;
++ len -= off;
++ if (len>count) len = count;
++ if (len<0) len = 0;
++
++ // insert printk's
++ printk( "UART test output\n" );
++ /*
++ printk( "URXD0(IMX_UART3_BASE): %08lX\n", (unsigned long) URXD0(IMX_UART3_BASE) );
++ printk( "URTX0(IMX_UART3_BASE): %08lX\n", (unsigned long) URTX0(IMX_UART3_BASE) );
++ */
++ printk( "UCR1 (IMX_UART3_BASE): %08lX\n", (unsigned long) UCR1 (IMX_UART3_BASE) );
++ printk( "UCR2 (IMX_UART3_BASE): %08lX\n", (unsigned long) UCR2 (IMX_UART3_BASE) );
++ printk( "UCR3 (IMX_UART3_BASE): %08lX\n", (unsigned long) UCR3 (IMX_UART3_BASE) );
++ printk( "UCR4 (IMX_UART3_BASE): %08lX\n", (unsigned long) UCR4 (IMX_UART3_BASE) );
++ printk( "UFCR (IMX_UART3_BASE): %08lX\n", (unsigned long) UFCR (IMX_UART3_BASE) );
++ printk( "USR1 (IMX_UART3_BASE): %08lX\n", (unsigned long) USR1 (IMX_UART3_BASE) );
++ printk( "USR2 (IMX_UART3_BASE): %08lX\n", (unsigned long) USR2 (IMX_UART3_BASE) );
++ printk( "UESC (IMX_UART3_BASE): %08lX\n", (unsigned long) UESC (IMX_UART3_BASE) );
++ printk( "UTIM (IMX_UART3_BASE): %08lX\n", (unsigned long) UTIM (IMX_UART3_BASE) );
++ printk( "UBIR (IMX_UART3_BASE): %08lX\n", (unsigned long) UBIR (IMX_UART3_BASE) );
++ printk( "UBMR (IMX_UART3_BASE): %08lX\n", (unsigned long) UBMR (IMX_UART3_BASE) );
++ printk( "UBRC (IMX_UART3_BASE): %08lX\n", (unsigned long) UBRC (IMX_UART3_BASE) );
++ printk( "ONMES(IMX_UART3_BASE): %08lX\n", (unsigned long) ONMES(IMX_UART3_BASE) );
++ printk( "UTS (IMX_UART3_BASE): %08lX\n", (unsigned long) UTS (IMX_UART3_BASE) );
++
++ return len;
++}
++
++static int chumby_sense1_open(struct inode *inode, struct file *file) {
++ // make sure we're not opened twice
++ if( sense1_status & SENSE1_IS_OPEN)
++ return -EBUSY;
++
++ sense1_status |= SENSE1_IS_OPEN;
++ return(0);
++}
++
++void sense1_task_record(unsigned long ptr) {
++ struct sense1data *data = (struct sense1data *)ptr;
++ int state = 0;
++
++ if( gResetSerial ) {
++ gResetSerial = 0;
++ UCR2(IMX_UART3_BASE) &= 0xFFFFFFFE;
++ }
++
++ if( data->currentTransitionTime == 0xFFFFFFFF ) {
++ // handle roll-over case
++ data->currentTransitionTime = 0;
++ // this is cheesy but in practice this should work
++ data->lastTransitionTime = 0;
++ } else {
++ data->currentTransitionTime++;
++ }
++
++ if( data->isDebounce ) {
++ // debouncing
++ if( data->currentTransitionTime > data->lastTransitionTime + gDebounce ) {
++ state = imx_gpio_read( GPIO_PORTKP | 0 );
++
++ if( state == data->lastTransitionState ) {
++ if( (state & 1) == 0 ) // 0 if switch is depressed
++ data->bent = 1;
++ else
++ data->bent = 0;
++ } else {
++ // ignore, it was spurious
++ }
++ // reset our state machine
++ data->lastTransitionTime = data->currentTransitionTime;
++ data->lastTransitionState = state;
++ data->isDebounce = 0;
++ } else {
++ // just wait until our turn comes
++ }
++ } else {
++ // not a debounce period
++ state = imx_gpio_read( GPIO_PORTKP | 0 ); // grab the current state
++ if( state != data->lastTransitionState ) {
++ // something changed, enter the debounce state
++ data->isDebounce = 1;
++ data->lastTransitionState = state;
++ data->lastTransitionTime = data->currentTransitionTime;
++ } else {
++ // else do nothing
++ }
++ }
++
++ // Handle LCD brightness control
++ switch( gDimLevel ) {
++ case 0: // on and bright
++ imx_gpio_write( GPIO_PORTC | 28, 1 );
++ imx_gpio_write( GPIO_PORTKP | (2 + 8), 1 );
++ break;
++ case 1: // on and dim
++ imx_gpio_write( GPIO_PORTC | 28, 0 );
++ imx_gpio_write( GPIO_PORTKP | (2 + 8), 1 );
++ break;
++ case 2: // off and (don't care, set to dim)
++ imx_gpio_write( GPIO_PORTC | 28, 0 );
++ imx_gpio_write( GPIO_PORTKP | (2 + 8), 0 );
++ break;
++ default:
++ break;
++ }
++
++ // handle speaker muting
++ if( gSpkMute == 1 ) {
++ imx_gpio_write( GPIO_PORTKP | (4 + 8), 1 ); // speakers off
++ } else {
++ imx_gpio_write( GPIO_PORTKP | (4 + 8), 0 ); // speakers on
++ }
++
++ // handle headphone plug-in status reporting
++ if( imx_gpio_read( GPIO_PORTKP | 3 | GPIO_IN ) )
++ gHPin = 0;
++ else
++ gHPin = 1;
++
++ // TODO: Handle battery and main voltage measurements and reporting
++
++ // now handle retasking
++ if( !gDone ) { // requires the done un-set to wait a few hundred ms to flush out the last timer added
++ data->timer.expires += 1; // next jiffie interval!
++ add_timer( &data->timer );
++ }
++}
++
++/*
++ this driver handles the following sensors:
++
++ * dimming level
++ * speaker muting
++ * DC voltage reading
++ * battery voltage reading
++ * headphone insertion status
++ * bend sensor ('switch')
++
++*/
++
++static int __init chumby_sense1_init(void) {
++ dev_t dev = 0;
++ int result, err;
++
++ sense1task_data.sense1_cdev = cdev_alloc();
++
++ // insert all device specific hardware initializations here
++ printk( "Chumby sensor suite 1 driver version %s initializing (bunnie@chumby.com)...\n", SENSE1_VERSION );
++
++ /*
++ * Get a range of minor numbers to work with, asking for a dynamic
++ * major unless directed otherwise at load time.
++ */
++ if (sense1_major) {
++ dev = MKDEV(sense1_major, sense1_minor);
++ result = register_chrdev_region(dev, sense1_nr_devs, "switch");
++ } else {
++ result = alloc_chrdev_region(&dev, sense1_minor, sense1_nr_devs,
++ "switch");
++ sense1_major = MAJOR(dev);
++ }
++ if (result < 0) {
++ printk(KERN_WARNING "switch: can't get major %d\n", sense1_major);
++ return result;
++ }
++
++ create_proc_read_entry ("sense1", 0, 0, sense1_read_proc, NULL);
++
++ cdev_init(sense1task_data.sense1_cdev, &sense1_fops);
++ sense1task_data.sense1_cdev->owner = THIS_MODULE;
++ sense1task_data.sense1_cdev->ops = &sense1_fops;
++ err = cdev_add (sense1task_data.sense1_cdev, dev, 1);
++ /* Fail gracefully if need be */
++ if (err)
++ printk(KERN_NOTICE "Error %d adding switch device\n", err);
++
++ // enable KPP interface
++ PCCR1 |= PCCR1_KPP_EN;
++ KPP_KPSR |= KPP_KPSR_KPPEN;
++
++ ////////// audio shutdown init code, KP_COL4
++ imx_gpio_mode( GPIO_PORTKP | (4 + 8) | GPIO_OUT );
++ imx_gpio_write( GPIO_PORTKP | (4 + 8), 0 ); // speakers on
++
++ ////////// LCD power on code
++ imx_gpio_mode( GPIO_PORTKP | (2 + 8) | GPIO_OUT );
++ imx_gpio_write( GPIO_PORTKP | (2 + 8), 1 ); // LCD on
++
++ ////////// LCD brightness control code, pin A19, PC28
++ imx_gpio_mode( GPIO_PORTC | 28 | GPIO_OUT | GPIO_GPIO );
++ imx_gpio_write( GPIO_PORTC | 28, 1 ); // LCD bright
++
++ ///// headphone detect input pin, KP_ROW3
++ imx_gpio_mode( GPIO_PORTKP | 3 | GPIO_IN );
++
++ ///// bend sensor input pin, KP_ROW0
++ imx_gpio_mode( GPIO_PORTKP | 0 | GPIO_IN );
++
++
++#if USE_SYSCTL
++ gSysCtlHeader = register_sysctl_table( gSysCtl, 0 );
++ if ( gSysCtlHeader != NULL ) {
++ gSysCtlHeader->ctl_table->child->de->owner = THIS_MODULE;
++ }
++#endif
++
++ ///// initialize periodic task queue
++ sense1task_data.bent = 0;
++ sense1task_data.lastTransitionTime = jiffies;
++ sense1task_data.isDebounce = 0;
++ sense1task_data.currentTransitionTime = sense1task_data.lastTransitionTime;
++ sense1task_data.lastTransitionState = 1; // default state of the sensor
++
++ init_timer( &sense1task_data.timer );
++ sense1task_data.timer.data = (unsigned long) &sense1task_data;
++ sense1task_data.timer.function = sense1_task_record;
++ sense1task_data.timer.expires = jiffies + 1;
++ add_timer( &(sense1task_data.timer) );
++
++ return (0);
++}
++
++static ssize_t chumby_sense1_read(struct file *file, char *buf,
++ size_t count, loff_t *ppos) {
++ char retval = 0;
++ size_t retlen = 1;
++
++ retval = sense1task_data.bent;
++
++ CHUMSENSE1_DEBUG( Trace, "sense1 read exit with: %d\n", retval );
++ copy_to_user(buf,&retval,retlen);
++
++ return(retlen);
++}
++
++static void __exit chumby_sense1_exit(void) {
++ dev_t devno = MKDEV(sense1_major, sense1_minor);
++
++ CHUMSENSE1_DEBUG( Trace, "Top of exit.\n" );
++ // set global flag that we're out of here and force a call to remove ourselves
++ gDone = 1;
++ del_timer( &(sense1task_data.timer) ); // delete our kernel task
++
++ mdelay(200); // wait to dequeue self; if kernel panics on rmmod try adding 100 to this value
++
++ // insert all cleanup stuff here
++ cdev_del( sense1task_data.sense1_cdev );
++ remove_proc_entry( "sense1", NULL );
++ unregister_chrdev_region(devno, sense1_nr_devs);
++
++#if USE_SYSCTL
++ if ( gSysCtlHeader != NULL ) {
++ unregister_sysctl_table( gSysCtlHeader );
++ }
++#endif
++
++}
++
++// entry and exit mappings
++module_init(chumby_sense1_init);
++module_exit(chumby_sense1_exit);
++
+--- /dev/null
++++ b/drivers/char/chumby_sense1.h
+@@ -0,0 +1,97 @@
++/*
++ chumby_sense1.h
++ bunnie -- June 2006 -- 1.4 -- linux 2.4.20
++ bunnie -- March 2007 -- 2.0 -- port to Ironforge linux 2.6.16
++
++ This file is part of the chumby sensor suite driver in the linux kernel.
++ Copyright (c) Chumby Industries, 2007
++
++ The sensor suite driver is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ The sensor suite driver 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 General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with the Chumby; if not, write to the Free Software
++ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++*/
++
++#include <linux/ioctl.h>
++
++// hysteresis defines
++#define DIM_LIGHT_TO_DARK 0
++#define DIM_DARK_TO_LIGHT 1
++
++// spi defines
++#define SPI_TE_INT_BIT 0x00000001
++#define SPI_TSHFE_INT_BIT 0x00000008
++#define SPI_XCH_BIT 0x00000200
++#define SPI_RR_INT_BIT 0x00000010
++#define SPI_XCH_MASK 0xfffffdff
++#define SPI_EN_BIT 0x00000400
++
++////// local macros; eventually migrate to custom .h file ////
++#define SENSE1_IS_OPEN 0x1
++// IOCTL commands
++#define SENSE1_LOOPBACK 0x0
++#define SENSE1_HASH_ARG 0x1
++// etc...
++
++// device numbers are allocated dynamically
++// see /proc/devices
++
++#define SENSE1_IOCTL_MAGIC 'C'
++
++/**
++ * Defines for each of the commands. Note that since we want to reduce
++ * the possibility that a user mode program gets out of sync with a given
++ * driver, we explicitly assign a value to each enumeration. This makes
++ * it more difficult to stick new ioctl's in the middle of the list.
++ */
++
++typedef enum
++ {
++ SENSE1_CMD_FIRST = 0x80, // just a placeholder, not a command
++
++ /* Insert new ioctls here */
++
++ SENSE1_CMD_LAST, // another placeholder
++} SENSE1_CMD;
++
++/*
++ * Our ioctl commands
++ */
++
++#define SENSE1_IOCTL_TICKLE _IO( SENSE1_IOCTL_MAGIC, SENSE1_CMD_TICKLE ) // arg is int
++#define SENSE1_IOCTL_INCR _IOWR( SENSE1_IOCTL_MAGIC, SENSE1_CMD_INCR, int ) // arg is int
++
++/*
++ * The following are for entries in /proc/sys/sense1
++ */
++#define CTL_CHUMSENSE1 0x4348554E /* 'CHUO' in hex form */
++
++enum
++{
++ CTL_CHUMSENSE1_DEBUG_TRACE = 101,
++ CTL_CHUMSENSE1_DEBUG_IOCTL = 102,
++ CTL_CHUMSENSE1_DEBUG_ERROR = 103,
++ CTL_CHUMSENSE1_DIMLEVEL = 105,
++ CTL_CHUMSENSE1_HPIN = 106,
++ CTL_CHUMSENSE1_DEBOUNCE = 107,
++ CTL_CHUMSENSE1_SPKMUTE = 111,
++ CTL_CHUMSENSE1_DCPOWER = 113,
++ CTL_CHUMSENSE1_BTPOWER = 114,
++ CTL_CHUMSENSE1_DEBUG_CLEAR = 116,
++ CTL_CHUMSENSE1_RESETSERIAL = 117,
++};
++
++// data structures
++struct sense1_data {
++ unsigned int dummy;
++};
++
+--- /dev/null
++++ b/drivers/char/chumby_timer.c
+@@ -0,0 +1,273 @@
++/*
++ chumby_timerx.c
++ bunnie -- August 2006 -- 1.0 -- linux 2.4.20
++ bunnie -- April 2007 -- 2.0 -- port to Ironforge linux 2.6.16
++
++ This file is part of the chumby sensor suite driver in the linux kernel.
++ Copyright (c) Chumby Industries, 2007
++
++ The sensor suite driver is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ The sensor suite driver 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 General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with the Chumby; if not, write to the Free Software
++ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++*/
++
++#define TIMERX_VERSION "2.0-Ironforge"
++
++#include <linux/config.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++
++#include <linux/kernel.h> /* printk() */
++#include <linux/slab.h> /* kmalloc() */
++#include <linux/fs.h> /* everything... */
++#include <linux/errno.h> /* error codes */
++#include <linux/types.h> /* size_t */
++#include <linux/proc_fs.h>
++#include <linux/fcntl.h> /* O_ACCMODE */
++#include <linux/seq_file.h>
++#include <linux/cdev.h>
++
++#include <asm/io.h>
++#include <asm/system.h> /* cli(), *_flags */
++#include <asm/uaccess.h> /* copy_*_user */
++
++#include <linux/miscdevice.h>
++#include <linux/ioport.h>
++#include <linux/poll.h>
++#include <linux/sysctl.h>
++#include <linux/spinlock.h>
++#include <linux/delay.h>
++#include <linux/rtc.h>
++
++#include <linux/timer.h>
++
++#include <asm/arch/imx-regs.h>
++
++#include "chumby_timer.h"
++
++/*
++ * basic parameters
++ */
++
++int timerx_major = 0; // dynamic allocation
++int timerx_minor = 0;
++int timerx_nr_devs = 1;
++
++module_param(timerx_major, int, S_IRUGO);
++module_param(timerx_minor, int, S_IRUGO);
++module_param(timerx_nr_devs, int, S_IRUGO);
++
++MODULE_AUTHOR("bunnie@chumby.com");
++MODULE_LICENSE("GPL");
++
++// static data
++static int gDone = 0;
++static unsigned long timerx_status = 0; /* bitmapped status byte. */
++
++/*
++ * Timerx sensor data logs, tracked by tasks that are scheduled by the
++ * task scheduler
++ */
++
++#define FIXEDPOINT_NORM 1000
++
++struct timerxdata {
++ struct cdev *timerx_cdev;
++} timerxtask_data;
++
++#define USE_SYSCTL 1
++
++#define TIMERX_BLOCKING 0 // turn off blocking read on timerx sensor
++
++#if 1
++# if USE_SYSCTL
++# define CHUMTIMERX_DEBUG( flag, fmt, args... ) do { if ( gDebug ## flag ) printk( "%s: " fmt, __FUNCTION__ , ## args ); } while (0)
++# else
++# define CHUMTIMERX_DEBUG( flag, fmt, args... ) printk( "%s: " fmt, __FUNCTION__ , ## args )
++# endif
++#else
++# define CHUMTIMERX_DEBUG( flag, fmt, args... )
++#endif
++
++// function protos
++static int chumby_timerx_open(struct inode *inode, struct file *file);
++static int chumby_timerx_release(struct inode *inode, struct file *file);
++static int timerx_read_proc(char *page, char **start, off_t off,
++ int count, int *eof, void *data);
++static ssize_t chumby_timerx_read(struct file *file, char *buf,
++ size_t count, loff_t *ppos);
++
++#if USE_SYSCTL
++
++// proc debug structure
++static int gDebugTrace = 0;
++static int gDebugIoctl = 0;
++static int gDebugError = 1;
++
++// create /proc/sys/debug-trace, etc...which can be written to set behavior of globals
++// in this driver on the fly
++static struct ctl_table_header *gSysCtlHeader;
++static struct ctl_table gSysCtlChumtimerx[] =
++{
++ { CTL_CHUMTIMERX_DEBUG_TRACE, "dbg-trace", &gDebugTrace, sizeof( int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMTIMERX_DEBUG_IOCTL, "dbg-ioctl", &gDebugIoctl, sizeof( int ), 0644, NULL, &proc_dointvec },
++ { CTL_CHUMTIMERX_DEBUG_ERROR, "dbg-error", &gDebugError, sizeof( int ), 0644, NULL, &proc_dointvec },
++ { 0 }
++};
++
++static struct ctl_table gSysCtl[] =
++{
++ { CTL_CHUMTIMERX, "timerx", NULL, 0, 0555, gSysCtlChumtimerx },
++ { 0 }
++};
++#endif // USE_SYSCTL
++
++// map into the generic driver infrastructure
++static struct file_operations timerx_fops = {
++ owner: THIS_MODULE,
++ // llseek: chumby_timerx_llseek,
++ read: chumby_timerx_read,
++ // poll: chumby_timerx_poll,
++ // ioctl: chumby_timerx_ioctl,
++ open: chumby_timerx_open,
++ release: chumby_timerx_release,
++ // write: chumby_timerx_write,
++ // fasync: chubmy_timerx_fasync,
++};
++
++///////////// code /////////////
++
++static int chumby_timerx_release(struct inode *inode, struct file *file) {
++ CHUMTIMERX_DEBUG( Trace, "Top of release.\n" );
++ timerx_status &= ~TIMERX_IS_OPEN;
++ return 0;
++}
++
++static int timerx_proc_output (char *buf) {
++ int printlen = 0;
++
++ // insert proc debugging output here
++ printlen = sprintf(buf, "Chumby timerx driver version %s (bunnie@chumby.com)\nThe current time is: %08lX\n", TIMERX_VERSION, jiffies );
++
++ return(printlen);
++}
++
++static int timerx_read_proc(char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ int len = timerx_proc_output (page);
++ if (len <= off+count) *eof = 1;
++ *start = page + off;
++ len -= off;
++ if (len>count) len = count;
++ if (len<0) len = 0;
++ return len;
++}
++
++static int chumby_timerx_open(struct inode *inode, struct file *file) {
++ // make sure we're not opened twice
++ if( timerx_status & TIMERX_IS_OPEN)
++ return -EBUSY;
++
++ timerx_status |= TIMERX_IS_OPEN;
++ return(0);
++}
++
++
++static int __init chumby_timerx_init(void) {
++ dev_t dev = 0;
++ int result, err;
++
++ timerxtask_data.timerx_cdev = cdev_alloc();
++
++ // insert all device specific hardware initializations here
++ printk( "Chumby timerx driver version %s initializing (bunnie@chumby.com)...show me your jiffies!!!\n", TIMERX_VERSION );
++
++ /*
++ * Get a range of minor numbers to work with, asking for a dynamic
++ * major unless directed otherwise at load time.
++ */
++ if (timerx_major) {
++ dev = MKDEV(timerx_major, timerx_minor);
++ result = register_chrdev_region(dev, timerx_nr_devs, "timerx");
++ } else {
++ result = alloc_chrdev_region(&dev, timerx_minor, timerx_nr_devs,
++ "timerx");
++ timerx_major = MAJOR(dev);
++ }
++ if (result < 0) {
++ printk(KERN_WARNING "timerx: can't get major %d\n", timerx_major);
++ return result;
++ }
++
++ create_proc_read_entry ("timerx", 0, 0, timerx_read_proc, NULL);
++
++ cdev_init(timerxtask_data.timerx_cdev, &timerx_fops);
++ timerxtask_data.timerx_cdev->owner = THIS_MODULE;
++ timerxtask_data.timerx_cdev->ops = &timerx_fops;
++ err = cdev_add (timerxtask_data.timerx_cdev, dev, 1);
++ /* Fail gracefully if need be */
++ if (err)
++ printk(KERN_NOTICE "Error %d adding timerx device\n", err);
++
++#if USE_SYSCTL
++ gSysCtlHeader = register_sysctl_table( gSysCtl, 0 );
++ if ( gSysCtlHeader != NULL ) {
++ gSysCtlHeader->ctl_table->child->de->owner = THIS_MODULE;
++ }
++#endif
++
++ return (0);
++}
++
++static ssize_t chumby_timerx_read(struct file *file, char *buf,
++ size_t count, loff_t *ppos) {
++ unsigned long retval;
++ size_t retlen = 0;
++
++ CHUMTIMERX_DEBUG( Trace, "Top of read.\n" );
++
++ retval = jiffies;
++ retlen = sizeof(unsigned long);
++ copy_to_user(buf,&retval,retlen);
++
++ return(retlen);
++}
++
++static void __exit chumby_timerx_exit(void) {
++ dev_t devno = MKDEV(timerx_major, timerx_minor);
++
++ CHUMTIMERX_DEBUG( Trace, "Top of exit.\n" );
++ // set global flag that we're out of here and force a call to remove ourselves
++ gDone = 1;
++
++ mdelay(200); // wait to dequeue self; if kernel panics on rmmod try adding 100 to this value
++
++ // insert all cleanup stuff here
++ cdev_del( timerxtask_data.timerx_cdev );
++ remove_proc_entry( "timerx", NULL );
++ unregister_chrdev_region(devno, timerx_nr_devs);
++
++#if USE_SYSCTL
++ if ( gSysCtlHeader != NULL ) {
++ unregister_sysctl_table( gSysCtlHeader );
++ }
++#endif
++
++}
++
++// entry and exit mappings
++module_init(chumby_timerx_init);
++module_exit(chumby_timerx_exit);
++
+--- /dev/null
++++ b/drivers/char/chumby_timer.h
+@@ -0,0 +1,49 @@
++/*
++ chumby_timerx.h
++ bunnie -- June 2006 -- 1.4 -- linux 2.4.20
++ bunnie -- March 2007 -- 2.0 -- port to Ironforge linux 2.6.16
++
++ This file is part of the chumby sensor suite driver in the linux kernel.
++ Copyright (c) Chumby Industries, 2007
++
++ The sensor suite driver is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ The sensor suite driver 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 General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with the Chumby; if not, write to the Free Software
++ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++*/
++
++#include <linux/ioctl.h>
++
++
++////// local macros; eventually migrate to custom .h file ////
++#define TIMERX_IS_OPEN 0x1
++
++// device numbers are allocated dynamically
++// see /proc/devices
++
++/*
++ * The following are for entries in /proc/sys/timerx
++ */
++#define CTL_CHUMTIMERX 0x43485551 /* 'CHUQ' in hex form */
++
++enum
++{
++ CTL_CHUMTIMERX_DEBUG_TRACE = 101,
++ CTL_CHUMTIMERX_DEBUG_IOCTL = 102,
++ CTL_CHUMTIMERX_DEBUG_ERROR = 103,
++};
++
++// data structures
++struct timerx_data {
++ unsigned long jiffies;
++};
++
diff --git a/series b/series
index f1095a2f1fe2c0..76f98eb3cd210c 100644
--- a/series
+++ b/series
@@ -138,3 +138,4 @@ usb/always-announce-new-usb-devices.patch
# work in progress goes here...
#
+chumby_drivers.patch