linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* SPI driver?
@ 2000-11-08 18:12 clark
  2000-11-08 18:37 ` Tobias Otto-Adamczak
  0 siblings, 1 reply; 11+ messages in thread
From: clark @ 2000-11-08 18:12 UTC (permalink / raw)
  To: linuxppc-embedded


	Hello,

	I was wondering if anbody has a working SPI driver yet. I noticed one that
wasn't working yet was included in HHL 1.2 . Has anyone made any progress
on it since then? I also noticed it relied on CPM_IIC micro code patch
,  does it have any known side effects?

Many thanks in advanced

	Conn Clark
*****************************************************************
   If you live at home long enough, your parrents will move out.
*****************************************************************

Conn Clark
Engineering Stooge				clark@esteem.com
Electronic Systems Technology Inc.		www.esteem.com

Stock Ticker Symbol				ELST

"clark@esteem.com" Copyright 2000 all rights reserved. May not be sold or
used for advertisements.


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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: SPI driver?
  2000-11-08 18:12 clark
@ 2000-11-08 18:37 ` Tobias Otto-Adamczak
  0 siblings, 0 replies; 11+ messages in thread
From: Tobias Otto-Adamczak @ 2000-11-08 18:37 UTC (permalink / raw)
  To: clark; +Cc: linuxppc-embedded


> I was wondering if anbody has a working SPI driver yet.

I have no ready-to-use driver but a few functions (init, transfer,
disable) that I use successfully with my MBX.

> I also noticed it relied on CPM_IIC micro code patch,

My code works with or witout the patch; MBX uses SCC1 for Ethernet.

Ciao - Tobias


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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: SPI driver?
@ 2000-11-08 21:18 Dan Winkler
  0 siblings, 0 replies; 11+ messages in thread
From: Dan Winkler @ 2000-11-08 21:18 UTC (permalink / raw)
  To: 'clark@esteem.com', linuxppc-embedded


I have a working SPI and I2C driver with and without the microcode patch
(I'm currently using it in slave mode but it can be easily modified to work
in master mode).  I'll e-mail  them after I clean them up a bit.

-Dan Winkler

-----Original Message-----
From: clark@esteem.com [mailto:clark@esteem.com]
Sent: Wednesday, November 08, 2000 1:13 PM
To: linuxppc-embedded@lists.linuxppc.org
Subject: SPI driver?



	Hello,

	I was wondering if anbody has a working SPI driver yet. I noticed
one that
wasn't working yet was included in HHL 1.2 . Has anyone made any progress
on it since then? I also noticed it relied on CPM_IIC micro code patch
,  does it have any known side effects?

Many thanks in advanced

	Conn Clark
*****************************************************************
   If you live at home long enough, your parrents will move out.
*****************************************************************

Conn Clark
Engineering Stooge				clark@esteem.com
Electronic Systems Technology Inc.		www.esteem.com

Stock Ticker Symbol				ELST

"clark@esteem.com" Copyright 2000 all rights reserved. May not be sold or
used for advertisements.


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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* SPI driver?
@ 2001-12-18  0:48 Kevin Fry
  2001-12-18  9:50 ` Alex Zeffertt
  0 siblings, 1 reply; 11+ messages in thread
From: Kevin Fry @ 2001-12-18  0:48 UTC (permalink / raw)
  To: linuxppc-embedded


Is there an MPC8260 SPI driver out there for Linux?  It wouldn't be
terribly hard to write one on my own, but why re-invent the wheel?

A link to an 8xx driver would be almost as good. The only one I've found
so far has been SPI over a PC's parallel port. heh

Thanks

Kevin Fry


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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: SPI driver?
@ 2001-12-18  0:58 Rod Boyce
  2001-12-18  1:38 ` Kevin Fry
  0 siblings, 1 reply; 11+ messages in thread
From: Rod Boyce @ 2001-12-18  0:58 UTC (permalink / raw)
  To: 'Kevin Fry', linuxppc-embedded


Have you tried looking in the <kernel root>/arch/ppc/8xx_io directory there
is a file there called spi.c This believe it or not is a SPI driver in a
limited form for the 8XX powerpc cpu.  The kernel I'm using is 2.2.14 from
ftp://ftp.denx.de <ftp://ftp.denx.de>  this is a very good kernel I suggest
you look it over at least once.


Rod

		-----Original Message-----
		From:	Kevin Fry [mailto:kevin@carts.com]
		Sent:	Tuesday, 18 December 2001 13:49
		To:	linuxppc-embedded@lists.linuxppc.org
		Subject:	SPI driver?


		Is there an MPC8260 SPI driver out there for Linux?  It
wouldn't be
		terribly hard to write one on my own, but why re-invent the
