[linux-cirrus] sorry no binary format allowed
- From: Manfred Gruber <manfred.gruber@xxxxxxxxx>
- To: linux-cirrus@xxxxxxxxxxxxx
- Date: Mon, 8 Nov 2004 21:34:10 +0100
/*
* FILE: ssp.c
*
* DESCRIPTION: SSP Interface Driver Module implementation
*
* Copyright Cirrus Logic Corporation, 2001-2003. All rights reserved
*
* This program 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* This driver provides a way to read and write devices on the SSP
* interface.
*
* For Tx devices, EGPIO7 is used as an address pin:
* I2S Codec CS4228 = EGPIO7 == 1
* Serial Flash AT25F1024 = EGPIO7 == 0
*/
#include <linux/delay.h>
#include <asm/irq.h>
#include <asm/semaphore.h>
#include <asm/uaccess.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <asm/arch/ssp.h>
#undef DEBUG
// #define DEBUG 1
#ifdef DEBUG
#define DPRINTK( x... ) printk( ##x )
#else
#define DPRINTK( x... )
#endif
#define EP93XX_KEY_TIMER_PERIOD_MSEC 20
static int SSP_Open(SSPDeviceType Device, SSPDataCallback Callback);
static int SSP_Close(int Handle);
static int SSP_Read(int Handle, unsigned int Addr, unsigned int *pValue);
static int SSP_Write(int Handle, unsigned int Addr, unsigned int Value);
static int CheckHandle(int Handle);
static void SetSSPtoPS2(void);
static void SetSSPtoI2S(void);
static int ReadIntoBuffer(void);
static int SSP_Write_I2SCodec(int Handle, unsigned int RegAddr,unsigned int
RegValue);
/*
* Key buffer...
*/
#define KEYBUF_SIZE 256
static unsigned int uiKeyBuffer[KEYBUF_SIZE];
static spinlock_t ssp_spinlock = SPIN_LOCK_UNLOCKED;
typedef enum{
SSP_MODE_UNKNOWN = 0,
SSP_MODE_PS2,
SSP_MODE_I2S,
SSP_MODE_FLASH,
} SSPmodes_t;
static struct timer_list g_KbdTimer;
static SSPmodes_t gSSPmode = SSP_MODE_UNKNOWN;
static SSPDataCallback gKeyCallback = NULL;
static int gHookedInterrupt = 0;
/*
* Keep the last valid handle for SSP for kbd, i2s, and flash
*/
static int iLastValidHandle = -1;
static int KeyboardHandle = 0;
static int I2SHandle = 0;
static int FlashHandle = 0;
#define SSP_DEVICE_MASK 0xf0000000
#define SSP_DEVICE_SHIFT 28
SSP_DRIVER_API SSPinstance =
{
SSP_Open,
SSP_Read,
SSP_Write,
SSP_Close,
};
/*
* The only instance of this driver.
*/
SSP_DRIVER_API *SSPDriver = &SSPinstance;
//=============================================================================
// SSPIrqHandler
//=============================================================================
// This routine will get all of the keys out of the SPI FIFO.
//=============================================================================
irqreturn_t SSPIrqHandler( int irq, void *dev_id, struct pt_regs *regs)
{
//
// Get key codes from SSP and send them to the keyboard callback.
//
ReadIntoBuffer();
//
// Clear the interrupt.
//
outl( 0, SSPIIR );
return IRQ_HANDLED;
}
//=============================================================================
// TimerRoutine
//=============================================================================
// This function is called periodically to make sure that no keys are stuck in
// the SPI FIFO. This is necessary because the SPI only interrupts on half
// full FIFO which can leave up to one keyboard event in the FIFO until
another
// key is pressed.
//=============================================================================
static void TimerRoutine(unsigned long Data)
{
int keycount;
//
// Get key codes from SSP and send them to the keyboard callback.
//
keycount = ReadIntoBuffer();
//
// If no keys were received, call the Data callback anyway so it can
// check for stuck keys.
//
if( (keycount==0) && gKeyCallback )
{
gKeyCallback(-1);
}
//
// Reschedule our timer in another 20 mSec.
//
g_KbdTimer.expires = jiffies +
MSECS_TO_JIFFIES( EP93XX_KEY_TIMER_PERIOD_MSEC );
add_timer(&g_KbdTimer);
}
/*
* HookInterrupt
*
* Requests SSP interrupt, sets up interrupt handler, sets up keyboard polling
* timer.
*/
static int HookInterrupt(void)
{
if (gHookedInterrupt)
{
printk( KERN_ERR "SSP driver interrupt already hooked\n");
return(-1);
}
if (request_irq(IRQ_SSPRX, SSPIrqHandler, SA_INTERRUPT, "ep93xxsspd",
NULL))
{
printk( KERN_ERR "SSP driver failed to get IRQ handler\n");
return(-1);
}
gHookedInterrupt = 1;
//
// Initialize the timer that we will use to poll the SPI.
//
init_timer(&g_KbdTimer);
g_KbdTimer.function = TimerRoutine;
g_KbdTimer.data = 1;
g_KbdTimer.expires = jiffies +
MSECS_TO_JIFFIES( EP93XX_KEY_TIMER_PERIOD_MSEC );
add_timer(&g_KbdTimer);
return(0);
}
static int SSP_Open(SSPDeviceType Device, SSPDataCallback Callback)
{
int Handle;
/*
* Generate a handle and pass it back.
*
* Increment the last valid handle.
* Check for wraparound (unlikely, but we like to be complete).
*/
iLastValidHandle++;
if((iLastValidHandle & ~SSP_DEVICE_MASK) == 0)
{
/*
* If we wrapped around start over. Unlikely.
*/
iLastValidHandle = 1;
}
Handle = iLastValidHandle | (Device << SSP_DEVICE_SHIFT);
switch (Device)
{
case PS2_KEYBOARD:
{
DPRINTK("SSP_Open - PS2_KEYBOARD\n");
if (KeyboardHandle)
{
return(-1);
}
else
{
DPRINTK("Handle:%08x Callback:%08x -- Success\n",
Handle, (unsigned int)Callback);
KeyboardHandle = Handle;
//
// Hook the interrupt if we have not yet.
//
HookInterrupt();
SetSSPtoPS2();
gKeyCallback = Callback;
}
break;
}
case I2S_CODEC:
{
DPRINTK("SSP_Open - I2S_CODEC\n");
if (I2SHandle)
{
return(-1);
}
else
{
DPRINTK("Handle:%08x Callback:%08x -- Success\n",
Handle, (unsigned int)Callback);
I2SHandle = Handle;
}
break;
}
case SERIAL_FLASH:
{
DPRINTK("SSP_Open - SERIAL_FLASH\n");
if (FlashHandle)
{
return(-1);
}
else
{
DPRINTK("Handle:%08x Callback:%08x -- Success\n",
Handle, (unsigned int)Callback);
FlashHandle = Handle;
}
break;
}
default:
{
return(-1);
}
}
/*
* Return the handle.
*/
return(Handle );
}
/*
* Release that Handle!
*/
static int SSP_Close(int Handle)
{
//
// Find out which device this API was called for.
//
switch( CheckHandle(Handle) )
{
case PS2_KEYBOARD:
{
DPRINTK("SSP_Open - PS2_KEYBOARD\n");
del_timer(&g_KbdTimer);
free_irq(IRQ_SSPRX, NULL);
gKeyCallback = NULL;
KeyboardHandle = 0;
gHookedInterrupt = 0;
break;
}
case I2S_CODEC:
{
DPRINTK("SSP_Open - I2S_CODEC\n");
I2SHandle = 0;
break;
}
case SERIAL_FLASH:
{
DPRINTK("SSP_Open - SERIAL_FLASH\n");
FlashHandle = 0;
break;
}
default:
{
return(-1);
}
}
return 0;
}
static int SSP_Read(int Handle, unsigned int Addr, unsigned int *pValue)
{
DPRINTK("SSP_Read\n");
return(0);
}
static int SSP_Write(int Handle, unsigned int Addr, unsigned int Value)
{
int iRet = 0;
// DPRINTK("SSP_Write - Handle:0x%08x Addr:0x%08x Value:0x%08x\n",
// Handle, Addr, Value );
//
// Find out which device this API was called for.
//
switch( CheckHandle(Handle) )
{
case PS2_KEYBOARD:
{
break;
}
case I2S_CODEC:
{
iRet = SSP_Write_I2SCodec( Handle, Addr, Value );
break;
}
case SERIAL_FLASH:
{
break;
}
default:
{
return(-1);
}
}
return iRet;
}
static void SetSSPtoPS2(void)
{
unsigned int uiRegTemp;
if( gSSPmode == SSP_MODE_PS2 )
{
return;
}
/*
* Disable the SSP, disable interrupts
*/
outl( 0, SSPCR1 );
/*
* It takes almost a millisecond for a key to come in so
* make sure we have completed all transactions.
*/
mdelay(1);
//
// Set EGPIO7 to disable EEPROM device on EDB9312.
//
uiRegTemp = inl(GPIO_PADDR);
outl( uiRegTemp | 0x80, GPIO_PADDR );
uiRegTemp = inl(GPIO_PADR);
outl( uiRegTemp | 0x80, GPIO_PADR );
/*
* Still haven't enabled the keyboard. So anything in
* the rx fifo is garbage. Time to take out the trash.
*/
while( inl(SSPSR) & SSPSR_RNE )
{
uiRegTemp = inl(SSPDR);
}
/*
* SPICR0_SPO - SCLKOUT Polarity
* SPICR0_SPH - SCLKOUT Phase
* Motorola format, 11 bit, one start, 8 data, one bit for
* parity, one stop bit.
*/
outl( (SSPCR0_FRF_MOTOROLA | SSPCR0_SPH | SSPCR0_SPO | SSPCR0_DSS_11BIT),
SSPCR0 );
/*
* Configure the device as a slave, Clear FIFO overrun interrupts,
* enable interrupts and reset the device.
*/
outl( (SSPC1_MS | SSPC1_RIE | SSPC1_SOD), SSPCR1 );
outl( 0, SSPIIR );
outl( (SSPC1_MS | SSPC1_RIE | SSPC1_SOD | SSPC1_SSE), SSPCR1 );
/*
* Configure EGPIO pins 12 and 14 as outputs because they are used
* as buffer enables for the SPI interface to the ps2 keyboard.
* Clear EGPIO pins 12 and 14, this will enable the SPI keyboard.
*/
uiRegTemp = inl(GPIO_PBDDR);
outl( uiRegTemp | 0x50, GPIO_PBDDR );
uiRegTemp = inl(GPIO_PBDR);
outl( uiRegTemp & ~0x50, GPIO_PBDR );
gSSPmode = SSP_MODE_PS2;
}
static void SetSSPtoI2S(void)
{
unsigned int uiRegTemp;
if( gSSPmode == SSP_MODE_I2S )
{
return;
}
/*
* Disable recieve interrupts.
*/
outl( (SSPC1_MS | SSPC1_SSE), SSPCR1 );
/*
* Set GPIO pins 12 and 14, this will bring the clock line low
* which signals to the keyboard to buffer keystrokes.
* Note that EGPIO 14 is the clock line and EGPIO 12 is data line.
*/
uiRegTemp = inl(GPIO_PBDR);
outl( 0x50 | uiRegTemp, GPIO_PBDR );
/*
* It takes almost a millisecond for an partial keystrokes to come in.
* Delay to make sure we have completed all transactions.
*/
mdelay(1);
/*
* Anything we just recieved is garbage. Time to take out the trash.
*/
while( inl(SSPSR) & SSPSR_RNE )
{
uiRegTemp = inl(SSPDR);
}
/*
* Disable the SSP and disable interrupts
*/
outl( 0, SSPCR1 );
/*
* Clock will be 14.7 MHz divided by 4.
*/
outl( 2, SSPCPSR );
/*
* Configure EGPIO7 as an output and set it. This selects
* I2S codec as the device on the SSP output instead of
* the serial flash.
*/
uiRegTemp = inl(GPIO_PADDR);
outl( uiRegTemp | 0x80, GPIO_PADDR );
uiRegTemp = inl(GPIO_PADR);
outl( uiRegTemp | 0x80, GPIO_PADR );
/*
* Motorola format, 8 bit.
*/
outl( (SSPCR0_SPO | SSPCR0_SPH | SSPCR0_FRF_MOTOROLA | SSPCR0_DSS_8BIT),
SSPCR0 );
/*
* Configure the device as master, reenable the device.
*/
outl( SSPC1_SSE, SSPCR1 );
gSSPmode = SSP_MODE_I2S;
udelay(10);
}
/*
* CheckHandle
*
* If Handle is valid, returns 0. Otherwise it returns -1.
*/
static int CheckHandle(int Handle)
{
int iRet;
if ((Handle != KeyboardHandle) &&
(Handle != I2SHandle) &&
(Handle != FlashHandle))
{
DPRINTK("OOPS! Invalid SSP Handle!\n");
return(-1);
}
/*
* Get the SSP driver instance number from the handle.
*/
iRet = (((int)Handle & SSP_DEVICE_MASK) >> SSP_DEVICE_SHIFT);
#if 0
switch(iRet)
{
case PS2_KEYBOARD:
DPRINTK("CheckHandle - valid - Keyboard\n");
break;
case I2S_CODEC:
DPRINTK("CheckHandle - valid - I2S\n");
break;
case SERIAL_FLASH:
DPRINTK("CheckHandle - valid - Keyboard\n");
break;
}
#endif
return iRet;
}
/*
* ReadIntoBuffer
*
* Drains the SSP rx fifo into a buffer here. If we overflow this buffer
* then something's wrong.
*/
static int ReadIntoBuffer(void)
{
unsigned int count, index, saved_count, uiRegTemp;
count = 0;
index = 0;
if( gSSPmode != SSP_MODE_PS2 )
{
return 0;
}
/*
* This spinlock will prevent I2S from grabbing the SSP to do a
* write while we are using the SSP for PS2.
*
* There is a slight chance that we are in the beginning phase
* of doing an I2S write but the mode flag hadn't yet switched
* to I2S. If that happens we will end up waiting on I2S to
* finish a write. Not great.
*/
spin_lock(&ssp_spinlock);
while( inl(SSPSR) & SSPSR_RNE)
{
/*
* Read in the value from the SPI controller into
* the partial key buffer.
*/
uiKeyBuffer[count] = inl(SSPDR);
if (((uiKeyBuffer[count] & 0x3fc) != 0x3e0) &&
((uiKeyBuffer[count] & 0x3fc) != 0x3c0))
{
/*
* Set GPIO pins 12 and 14, this will bring the clock line low
* which signals to the keyboard to buffer keystrokes.
* Note that EGPIO 14 is the clock line and EGPIO 12 is data
line.
*/
uiRegTemp = inl(GPIO_PBDR);
outl( 0x50 | uiRegTemp, GPIO_PBDR );
outl( 0, SSPCR1 );
outl( (SSPC1_MS | SSPC1_RIE | SSPC1_SSE), SSPCR1 );
/*
* Clear EGPIO pins 12 and 14, this will enable the SPI keyboard.
*/
uiRegTemp = inl(GPIO_PBDR);
outl( uiRegTemp & ~0x50, GPIO_PBDR );
count++;
break;
}
count++;
}
saved_count = count;
index = 0;
while (count)
{
//
// No callback, dump data.
//
if (gKeyCallback)
{
gKeyCallback(uiKeyBuffer[index++]);
}
count--;
}
spin_unlock(&ssp_spinlock);
return saved_count;
}
/*
* SSP_Write_I2SCodec
*
*/
static int SSP_Write_I2SCodec
(
int Handle,
unsigned int RegAddr,
unsigned int RegValue
)
{
SSPmodes_t saved_mode;
DPRINTK("SSP_Write_I2SCodec\n");
spin_lock(&ssp_spinlock);
/*
* Save the SSP mode. Switch to I2S mode if we're not
* already in I2S mode.
*/
saved_mode = gSSPmode;
SetSSPtoI2S();
/*
* Let TX fifo clear out. Poll the Transmit Fifo Empty bit.
*/
while( !( inl(SSPSR) & SSPSR_TFE ) );
/*
* Write the data out to the tx fifo.
*/
outl( 0x20, SSPDR ); /* chip address for CS4228 */
outl( (RegAddr & 0xff), SSPDR );
outl( (RegValue & 0xff), SSPDR );
/*
* Let TX fifo clear out. Poll the Transmit Fifo Empty bit.
*/
while( !( inl(SSPSR) & SSPSR_TFE ) );
/*
* Delay to let stuff make it out of the SR before doing
* anthing else to the SSP. It takes 6.8 uSec to do a
* I2S codec register write.
*/
udelay(10);
/*
* If we were in PS2 mode, switch back to PS2 mode.
* If we weren't in PS2 mode, that means we didn't compile in
* the PS2 keyboard support, so no need to switch to PS2 mode.
*/
if( saved_mode == SSP_MODE_PS2 )
{
SetSSPtoPS2();
}
spin_unlock(&ssp_spinlock);
/*
* Return success.
*/
return 0;
}
--
mfg
Manfred Gruber
==========================================
Contec Steuerungstechnik & Automation GmbH
Manfred Gruber
Waldeck 1
A-6330 Kufstein
AUSTRIA
------------------------------------------
HOMEPAGE: <http://www.contec.at/>
E-MAIL:manfred.gruber@xxxxxxxxx
Phone: 0043 / (0)5372 64121 25
Fax: 0043 / (0)5372 64121 20
==========================================
Other related posts:
- » [linux-cirrus] sorry no binary format allowed