SPI driver problems

Navin Boppuri nboppuri at trinetcommunication.com
Thu Dec 21 04:17:49 EST 2000


Hello All,

I am trying to implement an SPI driver for my custom board with 823.  I have
3 uarts connected to my SPI controller. PBDAT is used as chip selects for
these devices. I followed the IIC driver example for the 823 by Dan Malek. I
setup the receive and transmit buffer rings and data buffers. When I start
the transfer by setting SPCOM |= 0x80, the board just hangs. I dont see
anything. I do have an interrupt handler which does not seem to be working.
I am attaching the code. Can anyone suggest anything?

Thank you,
Sincerely,
Navin Boppuri

/* MPC8xx CPM SPI interface.
 */

#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/mpc8xx.h>
#include <asm/imd.h>
#include "commproc.h"

char quickhex(int i)
{
  if(i >= 10 ) return i - 10 + 'A';
  return i + '0';
}

int quickisprint(int c)
{
  if( c >= 'A' && c <= 'Z' ) return 1;
  if( c >= 'a' && c <= 'z' ) return 1;
  if( c >= '0' && c <= '9' ) return 1;
  return 0;
}

void memdump(void *pv, int num)
{
  int i;
  unsigned char* pc = (unsigned char*)pv;
  for(i = 0; i < num; i++ )
    printk("%c%c ", quickhex(pc[i] >> 4), quickhex(pc[i] & 0x0f));
  printk("\t");
  for(i = 0; i < num; i++ )
    printk("%c", quickisprint(pc[i]) ? pc[i] : '.');
  printk("\n");
}

/* This is the SPI device number, but I use the minors to indicate
 * the SPI device address.
 */
#define CPM_SPI_CDEV_MAJOR 89

void cpm_spi_init(void);
static int cpm_spi_open(struct inode *, struct file *);
static int cpm_spi_close(struct inode *, struct file *);
static ssize_t cpm_spi_write(struct file *, char *, size_t, loff_t *);
static void cpm_spi_interrupt(void *);

static struct file_operations cpm_spi_fops = {
 NULL,  /* lseek */
 NULL, /* read */
 cpm_spi_write,  /* write */
 NULL,  /* readdir */
 NULL,  /* poll */
 NULL,  /* ioctl */
 NULL,  /* mmap */
 cpm_spi_open,   /* open */
 NULL,  /* flush */
 cpm_spi_close,  /* close */
};

/* Only one user at a time.
*/
static int cpm_spi_flags;
#define CPM_SPI_OPEN 0x01

static dev_t spi_device;

static struct wait_queue *spi_wait;

static
ushort r_tbase, r_rbase;

void
cpm_spi_init()
{
 uint mem_addr, dp_addr, reloc;
 volatile cpm8xx_t *cp;
 volatile spi_t  *spi;
 volatile immap_t *immap;
 volatile cpic8xx_t  *cpi;
 volatile sysconf8xx_t   *sys;

 immap = (immap_t *)IMAP_ADDR; /* and to internal registers */

 cpi = (cpic8xx_t*)&(((immap_t*)IMAP_ADDR)->im_cpic); file://cpm interrupt
controller
 sys = (sysconf8xx_t*)&(((immap_t*)IMAP_ADDR)->im_siu_conf); file://cpm
interrupt controller

 cp = cpmp; /* Get pointer to Communication Processor */

 spi = (spi_t *)&cp->cp_dparam[PROFF_SPI];

 printk("Initializing spi\n");

 /* Check for and use a microcode relocation patch.
 */
 if ((reloc = spi->spi_rpbase))
 {
  spi = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase];
  printk(" MICROCODE RELOCATION PATCH ");
 }


 /* Initialize the parameter ram.
  * We need to make sure many things are initialized to zero,
  * especially in the case of a microcode patch.
  */
 spi->spi_rstate = 0;
 spi->spi_rdp = 0;
 spi->spi_rbptr = 0;
 spi->spi_rbc = 0;
 spi->spi_rxtmp = 0;
 spi->spi_tstate = 0;
 spi->spi_tdp = 0;
 spi->spi_tbptr = 0;
 spi->spi_tbc = 0;
 spi->spi_txtmp = 0;


 /* Allocate space for one transmit and one receive buffer
  * descriptor in the DP ram.
  */
 dp_addr = m8xx_cpm_dpalloc(sizeof(cbd_t) * 2);

 /* Set up the IIC parameters in the parameter ram.
 */
 spi->spi_rbase = r_rbase = dp_addr;
 spi->spi_tbase = r_tbase = dp_addr + sizeof(cbd_t);

 /* Set the buffer address.*/
 spi->spi_tfcr = SMC_EB;
 spi->spi_rfcr = SMC_EB;

 /* Set maximum receive size.*/
 spi->spi_mrblr = 64;

 /* Setting CPCR */