wheel?

		A link to an 8xx driver would be almost as good. The only
one I've found
		so far has been SPI over a PC's parallel port. heh

		Thanks

		Kevin Fry


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

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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: SPI driver?
  2001-12-18  0:58 Rod Boyce
@ 2001-12-18  1:38 ` Kevin Fry
  0 siblings, 0 replies; 11+ messages in thread
From: Kevin Fry @ 2001-12-18  1:38 UTC (permalink / raw)
  To: Rod Boyce; +Cc: linuxppc-embedded


Just got it and found the driver, gracias.  I also got Denx's 2.4.4 kernel and
the SPI driver is missing. I assume this is because the microcode patches are
there and the spi driver doesn't work with them?

Is this the only 8xx or 8260 driver there is?? If there's more I'd like to see
them, just to keep my options open.

Thanks again Rod

Kevin

Rod Boyce wrote:

> Have you tried looking in the <kernel root>/arch/ppc/8xx_io directory there
> is a file there called spi.c This believe it or not is a SPI driver in a
> limited form for the 8XX powerpc cpu.  The kernel I'm using is 2.2.14 from
> ftp://ftp.denx.de <ftp://ftp.denx.de>  this is a very good kernel I suggest
> you look it over at least once.
>
> Rod


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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: SPI driver?
  2001-12-18  0:48 Kevin Fry
@ 2001-12-18  9:50 ` Alex Zeffertt
  2001-12-18 10:54   ` Alex Zeffertt
  0 siblings, 1 reply; 11+ messages in thread
From: Alex Zeffertt @ 2001-12-18  9:50 UTC (permalink / raw)
  To: Kevin Fry, linuxppc-embedded


>
> A link to an 8xx driver would be almost as good. The only one I've found
> so far has been SPI over a PC's parallel port. heh

Kevin,

Here's one (below) for the 860.  You'll probably need to change the GPIO pin used to select the SPI
slave.  You may also want get rid of the semaphore ioctl call.  I put this in as a way to allow
processes to do a sequence of reads/writes atomically.  However, it also allows one process to
permanently lock out all others :-(

Alex

/*
   MPC8xx CPM SPI interface.
   Copyright (c) 2001 Navin Boppuri/Prashant Patel
   (nboppuri@trinetcommunication.com/pmpatel@trinetcommunication.com)
   This interface requires the microcode patches.
   Helper functions for memdump put in by Ralph Nichols (ralphn@trinetcommunication.com)

   Alex Zeffertt 06Sep01
   Notes:

   0.

   This code drives the MPC860 SPI as a master device.

   1.

   The Chip selects are controlled by the ioctl interface.
   This is necessary because some SPI devices lose state
   information when the CS is unasserted.
   For example, with an SPI EEPROM (e.g. AT25010) you may
   have to assert the CS, send a READ command, receive
   some data, and then unassert the CS.  The corresponding
   user commands are:

   ioctl(s, SPI_IOCTL_RESET, 1);       // 3rd argument selects port A pin 1
   write(s, readcmdstring, sizeof(readcmdstring));
   read(s, buffer, sizeof(buffer);
   ioctl(s, SPI_IOCTL_SET, 1);         // 3rd argument selects port A pin 1

   For such a device we cannot assert the CS at the start of
   write/read and unassert it at the end of write/read, because
   it would forget what command it was processing.

   Since all devices will differ in this respect we have left
   the decision of when to assert the CS to the user.

   2.

   Several processes may have this device open at a time.  However,
   this may result in one process interferring with the other
   process.  To prevent this we have introduced a semaphore:

   // To gain semaphore
   ioctl(s, SPI_IOCTL_SEMAPHORE_DOWN, 0); // 3rd argument is unused

   ... do reads and writes and non-semaphore ioctls

   // To release semaphore
   ioctl(s, SPI_IOCTL_SEMAPHORE_UP, 0);   // 3rd argument is unused

   If every process uses these two ioctls before and after any device
   operations, then every device operation will be atomic.

   3.

   This driver protects against simultaneous calls to read/write
   by the use of wait queues.

   4.

   This driver uses interrupts to indicate when the TX buffer is sent
   or the RX buffer is full.

*/

/*########################### Includes ###############################*/

#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/irq.h>
#include <asm/8xx_immap.h>
#include "commproc.h"

/*########################### Defines/typedefs ########################*/
//#define DEBUG
#ifdef DEBUG
#define DPRINTK(format,args...) printk(format,##args)
#else
#define DPRINTK(format,args...)
#endif


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

typedef int SPI_IOCTL_FN(uint cmd, ulong arg);

typedef struct _spi_ioctl_cmds
{
    char *name;
    SPI_IOCTL_FN *fn;
} SPI_IOCTL_CMDS;


typedef enum _spi_ioctls
{
    SPI_IOCTL_INIT,
    SPI_IOCTL_RESET,
    SPI_IOCTL_SET,
    SPI_IOCTL_SEMAPHORE_UP,
    SPI_IOCTL_SEMAPHORE_DOWN,
    NUM_SPI_IOCTL_CMDS
} SPI_IOCTLS;

// number of bits in a character
#define CHAR_BITS         8

#define SPI_CLK_SPEED     500000  // Max = 2.1MHz (see data sheet) Minium = BRGCLK/1024

/*########################## Forward declarations #####################*/

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 *, const char *, size_t, loff_t *);
static ssize_t cpm_spi_read(struct file *, char *, size_t, loff_t *);
static void cpm_spi_interrupt(void *);
static int cpm_spi_ioctl(struct inode *, struct file *, uint, ulong);
static int t1_semaphore_down(uint cmd, ulong arg);
static int t1_semaphore_up(uint cmd, ulong arg);
static int t1_cs_set(uint cmd, ulong arg);
static int t1_cs_reset(uint cmd, ulong arg);
static int t1_cs_init(uint cmd, ulong arg);
int spi_ioctl_set_clk(uint cmd, ulong arg);

/*########################## File scope variables #####################*/

static SPI_IOCTL_CMDS spi_ioctl_cmds[NUM_SPI_IOCTL_CMDS] = {
    {"Initialise CS",       &t1_cs_init },
    {"Reset CS",            &t1_cs_reset},
    {"Set CS",              &t1_cs_set},
    {"Semaphore UP",        &t1_semaphore_up},
    {"Semaphore DOWN",      &t1_semaphore_down},
};

/* Number of users of the driver at any one time */
static int	cpm_spi_users = 0;

DECLARE_WAIT_QUEUE_HEAD(waitq);      // For waiting for SPI hardware to complete operation
DECLARE_WAIT_QUEUE_HEAD(lockq);      // To prevent more than one user at a time in read() or write()
static int reading_writing = 0;      // Set this when in read() or write()
static struct semaphore spi_sem;     // set this in t1_semaphore_up, clear in t1_semaphore_down

static ushort t_tbase, r_rbase;
static volatile cbd_t  *tbd, *rbd;

/* RX and TX buffers */
static ulong hostpage;              /* points to page of host memory */
static pte_t pte_old;               /* saves hostpage's Page Table Entry */
static unsigned char *rxbuffer;     /* rx and tx buffers in hostpage */
static unsigned char *txbuffer;

static struct file_operations cpm_spi_fops = {
	read: cpm_spi_read,
	write: cpm_spi_write,
	ioctl: cpm_spi_ioctl,
	open: cpm_spi_open,
	release: cpm_spi_close,
};

// Stuff initialised by cpm_spi_init()
static int two_char_times_us;  // we need to wait 2 character times after tx interrupt to be sure
buf sent
static ushort spmode;          // This is the value to write to spmode register on an open

static char *cpm_spi_drivername = "spi";
static uint  cpm_spi_dp_addr;

/*########################## Function definitions #####################*/

static int t1_semaphore_down(uint cmd, ulong arg)
{
    down_interruptible(&spi_sem);
    return 0;
}

static int t1_semaphore_up(uint cmd, ulong arg)
{
    up(&spi_sem);
    return 0;
}

static int t1_cs_set(uint cmd, ulong arg)
{
 	volatile immap_t* immap = (immap_t*)IMAP_ADDR;

    // write one to PAarg
	immap->im_ioport.iop_padat |= (0x8000 >> arg);

    return 0;
}

static int t1_cs_reset(uint cmd, ulong arg)
{
 	volatile immap_t* immap = (immap_t*)IMAP_ADDR;

    // write zero to PAarg
	immap->im_ioport.iop_padat &= ~(0x8000 >> arg);

    return 0;
}

static int t1_cs_init(uint cmd, ulong arg)
{
 	volatile immap_t* immap = (immap_t*)IMAP_ADDR;

   // Port A pin PAarg
    // configured as actively driven
    // General purpose output
//	cpmp->cp_pbodr &= ~(0x0010);  // pre mod rev A ICU
    immap->im_ioport.iop_padir |=  (0x8000 >> arg);
	immap->im_ioport.iop_papar &= ~(0x8000 >> arg);

    // Set line!
    return t1_cs_set(cmd, arg);
}

static int cpm_spi_ioctl(struct inode *ip, struct file *fp, uint cmd, ulong arg)
{

    // Is this a valid IOCTL cmd
    if (cmd < NUM_SPI_IOCTL_CMDS) {

        DPRINTK("Handling ioctl->%s\n", spi_ioctl_cmds[cmd].name);

        // Yes so handle it
        return (*(spi_ioctl_cmds[cmd].fn)) (cmd, arg);
    }

    // Invalid IOCTL command
    return -ENOIOCTLCMD;
}

int __init cpm_spi_init(void)
{
    volatile spi_t        *spi;
    volatile immap_t      *immap;
    volatile cpic8xx_t    *cpi;
    volatile sysconf8xx_t *sys;
    volatile cpm8xx_t     *cp;

	bd_t* bd = (bd_t*)__res;
	unsigned int system_clock = bd->bi_busfreq * 1000000;
    unsigned int divisor = system_clock/SPI_CLK_SPEED;
    int sys_clocks_per_spi_clock = 1;

    pte_t *pte;

	DPRINTK(__FUNCTION__ "\n");


    // Work out what to write to SPMODE:
    spmode =
      //SPMODE_REV   | // msb of character sent and recvd first // E1 device has lsb first
        SPMODE_MASTER| // SPI is a master
        SPMODE_EN    | // enable device
        ((CHAR_BITS-1) << SPMODE_LEN_SHIFT); // Sets the number of bits in a character

    // Work out how to set speed
    if (divisor >= 16) {
        spmode |= SPMODE_DIV16;               // pre divide BRGCLK by 16
        divisor = (divisor + 15)/16;          // rounds down clockspeed
        sys_clocks_per_spi_clock *= 16;
    }
    if (divisor > 64) {
        printk("Error: (" __FUNCTION__ ") SPI_CLK_SPEED out of range\n");
        return -EFAULT;
    } else {
        divisor = (divisor + 3)/4;            // rounds down clockspeed
        spmode |= (divisor-1) << SPMODE_PM_SHIFT;
        sys_clocks_per_spi_clock *= (4*divisor);
    }

    // Calculate how many us 2 character times is (and round up for safety)
    two_char_times_us = ((2*CHAR_BITS*sys_clocks_per_spi_clock) +
(bd->bi_busfreq-1))/bd->bi_busfreq; // round up

	// Get internal registers
    immap = (immap_t *)IMAP_ADDR;

	//cpm interrupt controller
	cpi = (cpic8xx_t*)&(immap->im_cpic);

	//cpm interrupt controller
    sys = (sysconf8xx_t*)&(immap->im_siu_conf);

	// Get pointer to Communication Processor
    cp = cpmp;
    spi = (volatile spi_t *)&cp->cp_dparam[PROFF_SPI];

#if USE_IIC_PATCH // I2C patch also relocates SPI
	// Check for SPI relocation patch
    if ((reloc = spi->spi_rpbase)) {
        spi = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase];
        printk(" MICROCODE RELOCATION PATCH \n");
    }
#endif
    /* 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.
     */
    cpm_spi_dp_addr = m8xx_cpm_dpalloc(sizeof(cbd_t) * 2);

    // Set up the SPI parameters in the parameter ram.
    spi->spi_rbase = r_rbase = cpm_spi_dp_addr;
    spi->spi_tbase = t_tbase = cpm_spi_dp_addr + sizeof(cbd_t);

	/***********IMPORTANT******************/
	/* Setting transmit and receive buffer descriptor
	   pointers intially to rbase and rbase. Only the
	   microcode patches documentation talks about initializing
	   this pointer. This is missing from the sample I2C driver.
	   If you dont initialize these pointers, the kernel hangs. */
    spi->spi_rbptr = r_rbase;
    spi->spi_tbptr = t_tbase;

    // Set to big endian
    spi->spi_tfcr = SMC_EB;
    spi->spi_rfcr = SMC_EB;

    // Set maximum receive size
    spi->spi_mrblr = SPI_BUFFER_SIZE;

    // Setting CPCR (initialise rx and tx params)
	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SPI,CPM_CR_INIT_TRX) | CPM_CR_FLG;

    // Wait for acknowledgement
    while (cp->cp_cpcr & CPM_CR_FLG)
       ;
    // sets SDMA configuration register
	immap->im_siu_conf.sc_sdcr = 0x0001;

	// clear all spi events
    cp->cp_spie = 0xff;

	// Clear SPI interrupt Mask (We only want interrupts when device is open)
    cp->cp_spim = 0;

	/* My PortB setting */
	/* These setting may be different for you.
	   Refer example 16-443 MPC823 Manual*/

    // Port B pin 31 configured as
    // actively driven General purpose
    // output
    // Port B pins 28, 29, 30
    // configured as SPIMISO, SPIMOSI,
    // and SPICLK, respectively
	cp->cp_pbodr &= ~0x000e;
    cp->cp_pbodr |=  0x0001;
	cp->cp_pbdir |=  0x000f;
    cp->cp_pbpar &= ~0x0001;
	cp->cp_pbpar |=  0x000e;

    // Initialise PA1 and PA3 for output - these go to CLKXSRC on McBSB1
    // and CS on E1 line driver, respectively
    t1_cs_init(2, 1);
    t1_cs_init(2, 3);

	// tx and rx buffer descriptors
	tbd = (volatile cbd_t *)&cp->cp_dpmem[t_tbase];
	rbd = (volatile cbd_t *)&cp->cp_dpmem[r_rbase];

	// Initialize tx and rx bd's (in particular
    // clear the BD_SC_READY and BD_SC_EMPTY bits
    memset((void*)tbd, 0, sizeof(cbd_t));
    memset((void*)rbd, 0, sizeof(cbd_t));

	// Allocate 1 page of host memory for the data
	hostpage = __get_dma_pages(GFP_KERNEL,0 /*order*/);
    pte = va_to_pte(hostpage);

    // Save the current pte setting for the page
    pte_val(pte_old) = pte_val(*pte);

    // Disable cacheing for the page
    pte_val(*pte) |= _PAGE_NO_CACHE;

    // Make it clean
    flush_tlb_page(current->mm->mmap, hostpage);

    rxbuffer = (unsigned char *) hostpage;
    txbuffer = rxbuffer + SPI_BUFFER_SIZE;

	/* Set the bd's rx and tx buffer address pointers */
    tbd->cbd_bufaddr = __pa(txbuffer);
    rbd->cbd_bufaddr = __pa(rxbuffer);

    /* install interrupt handler if using interrupts */
    cpm_install_handler(CPMVEC_SPI, cpm_spi_interrupt, (void *)spi);

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

    printk("Successfully registered spi major=%d\n", CPM_SPI_CDEV_MAJOR);

    /* initialise the semaphore for mutual exclusion */
    sema_init(&spi_sem, 1);

    return 0;
}

/* Open does not have to do much */
static int cpm_spi_open(struct inode *ip, struct file *fp)
{
	DPRINTK(__FUNCTION__ "\n");

    fp->f_op = &cpm_spi_fops;

	/* Increment the number of users. */
    if (cpm_spi_users++ == 0)
    {
        /* If this is the first user we need to initialise device */
        /* Write to SPMODE */
        cpmp->cp_spmode = spmode;
        DPRINTK("cpmp->cp_spmode=%04x\n",cpmp->cp_spmode);

        /* Set mask to allow all interrupts */
        cpmp->cp_spim =
            SPIE_MME|
            SPIE_TXE|
            SPIE_BSY|
            SPIE_TXB|
            SPIE_RXB;
    }

    return(0);
}

static int cpm_spi_close(struct inode *ip, struct file *fp)
{
	DPRINTK(__FUNCTION__ "\n");

    if (--cpm_spi_users == 0)
    {
        /* If no more users left shut down SPI. */
        cpmp->cp_spmode = 0;
        cpmp->cp_spie = 0xff;
        cpmp->cp_spim = 0;
    }

    return(0);
}


/* write is exactly like read.
   spi does read and write simultaneously */

static ssize_t cpm_spi_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
{
    volatile immap_t	*immap;
    volatile cpic8xx_t 	*cpi;
    volatile cpm8xx_t	*cp;

    // Cannot have 2 users sending or receiving at once
    wait_event_interruptible(lockq,reading_writing == 0);
    reading_writing = 1;

	DPRINTK(__FUNCTION__ "\n");

    // Make sure buffer is not greater than buffer size !
    if (count > SPI_BUFFER_SIZE)
        return -EINVAL;

    // Get internal memory map
    immap = (immap_t *)IMAP_ADDR;

    // Get cpm interrupt controller
    cpi = (cpic8xx_t*) &(immap->im_cpic);

    // Get pointer to Communication Processor
    cp = cpmp;

    // Make sure BD is not in use !
    if ((tbd->cbd_sc & BD_SC_READY) || (rbd->cbd_sc & BD_SC_EMPTY))
    {
        printk("Error: BD in use\n");
        return -EFAULT;
    }

    // Set the tx bd status and data length
    tbd->cbd_sc =  BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_SC_INTRPT;
    tbd->cbd_datlen  = count;

    // Set rx bd status and data length (=0 because we're not reading here)
    rbd->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP | BD_SC_INTRPT;
    rbd->cbd_datlen = 0;

    // Copy transmit data from user space to tx buffer */
    if (copy_from_user((void*)txbuffer,(void*)buf, count))
    {
        printk("Error: copying from user\n");
        return -EFAULT;
    }

    // Start spi transfer
    cp->cp_spcom  |= 0x80;

    // Wait for transfer to complete
    wait_event_interruptible(waitq, (tbd->cbd_sc & BD_SC_READY) == 0 && (rbd->cbd_sc & BD_SC_EMPTY)
== 0);
    DPRINTK("txsc=%04x/rxsc=%04x\n", tbd->cbd_sc, rbd->cbd_sc);

    // Wait two char times like the book says
    udelay(two_char_times_us);

    // Allow another user to send or receive
    reading_writing = 0;
    wake_up_interruptible(&lockq);

    return count;
}


/* read is exactly like write.
   spi does read and write simultaneously.
   It's not exactly simultaneous. I've noticed that transmit is done
   first and then receive.*/

static ssize_t cpm_spi_read(struct file *file, char *buf,
                            size_t count, loff_t *ppos)
{
    volatile immap_t	*immap;
    volatile cpic8xx_t 	*cpi;
    volatile cpm8xx_t	*cp;

    // Cannot have 2 users sending or receiving at once
    wait_event_interruptible(lockq,reading_writing == 0);
    reading_writing = 1;

	DPRINTK(__FUNCTION__ "\n");

    // Make sure buffer is not greater than buffer size !
    if (count > SPI_BUFFER_SIZE)
    {
        return -EINVAL;
    }

    //printk("Count is %d\n", count);

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

    cpi = (cpic8xx_t*)&(immap->im_cpic); //cpm interrupt controller
    cp = cpmp;	/* Get pointer to Communication Processor */

    // check BDs are not in use!
    if ((tbd->cbd_sc & BD_SC_READY) || (rbd->cbd_sc & BD_SC_EMPTY))
    {
        printk("Error: BD in use\n");
        return -EFAULT;
    }

    /* Initialize buffer memory to empty */
    memset((void*)txbuffer, 0, count);

    // Setting tx bd status and data length
    tbd->cbd_sc =  BD_SC_READY | BD_SC_LAST | BD_SC_WRAP | BD_SC_INTRPT;
    tbd->cbd_datlen  = count; /* We write count 0 s and hope device ignores it */

    // Setting rx bd status and data length
    rbd->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP | BD_SC_INTRPT;
    rbd->cbd_datlen = count;

    // Start spi transfer
    cp->cp_spcom |= 0x80;

    // Wait for transfer to complete
    wait_event_interruptible(waitq, (tbd->cbd_sc & BD_SC_READY) == 0 && (rbd->cbd_sc & BD_SC_EMPTY)
== 0);
    DPRINTK("txsc=%04x/rxsc=%04x\n", tbd->cbd_sc, rbd->cbd_sc);

    // Wait two char times like the book says
    udelay(two_char_times_us);

    // Transfer is done.

    //printk("Reception: ");
    //memdump((void*)rxbuffer, count);/* dump of rxbuffer after transmit */

    /* copy received bytes to user space */
    /***********
     *  Please note that the data in rxbuffer is in big endian format.
     *  you will have to flip it.
     *************/
    copy_to_user((void*)buf, (void*)rxbuffer, count);

    // Allow another user to send or receive
    reading_writing = 0;
    wake_up_interruptible(&lockq);

    return count;
}

/* Interrupt handler in case you use spi interrupts. I have tried using the interrupts
   and they work just fine. You can use his handler to detect */
static void cpm_spi_interrupt(void *dev_id)
{
    unsigned char events = cpmp->cp_spie;

	DPRINTK("Spi event reg. 0x%x\n", events);

	/* reset spi event register and mode */
	cpmp->cp_spie = events;

    if (SPIE_MME & events)
        printk ("Error: ~SPISEL asserted externally while SPI in master mode\n");

    if (SPIE_TXE & events)
        printk("Error: Tx error\n");

    if (SPIE_BSY & events)
        printk("Error: No Rx buffer available\n");

    if (SPIE_TXB & events)
        wake_up_interruptible(&waitq);

    if (SPIE_RXB & events)
        wake_up_interruptible(&waitq);
}
/********************************************************
 * module_init() is called by insmod, if built as module,
 * or by do_initcalls(), if built as a resident driver.
 ********************************************************/
module_init(cpm_spi_init);

#ifdef MODULE
static void __exit cpm_spi_exit(void)
{
    volatile cpm8xx_t	*cp = cpmp;
    pte_t *pte;

    // Only allow this is no user has device open
    // This should be automatically true.
    // (This means we're not going to get any interrupts while doing rmmod.)
    if (cpm_spi_users != 0)
        printk(__FILE__ " " __FUNCTION__ " line %d: This should not happen\n", __LINE__);

    // unregister the driver majore number
    if (unregister_chrdev(CPM_SPI_CDEV_MAJOR, cpm_spi_drivername) < 0)
        printk(__FILE__ " " __FUNCTION__ " line %d: This should not happen\n", __LINE__);

    // uninstall cpm interrupt handler
    cpm_free_handler(CPMVEC_SPI);

    // Hand back page containing rxbuffer and txbuffer
    pte = va_to_pte(hostpage);
    pte_val(*pte) = pte_val(pte_old);

    // Make it clean
    flush_tlb_page(current->mm->mmap, hostpage);

    // Free the page
    free_pages(hostpage, 0 /*order*/);

    // Reinitialise cs's back to old state
    t1_cs_init(2, 3);
    t1_cs_init(2, 1);

    // Reinitialise Port B
    cp->cp_pbpar &=  ~0x000f;
	cp->cp_pbdir |=  0x000f;
    cp->cp_pbdat |=  0x000f;


    // Free the memory allocated to cpm_spi_dp_addr
    m8xx_cpm_dpfree(cpm_spi_dp_addr);
}
module_exit(cpm_spi_exit);
#endif // MODULE

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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: SPI driver?
  2001-12-18  9:50 ` Alex Zeffertt
