diff options
Diffstat (limited to 'packages/robostix-module/files/robostix.c')
-rw-r--r-- | packages/robostix-module/files/robostix.c | 1011 |
1 files changed, 1011 insertions, 0 deletions
diff --git a/packages/robostix-module/files/robostix.c b/packages/robostix-module/files/robostix.c new file mode 100644 index 0000000000..1b3adac577 --- /dev/null +++ b/packages/robostix-module/files/robostix.c @@ -0,0 +1,1011 @@ +/**************************************************************************** +* +* Copyright (c) 2006 Dave Hylands <dhylands@gmail.com> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* Alternatively, this software may be distributed under the terms of BSD +* license. +* +* See README and COPYING for more details. +* +****************************************************************************/ +/** +* +* robostix.c +* +* PURPOSE: +* +* This implements a driver for using the robostix from the gumstix +* +* Initially, this contains the required support to emulate enough of the +* parallel port interface to allow avrdude to program the ATMega128. +* +*****************************************************************************/ + +/* ---- Include Files ---------------------------------------------------- */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/fs.h> + +#include <linux/parport.h> +#include <linux/ppdev.h> +#include <linux/sysctl.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/version.h> + +#include <asm/delay.h> +#include <asm/uaccess.h> + +#include <asm/arch/hardware.h> +#include <asm/arch/pxa-regs.h> + +#include "robostix.h" + +/**************************************************************************** +* +* This driver assumes that the robostix uses the following GPIO pins: +* +* Robostix gumstix ATmega +* Symbol GPIO Dir Symbol Description +* ---------- ---- --- -------------- ------------------------------------- +* IR_RXD 46 in ATM_TX0 PE1 Acts as PDO or MISO for programming +* IR_TXD 47 out ATM_RX0 PE0 Acts as PDI or MOSI for programming +* +* L_DD11 69 in ATM_IRQ PE2 IRQ from ATmega128 to gumstix +* L_DD12 70 out Enable for Vcc5 and AVCC for the ATMega +* L_DD14 72 out Active low enable for the 245's +* L_DD15 73 out ATM_RESET Resets the processor +* +* The following shows the mapping of the SPI port for the gumstix: +* +* NSSPCLK 81 out ATM_SCK Acts as SCK for SPI use +* NSSPFRAME 82 out ATM_SS Acts as SS for SPI use +* X_MOSI 83 out ATM_MOSI Acts as MOSI for SPI use +* X_MISO 84 in ATM_MISO Acts as MISO for SPI use +* +* On the verdex, the mapping for the SPI port is slightly different: +* +* SSPCLK2 19 out ATM_SCK Acts as SCK for SPI use +* SSPSFRM2 14 out ATM_SS Acts as SS for SPI use +* SSPTXD2 13 out ATM_MOSI Acts as MOSI for SPI use +* SSPRXD2 11 in ATM_MISO Acts as MISO for SPI use +* +* X_SCL - i/o ATM_SCL i2c clock +* X_SDA - i/o ATM_SDA i2c data +* +*****************************************************************************/ + +//--------------------------------------------------------------------------- +// +// This was extracted from avrdude, and it gives the pin configuration for +// AVR Dude's default programmer. UISP calls it "BSD" +// This is what I've chosen for this driver to implement. + +#if 0 +programmer + id = "bsd"; + desc = "Brian Dean's Programmer, http://www.bsdhome.com/avrdude/"; + type = par; + vcc = 2, 3, 4, 5; + reset = 7; + sck = 8; + mosi = 9; + miso = 10; +; +#endif +//--------------------------------------------------------------------------- +// +// The following was extracted from avrdude. It basically gives the pin +// number to register mapping that is in effect for the parallel port. +// +// I added the comments on the right which shows pin usage for the default +// programmer. +// +// From this, we can glean that the Control register is never used, and none +// of the signals which are used are inverted. +// +// Furthermore, all of the Data accesses are writes and all of the Status +// accesses are reads. + +#if 0 +struct ppipins_t { + int pin; + int reg; + int bit; + int inverted; +}; + +static struct ppipins_t pins[] = { + { 1, PPICTRL, 0x01, 1 }, + { 2, PPIDATA, 0x01, 0 }, // Vcc + { 3, PPIDATA, 0x02, 0 }, // Vcc + { 4, PPIDATA, 0x04, 0 }, // Vcc + { 5, PPIDATA, 0x08, 0 }, // Vcc + { 6, PPIDATA, 0x10, 0 }, + { 7, PPIDATA, 0x20, 0 }, // Reset + { 8, PPIDATA, 0x40, 0 }, // SCK + { 9, PPIDATA, 0x80, 0 }, // MOSI + { 10, PPISTATUS, 0x40, 0 }, // MISO + { 11, PPISTATUS, 0x80, 1 }, + { 12, PPISTATUS, 0x20, 0 }, + { 13, PPISTATUS, 0x10, 0 }, + { 14, PPICTRL, 0x02, 1 }, + { 15, PPISTATUS, 0x08, 0 }, + { 16, PPICTRL, 0x04, 0 }, + { 17, PPICTRL, 0x08, 1 } +}; + +#endif + +#define PPI_DATA_VCC_MASK ( 0x01 | 0x02 | 0x04 | 0x08 ) +#define PPI_DATA_RESET_MASK ( 0x20 ) +#define PPI_DATA_SCK_MASK ( 0x40 ) +#define PPI_DATA_MOSI_MASK ( 0x80 ) + +#define PPI_STATUS_MISO_MASK ( 0x40 ) + +/* ---- Public Variables ------------------------------------------------- */ +/* ---- Private Constants and Types -------------------------------------- */ + +#define USE_SYSCTL 1 + +#if 1 +# if USE_SYSCTL +# define ROBO_DEBUG( flag, fmt, args... ) do { if ( gDebug ## flag ) printk( "%s: " fmt, __FUNCTION__ , ## args ); } while (0) +# else +# define ROBO_DEBUG( flag, fmt, args... ) printk( "%s: " fmt, __FUNCTION__ , ## args ) +# endif +#else +# define ROBO_DEBUG( flag, fmt, args... ) +#endif + +#define SET_GPIO( pin, val ) do { if ( val ) { GPSR( pin ) = GPIO_bit( pin ); } else { GPCR( pin ) = GPIO_bit( pin ); }} while(0) +#define GET_GPIO( pin ) (( GPLR( pin ) & GPIO_bit( pin )) != 0 ) + +// The Alternate function register is 2 bits per pin, so we can't use the +// GPIO_bit macro. + +#define GPIO_AF_shift(x) (((x) & 0x0F ) << 1 ) +#define GPIO_AF_mask(x) ( 3 << GPIO_AF_shift( x )) + +/* + * Define the mappings between various GPIO pins and functions on the robostix + * board. + */ + +#define ROBOSTIX_GPIO_ATM_IRQ GPIO69_LDD_11 +#define ROBOSTIX_GPIO_VCC5_ENABLE GPIO70_LDD_12 +#define ROBOSTIX_GPIO_245_ENABLE GPIO72_LDD_14 +#define ROBOSTIX_GPIO_ATM_RESET GPIO73_LDD_15 + +#ifdef CONFIG_PXA27x +# define ROBOSTIX_GPIO_ATM_SCK ( 19 | GPIO_ALT_FN_1_OUT ) +# define ROBOSTIX_GPIO_ATM_SS ( 14 | GPIO_ALT_FN_2_OUT ) +# define ROBOSTIX_GPIO_ATM_MOSI ( 13 | GPIO_ALT_FN_1_OUT ) +# define ROBOSTIX_GPIO_ATM_MISO ( 11 | GPIO_ALT_FN_2_IN ) +#else +# define ROBOSTIX_GPIO_ATM_SCK GPIO81_NSCLK +# define ROBOSTIX_GPIO_ATM_SS GPIO82_NSFRM +# define ROBOSTIX_GPIO_ATM_MOSI GPIO83_NSTXD +# define ROBOSTIX_GPIO_ATM_MISO GPIO84_NSRXD +#endif + +#define ROBOSTIX_GPIO_IR_RXD_5V GPIO46_STRXD +#define ROBOSTIX_GPIO_IR_TXD_5V GPIO47_STTXD + +// Since IR TxD/RxD behave like MOSI/MISO during programming, we define a +// couple of aliases + +#define ROBOSTIX_GPIO_ATM_PGM_MOSI ROBOSTIX_GPIO_IR_TXD_5V +#define ROBOSTIX_GPIO_ATM_PGM_MISO ROBOSTIX_GPIO_IR_RXD_5V + + +typedef enum +{ + RoboStixGpioIn, + RoboStixGpioOut, +} PinMode_e; + +typedef struct +{ + unsigned grer; + unsigned gfer; + unsigned gafr; + unsigned gpdr; + unsigned gplr; + +} PinConfig_t; + +/* ---- Private Variables ------------------------------------------------ */ + +#define ROBOSTIX_DEV_NAME "robostix" + +static char gBanner[] __initdata = KERN_INFO "Robostix Driver Compiled: " __DATE__ " at " __TIME__ "\n"; + +static PinConfig_t gIrTxdConfig; +static PinConfig_t gIrRxdConfig; + +dev_t gRobostixDevNum; +struct cdev gRobostixCDev; +struct class *gRobostixClass; + +#if USE_SYSCTL + +static int gDebugTrace = 0; +static int gDebugIoctl = 0; +static int gDebugError = 1; + +static struct ctl_table_header *gSysCtlHeader; + +static struct ctl_table gSysCtlRobostix[] = +{ + { + .ctl_name = CTL_ROBOSTIX_DEBUG_TRACE, + .procname = "debug-trace", + .data = &gDebugTrace, + .maxlen = sizeof( int ), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { + .ctl_name = CTL_ROBOSTIX_DEBUG_IOCTL, + .procname = "debug-ioctl", + .data = &gDebugIoctl, + .maxlen = sizeof( int ), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { + .ctl_name = CTL_ROBOSTIX_DEBUG_ERROR, + .procname = "debug-error", + .data = &gDebugError, + .maxlen = sizeof( int ), + .mode = 0644, + .proc_handler = &proc_dointvec + }, + { 0 } +}; + +static struct ctl_table gSysCtl[] = +{ + { + .ctl_name = CTL_ROBOSTIX, + .procname = "robostix", + .mode = 0555, + .child = gSysCtlRobostix + }, + { 0 } +}; + +#endif // USE_SYSCTL + +/* ---- Private Function Prototypes -------------------------------------- */ + +static void robostix_configure_pin( int pin, PinMode_e pinMode ); +static void robostix_get_pin_config( int pin, PinConfig_t *pinConfig ); +static void robostix_set_pin_config( int pin, const PinConfig_t *pinConfig ); + +static void robostix_exit( void ); +static int robostix_init( void ); +static int robostix_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg ); +static int robostix_open( struct inode *inode, struct file *file ); +static int robostix_release( struct inode *inode, struct file *file ); + +/**************************************************************************** +* +* File Operations (these are the device driver entry points) +* +*****************************************************************************/ + +static struct file_operations robostix_fops = +{ + owner: THIS_MODULE, + ioctl: robostix_ioctl, + open: robostix_open, + release: robostix_release, +}; + +/* ---- Functions -------------------------------------------------------- */ + +/**************************************************************************** +* +* robostix_configure_pin +* +* Configures a GPIO pin for use with the RoboStix. +* +*****************************************************************************/ + +void robostix_configure_pin( int pin, PinMode_e pinMode ) +{ + // Make sure that interrupts on rising/falling edges are turned off. This + // is a bit paranoid, but might as well be sure. + + GRER( pin ) &= ~GPIO_bit( pin ); + GFER( pin ) &= ~GPIO_bit( pin ); + + // Set the pin to be a GPIO pin + + GAFR( pin ) &= ~GPIO_AF_mask( pin ); // AF = 0 is GPIO + + // Reprogram the direction of the pin. + + if ( pinMode == RoboStixGpioIn ) + { + GPDR( pin ) &= ~GPIO_bit( pin ); // in + } + else + { + GPDR( pin ) |= GPIO_bit( pin ); // out + } + +} // robostix_configure_pin + +/**************************************************************************** +* +* robostix_get_pin_config +* +* Retrieves the current pin configuration and stores it in @a pinConfig. +* +*****************************************************************************/ + +void robostix_get_pin_config( int pin, PinConfig_t *pinConfig ) +{ + pinConfig->grer = GRER( pin ) & GPIO_bit( pin ); + pinConfig->gfer = GFER( pin ) & GPIO_bit( pin ); + pinConfig->gafr = GAFR( pin ) & GPIO_AF_mask( pin ); + pinConfig->gpdr = GPDR( pin ) & GPIO_bit( pin ); + pinConfig->gplr = GPLR( pin ) & GPIO_bit( pin ); + +} // robostix_get_pin_config + +/**************************************************************************** +* +* robostix_set_pin_config +* +* Restores the pin configuration to a previously saved comfiguration. +* +*****************************************************************************/ + +void robostix_set_pin_config( int pin, const PinConfig_t *pinConfig ) +{ + GRER( pin ) = ( GRER( pin ) & ~GPIO_bit( pin )) | ( pinConfig->grer & GPIO_bit( pin )); + GFER( pin ) = ( GFER( pin ) & ~GPIO_bit( pin )) | ( pinConfig->gfer & GPIO_bit( pin )); + GPDR( pin ) = ( GPDR( pin ) & ~GPIO_bit( pin )) | ( pinConfig->gpdr & GPIO_bit( pin )); + GAFR( pin ) = ( GAFR( pin ) & ~GPIO_AF_mask( pin )) | ( pinConfig->gafr & GPIO_AF_mask( pin )); + + if (( pinConfig->gplr & GPIO_bit( pin )) == 0 ) + { + GPSR( pin ) |= GPIO_bit( pin ); + } + else + { + GPCR( pin ) |= GPIO_bit( pin ); + } + +} // robostix_set_pin_config + +/**************************************************************************** +* +* robostix_exit +* +* Called to perform module cleanup when the module is unloaded. +* +*****************************************************************************/ + +void robostix_exit( void ) +{ + ROBO_DEBUG( Trace, "called\n" ); + + class_device_destroy( gRobostixClass, gRobostixDevNum ); + class_destroy( gRobostixClass ); + + cdev_del( &gRobostixCDev ); + +#if USE_SYSCTL + if ( gSysCtlHeader != NULL ) + { + unregister_sysctl_table( gSysCtlHeader ); + } +#endif + + unregister_chrdev_region( gRobostixDevNum, 1 ); + +#if 0 + unregister_chrdev( ROBOSTIX_MAJOR, ROBOSTIX_DEV_NAME ); +#endif + +} // robostix_exit + +/**************************************************************************** +* +* robostix_init +* +* Called to perform module initialization when the module is loaded. +* +*****************************************************************************/ + +int __init robostix_init( void ) +{ + int rc; + + ROBO_DEBUG( Trace, "called\n" ); + + printk( gBanner); + +#if 0 + // Register our device with Linux + + if (( rc = register_chrdev( ROBOSTIX_MAJOR, ROBOSTIX_DEV_NAME, &robostix_fops )) < 0 ) + { + printk( KERN_WARNING "robostix: register_chrdev failed for major %d\n", ROBOSTIX_MAJOR ); + return rc; + } +#endif + + if (( rc = alloc_chrdev_region( &gRobostixDevNum, 0, 1, ROBOSTIX_DEV_NAME )) < 0 ) + { + printk( KERN_WARNING "robostix: Unable to allocate major, err: %d\n", rc ); + return rc; + } + +#if USE_SYSCTL + #if ( LINUX_VERSION_CODE <= KERNEL_VERSION( 2, 6, 20 )) + gSysCtlHeader = register_sysctl_table( gSysCtl, 0 ); + if ( gSysCtlHeader != NULL ) + { + gSysCtlHeader->ctl_table->child->de->owner = THIS_MODULE; + } + #else + gSysCtlHeader = register_sysctl_table( gSysCtl ); + #endif +#endif + + // Initialize the various GPIO pins that control the Robostix. + // + // IR_RXD 46 in ATM_TX0 PE1 Acts as PDO or MISO for programming + // IR_TXD 47 out ATM_RX0 PE0 Acts as PDI or MOSI for programming + // + // L_DD11 69 in ATM_IRQ PE2 IRQ from ATmega128 to gumstix + // L_DD12 70 out Enable for Vcc5 and AVCC for the ATMega + // L_DD14 72 out Active low enable for the 245's + // L_DD15 73 out ATM_RESET Resets the processor + // + // NSSPCLK 81 out ATM_SCK Acts as SCK for SPI use + // NSSPFRAME 82 out ATM_SS Acts as SS for SPI use + // X_MOSI 83 out ATM_MOSI Acts as MOSI for SPI use + // X_MISO 84 in ATM_MISO Acts as MISO for SPI use + // + // For now, we initialize things so that they continue on the way that + // they were when the gumstix boots: + // + // 245 is enabled + // voltage regulators are enabled + // Robostix is held in Reset + // + // I'd like to either see the voltage regulators in a disabled state + // or see the robostix not be held in reset so that we don't have sensors + // and motors and stuff going wild while the gumstix boots. + // + // The gumstix console runs through the '245 so we make sure to leave + // it enabled. + + // The first thing to do is configure the input pins. + + robostix_configure_pin( ROBOSTIX_GPIO_ATM_MISO, RoboStixGpioIn ); + + // TODO: Set ATM_IRQ line to generate an interrupt + +// printk( "IRQ\n" ); +// robostix_configure_pin( ROBOSTIX_GPIO_ATM_IRQ, RoboStixGpioIn ); + + // Configure the output pins. We set the GPIO value register before + // setting configuring it as a GPIO so that we don't create a glitch. + + SET_GPIO( ROBOSTIX_GPIO_VCC5_ENABLE, 1 ); // Voltage regulator on (active high) + robostix_configure_pin( ROBOSTIX_GPIO_VCC5_ENABLE, RoboStixGpioOut ); + + SET_GPIO( ROBOSTIX_GPIO_ATM_RESET, 0 ); // AVR held in Reset (active low) + robostix_configure_pin( ROBOSTIX_GPIO_ATM_RESET, RoboStixGpioOut ); + + // The values of these pins don't really matter. + + robostix_configure_pin( ROBOSTIX_GPIO_ATM_SCK, RoboStixGpioOut ); + robostix_configure_pin( ROBOSTIX_GPIO_ATM_SS, RoboStixGpioOut ); + robostix_configure_pin( ROBOSTIX_GPIO_ATM_MOSI, RoboStixGpioOut ); + + // Finally enable the '245 + + SET_GPIO( ROBOSTIX_GPIO_245_ENABLE, 0 ); // '245 enabled (active low) + robostix_configure_pin( ROBOSTIX_GPIO_245_ENABLE, RoboStixGpioOut ); + + // Don't change the configuration of the IR TxD/RxD here. Instead we + // save/restore using the PPCLAIM/PPRELEASE which brackets when avrdude + // is doing the actual programming. + + // TODO: Probably configure IR TxD as UART + + // Register our device. The device becomes "active" as soon as cdev_add + // is called. + + cdev_init( &gRobostixCDev, &robostix_fops ); + gRobostixCDev.owner = THIS_MODULE; + + if (( rc = cdev_add( &gRobostixCDev, gRobostixDevNum, 1 )) != 0 ) + { + printk( KERN_WARNING "robostix: cdev_add failed: %d\n", rc ); + return rc; + } + + gRobostixClass = class_create( THIS_MODULE, ROBOSTIX_DEV_NAME ); + if ( IS_ERR( gRobostixClass )) + { + printk( KERN_WARNING "robostix: Unable to create class\n" ); + return -1; + } + + class_device_create( gRobostixClass, NULL, gRobostixDevNum, NULL, ROBOSTIX_DEV_NAME ); + + return 0; + +} // robostix_init + +/**************************************************************************** +* +* robostix_ioctl +* +* Called to process ioctl requests +* +*****************************************************************************/ + +int robostix_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg ) +{ + int err; + int userVal; + + ROBO_DEBUG( Trace, "type: '%c' cmd: 0x%x\n", _IOC_TYPE( cmd ), _IOC_NR( cmd )); + + if (( _IOC_TYPE( cmd ) != ROBOSTIX_IOCTL_MAGIC ) + || ( _IOC_NR( cmd ) < ROBOSTIX_CMD_FIRST ) + || ( _IOC_NR( cmd ) >= ROBOSTIX_CMD_LAST )) + { + // Since we emulate some of the parallel port commands, we need to allow + // those as well. + + if (( _IOC_TYPE( cmd ) != PP_IOCTL ) + || ( _IOC_NR( cmd ) < 0x80 ) + || ( _IOC_NR( cmd ) >= 0x9b )) + { + 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 ) + { + ROBO_DEBUG( Error, "arg pointer is invalid\n" ); + return -EFAULT; + } + + switch ( cmd ) + { + case ROBOSTIX_IOCTL_POWER_VCC5: + { + ROBO_DEBUG( Ioctl, "Power: %ld\n", arg ); + +#if 1 + // Until I figure something else out, the only way I can get the + // power to really go off is to also turn off the '245. This means + // that we'll lose the console, so you better be turning things + // back on real soon now + + SET_GPIO( ROBOSTIX_GPIO_245_ENABLE, !arg ); // '245 is active low + + // Also - I noticed that the Console connector on the Robostix has + // Vcc5 going to it, which means that if a TTL <=> RS232 converter + // is being powered off the robostix, we'll lose our console as + // soon as the voltage regulator (which generates Vcc5) gets + // turned off. + +#else + // In order to truly power off the robostix, we need to turn off + // the voltage regulator. We assume that the '245 stays on so we + // continue to get our console. This also means that we need to + // take all of the GPIO lines low to eliminate any leak-thru + // current. + + if ( arg ) + { + // Powering on - Configure I/O pins in "typical" manner. + + // TODO: Probably set MOSI/SCK/SS back to NSSP + // TODO: Probably set IR TxD back to UART + } + else + { + // Powering off. Make them all GPIO's so that we can force + // them low. + + SET_GPIO( ROBOSTIX_GPIO_ATM_RESET, 0 ); + SET_GPIO( ROBOSTIX_GPIO_ATM_MOSI, 0 ); + SET_GPIO( ROBOSTIX_GPIO_ATM_SCK, 0 ); + SET_GPIO( ROBOSTIX_GPIO_ATM_SS, 0 ); + SET_GPIO( ROBOSTIX_GPIO_IR_TXD_5V, 0 ); + + robostix_configure_pin( ROBOSTIX_GPIO_ATM_RESET, RoboStixGpioOut ); + robostix_configure_pin( ROBOSTIX_GPIO_ATM_MOSI, RoboStixGpioOut ); + robostix_configure_pin( ROBOSTIX_GPIO_ATM_SCK, RoboStixGpioOut ); + robostix_configure_pin( ROBOSTIX_GPIO_ATM_SS, RoboStixGpioOut ); + robostix_configure_pin( ROBOSTIX_GPIO_IR_TXD_5V, RoboStixGpioOut ); + + // Grr - ATM_SCL and ATM_SDA are both pulled up to V_BATT, + // so we probably need to make these go low too, which effectively + // means that we lose the i2c bus. + } +#endif + + SET_GPIO( ROBOSTIX_GPIO_VCC5_ENABLE, arg ); // Voltage regulator is active high + break; + } + + case ROBOSTIX_IOCTL_RESET: + { + if ( arg == ROBOSTIX_PIN_PULSE ) + { + // The ATMega128 datasheet says that the reset pulse needs + // to have a minimum pulse width of 1.5 usec. + + ROBO_DEBUG( Ioctl, "Reset: %ld (pulse)\n", arg ); + + SET_GPIO( ROBOSTIX_GPIO_ATM_RESET, 0 ); // Reset is active low + udelay( 3 ); + SET_GPIO( ROBOSTIX_GPIO_ATM_RESET, 1 ); + } + else + { + // Reset is active low, so "on" means low + + ROBO_DEBUG( Ioctl, "Reset: %ld\n", arg ); + + SET_GPIO( ROBOSTIX_GPIO_ATM_RESET, !arg ); + } + break; + } + + case ROBOSTIX_IOCTL_245_ENABLE: + { + // The 245 is active low, so we invert the sense of on/off + + ROBO_DEBUG( Ioctl, "245 Enable: %ld\n", arg ); + + if ( arg != 0 ) + { + printk( KERN_WARNING "Robostix: Warning turning '245 off - console may become inactive\n" ); + + // Allow some time for the above warning to get printed on the + // console before we turn it off. + + set_current_state( TASK_INTERRUPTIBLE ); + schedule_timeout( 2 ); + } + + SET_GPIO( ROBOSTIX_GPIO_245_ENABLE, !arg ); + break; + } + + case ROBOSTIX_IOCTL_SET_SCK: // out + { + ROBO_DEBUG( Ioctl, "Set SCK: %ld\n", arg ); + + SET_GPIO( ROBOSTIX_GPIO_ATM_SCK, arg ); + break; + } + + case ROBOSTIX_IOCTL_SET_SS: // out + { + ROBO_DEBUG( Ioctl, "Set SS: %ld\n", arg ); + + SET_GPIO( ROBOSTIX_GPIO_ATM_SS, arg ); + break; + } + + case ROBOSTIX_IOCTL_SET_IR_TXD: // out + { + // This particular ioctl should only ever be called as part of + // somebody testing something. We assume that they'll be smart + // enough to reconfigure when they're done. + + ROBO_DEBUG( Ioctl, "Set IR TxD: %ld\n", arg ); + + robostix_configure_pin( ROBOSTIX_GPIO_IR_TXD_5V, RoboStixGpioOut ); + + SET_GPIO( ROBOSTIX_GPIO_IR_TXD_5V, arg ); + break; + } + + case ROBOSTIX_IOCTL_GET_IR_RXD: // in + { + // This particular ioctl should only ever be called as part of + // somebody testing something. We assume that they'll be smart + // enough to reconfigure when they're done. + + robostix_configure_pin( ROBOSTIX_GPIO_IR_RXD_5V, RoboStixGpioIn ); + + userVal = GET_GPIO( ROBOSTIX_GPIO_IR_RXD_5V ); + if ( copy_to_user( (int *)arg, &userVal, sizeof( userVal )) != 0 ) + { + return -EFAULT; + } + + ROBO_DEBUG( Ioctl, "Get IR RxD: %d\n", userVal ); + break; + } + + case ROBOSTIX_IOCTL_SET_MOSI: // out + { + ROBO_DEBUG( Ioctl, "Set MOSI: %ld\n", arg ); + + SET_GPIO( ROBOSTIX_GPIO_ATM_MOSI, arg ); + break; + } + + case ROBOSTIX_IOCTL_GET_MISO: // in + { + userVal = GET_GPIO( ROBOSTIX_GPIO_ATM_MISO ); + if ( copy_to_user( (int *)arg, &userVal, sizeof( userVal )) != 0 ) + { + return -EFAULT; + } + ROBO_DEBUG( Ioctl, "Get MISO: %d\n", userVal ); + break; + } + + case ROBOSTIX_IOCTL_GET_IRQ: // in + { + userVal = GET_GPIO( ROBOSTIX_GPIO_ATM_IRQ ); + if ( copy_to_user( (int *)arg, &userVal, sizeof( userVal )) != 0 ) + { + return -EFAULT; + } + ROBO_DEBUG( Ioctl, "Get IRQ: %d\n", userVal ); + break; + } + + case ROBOSTIX_IOCTL_DELAY_USEC: + { + ROBO_DEBUG( Ioctl, "Delay: %ld usecs\n", arg ); + + udelay( arg ); + break; + } + + //------------------------------------------------------------------- + // + // Parallel port interface. Some documentation on these ioctls can + // be found here: + // http://www.kernelnewbies.org/documents/kdoc/parportbook/x623.html + // + + case PPRSTATUS: // Read status register + { + unsigned char statusReg = 0; + int miso; + + // The only thing mapped into the status register, is MISO. + + miso = GET_GPIO( ROBOSTIX_GPIO_ATM_PGM_MISO ); + + + if ( miso ) + { + statusReg |= PPI_STATUS_MISO_MASK; + } + + ROBO_DEBUG( Ioctl, "PPRSTATUS: 0x%02x miso:%d\n", statusReg, miso ); + + if ( copy_to_user( (unsigned char *)arg, &statusReg, sizeof( statusReg )) != 0 ) + { + return -EFAULT; + } + break; + } + + case PPRCONTROL: // Read control register + { + // Called once to initialize avrdude's shadow registers + + unsigned char controlReg = 0; + + ROBO_DEBUG( Ioctl, "PPRCONTROL: 0x%02x\n", controlReg ); + + if ( copy_to_user( (unsigned char *)arg, &controlReg, sizeof( controlReg )) != 0 ) + { + return -EFAULT; + } + break; + } + + case PPWCONTROL: // Write control register + { + unsigned char controlReg = 0; + + if ( copy_from_user( &controlReg, (unsigned char *)arg, sizeof( controlReg )) != 0 ) + { + return -EFAULT; + } + + ROBO_DEBUG( Ioctl, "PPWCONTROL: 0x%02x\n", controlReg ); + break; + } + + case PPRDATA: // Read data register + { + // Called once to initialize avrdude's shadow registers + + unsigned char dataReg = 0; + int power, sck, reset, mosi; + + power = GET_GPIO( ROBOSTIX_GPIO_VCC5_ENABLE ); + sck = GET_GPIO( ROBOSTIX_GPIO_ATM_SCK ); + reset = GET_GPIO( ROBOSTIX_GPIO_ATM_RESET ); + mosi = GET_GPIO( ROBOSTIX_GPIO_ATM_PGM_MOSI ); + + if ( power ) + { + dataReg |= PPI_DATA_VCC_MASK; + } + if ( reset ) + { + dataReg |= PPI_DATA_RESET_MASK; + } + if ( sck ) + { + dataReg |= PPI_DATA_SCK_MASK; + } + if ( mosi ) + { + dataReg |= PPI_DATA_MOSI_MASK; + } + + ROBO_DEBUG( Ioctl, "PPRDATA: 0x%02x pow:%d reset:%d sck:%d mosi: %d\n", dataReg, power, reset, sck, mosi ); + + if ( copy_to_user( (unsigned char *)arg, &dataReg, sizeof( dataReg )) != 0 ) + { + return -EFAULT; + } + break; + } + + case PPWDATA: // Write data register + { + unsigned char dataReg = 0; + int power, sck, reset, mosi; + + if ( copy_from_user( &dataReg, (unsigned char *)arg, sizeof( dataReg )) != 0 ) + { + return -EFAULT; + } + + power = ( dataReg & PPI_DATA_VCC_MASK ) != 0; + sck = ( dataReg & PPI_DATA_SCK_MASK ) != 0; + reset = ( dataReg & PPI_DATA_RESET_MASK ) != 0; + mosi = ( dataReg & PPI_DATA_MOSI_MASK ) != 0; + + ROBO_DEBUG( Ioctl, "PPWDATA: 0x%02x pow:%d reset:%d sck:%d mosi: %d\n", dataReg, power, reset, sck, mosi ); + + SET_GPIO( ROBOSTIX_GPIO_VCC5_ENABLE, power ); + SET_GPIO( ROBOSTIX_GPIO_245_ENABLE, !power ); // 245 is active low + + SET_GPIO( ROBOSTIX_GPIO_ATM_SCK, sck ); + SET_GPIO( ROBOSTIX_GPIO_ATM_RESET, reset ); + SET_GPIO( ROBOSTIX_GPIO_ATM_PGM_MOSI, mosi ); + break; + } + + case PPCLAIM: // Claim the parallel port + { + ROBO_DEBUG( Ioctl, "PPCLAIM\n" ); + + // We use this opportunity to save away the state of the IR Txd/Rxd lines + // and convert them to GPIO. + + robostix_get_pin_config( ROBOSTIX_GPIO_IR_TXD_5V, &gIrTxdConfig ); + robostix_get_pin_config( ROBOSTIX_GPIO_IR_RXD_5V, &gIrRxdConfig ); + + robostix_configure_pin( ROBOSTIX_GPIO_IR_TXD_5V, RoboStixGpioOut ); + robostix_configure_pin( ROBOSTIX_GPIO_IR_RXD_5V, RoboStixGpioIn ); + break; + } + + case PPRELEASE: // Release the parallel port + { + ROBO_DEBUG( Ioctl, "PPRELEASE\n" ); + + // We use this opportunity to restore the state of the IR Txd/Rxd lines + // back to what they were. + + robostix_set_pin_config( ROBOSTIX_GPIO_IR_TXD_5V, &gIrTxdConfig ); + robostix_set_pin_config( ROBOSTIX_GPIO_IR_RXD_5V, &gIrRxdConfig ); + break; + } + + case PPDATADIR: + { + int dataDirReg; + + if ( copy_from_user( &dataDirReg, (int *)arg, sizeof( dataDirReg )) != 0 ) + { + return -EFAULT; + } + + ROBO_DEBUG( Ioctl, "PPDATADIR: 0x%02x\n", dataDirReg ); + break; + } + + default: + { + ROBO_DEBUG( Error, "Unrecognized ioctl: '0x%x'\n", cmd ); + return -ENOTTY; + } + } + + return 0; + +} // robostix_ioctl + +/**************************************************************************** +* +* robostix_open +* +* Called to process open requests +* +*****************************************************************************/ + +int robostix_open( struct inode *inode, struct file *file ) +{ + ROBO_DEBUG( Trace, "major = %d, minor = %d\n", MAJOR( inode->i_rdev ), MINOR( inode->i_rdev )); + + return 0; + +} // robostix_open + +/**************************************************************************** +* +* robostix_release +* +* Called when the last istance is closed. +* +*****************************************************************************/ + +int robostix_release( struct inode *inode, struct file *file ) +{ + ROBO_DEBUG( Trace, "called\n" ); + + return 0; + +} // robostix_release + +/****************************************************************************/ + +module_init(robostix_init); +module_exit(robostix_exit); + +MODULE_AUTHOR("Dave Hylands"); +MODULE_DESCRIPTION("gumstix/robostix driver"); +MODULE_LICENSE("Dual BSD/GPL"); + |