// if (reloc == 0) {
//  cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SPI, CPM_CR_INIT_TRX) | CPM_CR_FLG;
//  while (cp->cp_cpcr & CPM_CR_FLG);
// }
 cp->cp_cpcr |= 0x51;
 immap->im_siu_conf.sc_sdcr = 0x0001;

 cp->cp_spie = 0xff; file://clear all spi events
 cp->cp_spim = 0x00; file://mask all SPI events

 /* Setting some interrupts */
 file://cpi->cpic_cimr |= 0x00000020;
 sys->sc_simask |= 0x20400000;
 sys->sc_siel   |= 0x20000000;

 /* SPI Mode setting */
 cp->cp_spmode = 0x0fff;

 asm("mtspr 80,0");

 /* Install interrupt handler.
 */
 cpm_install_handler(CPMVEC_SPI, cpm_spi_interrupt, (void *)spi);

 if (register_chrdev(CPM_SPI_CDEV_MAJOR,"spi",&cpm_spi_fops))
  printk("unable to get major %d for SPI devs\n",
    CPM_SPI_CDEV_MAJOR);


 printk("Succesfully registered spi 89 \n");


return;
}

/* Open does not have to do much, just make sure only one user.
*/
static int
cpm_spi_open(struct inode *ip, struct file *fp)
{
 printk("Opening spi \n");
 /* We allow only one user of the device.
 */
 if (cpm_spi_flags & CPM_SPI_OPEN) /* device is busy */
  return(EBUSY);

 fp->f_op = &cpm_spi_fops;

 spi_device = MINOR(ip->i_rdev);

 cpm_spi_flags |= CPM_SPI_OPEN;

 printk("Successfully opened spi\n");

 return(0);
}

static int
cpm_spi_close(struct inode *ip, struct file *fp)
{
 volatile cpm8xx_t *cp;
 cp = cpmp;

 printk("Closing spi \n");

 /* Shut down SPI.
 */
 cp->cp_spmode = 0;
 cp->cp_spie = 0xff;
 cp->cp_spim = 0;

 cpm_spi_flags &= ~CPM_SPI_OPEN;

 printk("Closed spi \n");

return(0);
}


/* Read
 */
static ushort txbuffer[64]; // transmit buffer
static  ushort  rxbuffer[64]; file://receive buffer

static ssize_t
cpm_spi_write(struct file *file, char *buf, size_t count, loff_t *ppos)
{
 volatile buf_t* spi_buf = 0;
 volatile uint virt_addr = 0;
 volatile cpic8xx_t *cpi;
 volatile cbd_t *tbdf, *rbdf;
 volatile cpm8xx_t *cp;
 char tspitemp[count];
 int i;
 unsigned long flags;


 cpi = (cpic8xx_t*)&(((immap_t*)IMAP_ADDR)->im_cpic); file://cpm interrupt
controller

 cp = cpmp; /* Get pointer to Communication Processor */

 tbdf = (cbd_t *)&cp->cp_dpmem[r_tbase];
 rbdf = (cbd_t *)&cp->cp_dpmem[r_rbase];

 /* Preparing transmit buffer */
 for(i = 0;i < count+1;i++)
 {
  if(i == 0)
   txbuffer[i] = 0x0bc4; /* uart initialization */
  else
   txbuffer[i] = ((ushort)buf[i-1] << 8) | 0x0080; /* data in BIG-Endian */
 }
 memdump((void*)txbuffer,32); /* view txbuffer */

 tbdf->cbd_bufaddr = __pa(txbuffer);
 tbdf->cbd_datlen  = (count*2) + 2;
 tbdf->cbd_sc =  BD_SC_READY | BD_SC_LAST;

 memdump((void*)rxbuffer,32); /* view rxbuffer */
 rbdf->cbd_bufaddr = __pa(rxbuffer);
 rbdf->cbd_datlen = 0;
 rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_INTRPT | BD_SC_LAST;


 /* Setting up Port B */

 cp->cp_pbodr &= ~(0x000e);
 cp->cp_pbdir |= 0x000f;
 cp->cp_pbpar |= 0x000e;

 cp->cp_pbodr &= ~(0x1230);
 cp->cp_pbdir |= 0x1230;
 cp->cp_pbpar &= ~(0x1231);

 cp->cp_pbdat |= 0x0001;
 cp->cp_pbdat |= 0x1230;
 cp->cp_pbdat &= ~(0x0010);



 save_flags(flags);cli();

 cp->cp_spie = 0xff; file://clear all spi events
 cp->cp_spim = 0x37; file://mask all SPI events

 /* Starting transfer */
 cp->cp_spcom   |= 0x80;
 cpi->cpic_cicr |= 0x0080;

 interruptible_sleep_on(&spi_wait);
 restore_flags(flags);
 if (signal_pending(current))
  return -ERESTARTSYS;

 printk("Done writing, now return\n");
 return count;
}



static void
cpm_spi_interrupt(void *dev_id)
{
 volatile spi_t *spi;
 volatile cpm8xx_t *cp;

 cp = cpmp; /* Get pointer to Communication Processor */

 printk("In interuppt code\n");
 spi = (spi_t *)dev_id;

 /* Chip errata, clear enable.
 */
 cp->cp_spmode = 0;

 /* Clear interrupt.
 */
 cp->cp_spie = 0xff;

 /* Get 'me going again.
 */
 wake_up_interruptible(&spi_wait);

 printk("Leaving interrupt code\n");
}


** Sent via the linuxppc-embedded mail list. See http://lists.linuxppc.org/





More information about the Linuxppc-embedded mailing list