@ 2001-12-18 10:54   ` Alex Zeffertt
  0 siblings, 0 replies; 11+ messages in thread
From: Alex Zeffertt @ 2001-12-18 10:54 UTC (permalink / raw)
  To: Kevin Fry, linuxppc-embedded


Alex Zeffertt wrote:
>
> >
> > A link to an 8xx driver would be almost as good. The only one I've found
> > so far has been SPI over a PC's parallel port. heh
>

Kevin,

Sorry, I forgot to add that you need to make the following mods to arch/ppc/8xx_io/commproc.h

Alex

diff -u linux-2.4.4-2001-11-24/arch/ppc/8xx_io/commproc.h
linux-2.4.4-2001-11-24.new/arch/ppc/8xx_io/commproc.h
--- linux-2.4.4-2001-11-24/arch/ppc/8xx_io/commproc.h   Mon Sep 10 16:29:33
2001                                                               +++
linux-2.4.4-2001-11-24.new/arch/ppc/8xx_io/commproc.h       Fri Dec  7 11:54:58 2001
@@ -37,6 +37,7 @@
 #define CPM_CR_RESTART_TX      ((ushort)0x0006)
 #define CPM_CR_SET_GADDR       ((ushort)0x0008)
 #define CPM_CR_SET_TIMER       CPM_CR_SET_GADDR
+#define CPM_CR_CLOSE_RX_BD  ((ushort)0x0007)

 /* Channel numbers.
 */
@@ -93,7 +94,9 @@
 #define BD_SC_PR       ((ushort)0x0008)        /* Parity error */
 #define BD_SC_NAK      ((ushort)0x0004)        /* NAK - did not respond */
 #define BD_SC_OV       ((ushort)0x0002)        /* Overrun */
+#define BD_SC_UN    ((ushort)0x0002)    /* Underrun */
 #define BD_SC_CD       ((ushort)0x0001)        /* ?? */
+#define BD_SC_CL    ((ushort)0x0001)    /* Collision */

 /* Parameter RAM offsets.
 */
@@ -704,7 +707,11 @@
 #define SICR_ENET_MASK ((uint)0x0000ff00)
 #define SICR_ENET_CLKRT        ((uint)0x00003E00)

-#undef USE_IIC_PATCH                           /* We need the I²C µCode Patch */
+#ifdef CONFIG_UCODE_PATCH
+#  define USE_IIC_PATCH
+#else
+#  undef  USE_IIC_PATCH                                /* We need the I²C µCode Patch */
+#endif

 #endif /* CONFIG_LWMON */

@@ -962,6 +969,16 @@
 #define SPMODE_EN      ((ushort)0x0100)        /* Enable */
 #define SPMODE_LENMSK  ((ushort)0x00f0)        /* character length */
 #define SPMODE_PMMSK   ((ushort)0x000f)        /* prescale modulus */
+#define SPMODE_MASTER  ((ushort)0x0200)     /* Am SPI master */
+#define SPMODE_LEN_SHIFT 4                  /* shift of character length field */
+#define SPMODE_PM_SHIFT  0                  /* shift of Prescale Modulus field */
+
+/* SPIE fields */
+#define SPIE_MME  0x20
+#define SPIE_TXE  0x10
+#define SPIE_BSY  0x04
+#define SPIE_TXB  0x02
+#define SPIE_RXB  0x01

 /* CPM interrupts.  There are nearly 32 interrupts generated by CPM
  * channels or devices.  All of these are presented to the PPC core

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

^ permalink raw reply	[flat|nested] 11+ messages in thread

* SPI driver?
@ 2007-09-10 16:16 Hesam Kohanteb
  2007-09-11 15:50 ` Nicholas Hickman
  0 siblings, 1 reply; 11+ messages in thread
From: Hesam Kohanteb @ 2007-09-10 16:16 UTC (permalink / raw)
  To: linuxppc-embedded

We need a Linux based SPI driver for MPC885. Do you know where can I 
possibly find the code.

Regards. --Hesam

-- 
Hesam Yehuda Kohanteb
(Netra Systems & Networking), 
M/S USCA12-216  4120 Network Circle
Santa Clara, CA  95054
(W) 408-276-7329 X17329, Fax 408-276-4552

^ permalink raw reply	[flat|nested] 11+ messages in thread

* RE: SPI driver?
  2007-09-10 16:16 SPI driver? Hesam Kohanteb
@ 2007-09-11 15:50 ` Nicholas Hickman
  2007-09-11 20:19   ` Wolfgang Denk
  0 siblings, 1 reply; 11+ messages in thread
From: Nicholas Hickman @ 2007-09-11 15:50 UTC (permalink / raw)
  To: Hesam.Kohanteb, linuxppc-embedded

I am also in search of this, or some information about one.  Please let
me know if you find anything.

I ran across an existing platform that used an mpc8270.  During boot up
it displayed:
[   17.828513] SPIDriver: module license 'Proprietary' taints kernel.
[   17.865052] CPM SPI Driver: $Revision: 1.0 $ wd@denx.de

I have been all over denx and have not been able to find it.  This
particular system used the SPI for a DSP chip.=20



-Nick

-----Original Message-----
From: linuxppc-embedded-bounces+nhickman=3Ddtechlabs.com@ozlabs.org
[mailto:linuxppc-embedded-bounces+nhickman=3Ddtechlabs.com@ozlabs.org] =
On
Behalf Of Hesam Kohanteb
Sent: Monday, September 10, 2007 12:17 PM
To: linuxppc-embedded@ozlabs.org
Subject: SPI driver?

We need a Linux based SPI driver for MPC885. Do you know where can I
possibly find the code.

Regards. --Hesam

--
Hesam Yehuda Kohanteb
(Netra Systems & Networking),
M/S USCA12-216  4120 Network Circle
Santa Clara, CA  95054
(W) 408-276-7329 X17329, Fax 408-276-4552

_______________________________________________
Linuxppc-embedded mailing list
Linuxppc-embedded@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-embedded

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: SPI driver?
  2007-09-11 15:50 ` Nicholas Hickman
@ 2007-09-11 20:19   ` Wolfgang Denk
  0 siblings, 0 replies; 11+ messages in thread
From: Wolfgang Denk @ 2007-09-11 20:19 UTC (permalink / raw)
  To: Nicholas Hickman; +Cc: Hesam.Kohanteb, linuxppc-embedded

In message <8140AAF341CC904BA92C3D6A5A1909782F0D4F@ditech-1.ditechllc.com> you wrote:
> I am also in search of this, or some information about one.  Please let
> me know if you find anything.
> 
> I ran across an existing platform that used an mpc8270.  During boot up
> it displayed:
> [   17.828513] SPIDriver: module license 'Proprietary' taints kernel.
> [   17.865052] CPM SPI Driver: $Revision: 1.0 $ wd@denx.de
> 
> I have been all over denx and have not been able to find it.  This
> particular system used the SPI for a DSP chip. 

Probably somebody copied and modified a  driver  they  found  in  our
trees. We release all such code under GPL...

You can find SPI driver code in our old linuxppc_2_4_devel tree,  but
this  does  not  include  support  for  MPC82xx; and then there is an
ancient (5+ years) 82xx SPI driver in our  linux-2.4  (2.4.4)  kernel
tree.

Best regards,

Wolfgang Denk

-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd@denx.de
Thought for the day: What if there were no hypothetical situations?

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2007-09-11 20:19 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-09-10 16:16 SPI driver? Hesam Kohanteb
2007-09-11 15:50 ` Nicholas Hickman
2007-09-11 20:19   ` Wolfgang Denk
  -- strict thread matches above, loose matches on Subject: below --
2001-12-18  0:58 Rod Boyce
2001-12-18  1:38 ` Kevin Fry
2001-12-18  0:48 Kevin Fry
2001-12-18  9:50 ` Alex Zeffertt
2001-12-18 10:54   ` Alex Zeffertt
2000-11-08 21:18 Dan Winkler
2000-11-08 18:12 clark
2000-11-08 18:37 ` Tobias Otto-Adamczak

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).