linux-c-programming.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Yigit Can" <yigit.can@karel.com.tr>
To: kernelnewbies <kernelnewbies@nl.linux.org>,
	linux c programming <linux-c-programming@vger.kernel.org>
Subject: Re: SPI driver
Date: Thu, 26 Jun 2003 08:35:05 +0300	[thread overview]
Message-ID: <004201c33ba4$b4345630$3002a8c0@yigitcan> (raw)

[-- Attachment #1: Type: text/plain, Size: 742 bytes --]

sorry my mistake the code  is attached now.

the spmode register is in loopback mode to test the driver.

the problem is when we set the rx_buffer_desc. to 0xb000 it does not return
to empty state after the writing has finished.

the writing part is working (I can see it from oscilloscope)

thanks

----- Original Message -----
From: "Yigit Can" <yigit.can@karel.com.tr>
To: "linux c programming" <linux-c-programming@vger.kernel.org>
Sent: Wednesday, June 25, 2003 5:29 PM
Subject: SPI driver


>
> Hello,
>
> I'm trying to write an SPI driver for 850_ppc kernel
>
> I have written some code according to the datasheet and some samples.
>
> I can send data but I am not able to receive any data
>
> any comments ?
>
> Thank you
>
> Yigit
>

[-- Attachment #2: cpm_spi.c --]
[-- Type: application/octet-stream, Size: 9769 bytes --]

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <asm/page.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/8xx_immap.h>
#include <asm/mpc8xx.h>
#include <linux/wrapper.h>

#include "commproc.h"
//#include "cpm_spi.h"

#define DRIVER_NAME "cmp_spi"

MODULE_AUTHOR("YIGIT CAN");
MODULE_DESCRIPTION("SPI driver");

#undef DEBUG


#define CPM_SPI_MAJOR 89
#define CONFIG_CPM_SPI_BDSIZE 16

static immap_t *immap = NULL;
static cpm8xx_t *cpmp = NULL;


static unsigned int dp_addr;
static ushort t_tbase, r_rbase; 
static volatile cbd_t *tx_bdf, *rx_bdf;
static u_char *tx_buf, *rx_buf;


static int cpm_spi_open(struct inode *inode, struct file *fp);
static int cpm_spi_release(struct inode *inode, struct file *fp);
static ssize_t cpm_spi_read(struct file *filp, char *buf, size_t count, loff_t *f_pos);
static ssize_t cpm_spi_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos);
static int cpm_spi_init(void);
static void cpm_spi_cleanup(void);

/*
 * File operations supported by this driver.
 */
struct file_operations cpm_spi_fops = {
	open:    cpm_spi_open,
	release: cpm_spi_release,
	write:    cpm_spi_write,
	read:	cpm_spi_read,
};


static int cpm_spi_open ( struct inode *inode, struct file *fp )
{
	fp->f_op = &cpm_spi_fops;
	MOD_INC_USE_COUNT;
	return 0;
}

static int cpm_spi_release( struct inode *inode, struct file *fp )
{
	MOD_DEC_USE_COUNT;
	return 0;
}





static ssize_t cpm_spi_read( struct file *fp, char *buf, size_t count, loff_t *f_pos )
{
	
	volatile cpm8xx_t *cp; // comm proc pointer
	unsigned int i;

	if ( count > CONFIG_CPM_SPI_BDSIZE ) // if the length of data is greater than buffer size print error
	{
	    printk( "SPI : Invalid Size\n" );
	    return -EINVAL;
	}
	
	cp = cpmp; // pointer to the comm proc
	
/*	if ( rx_bdf->cbd_sc & BD_SC_EMPTY ) // if buffer is in use exit with error
	{
	    printk( "SPI : Buffer Descriptor is in use\n" );
	    return -EFAULT;
	}
*/
	memset( ( void* )tx_buf, 0, count ); // empty tx_buf ( fill with 0 s )

	tx_bdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP;
	tx_bdf->cbd_datlen = count;
	
	rx_bdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP; // receive reg statuses
	rx_bdf->cbd_datlen = count; // data length
	
		// start SPI transfer
	
	cp->cp_spcom |= 0x80; // start transfer
	
	for ( i=0 ;i<100000; i++) // 10000 us wait to complete transfer
	{
	    udelay( 1 );
//	    if ( (rx_bdf->cbd_sc & BD_SC_EMPTY) == 0 ) // this bit turns to 0 when the transfer completes

	    if ( (tx_bdf->cbd_sc & BD_SC_READY) == 0 ) // this bit turns to 0 when the transfer completes
		break;
	}
	
	if ( i == 100000 ) // is data received in 10000 us period ?
	{
	    printk ( "SPI : Transfer Timeout\n" );
	    return -EIO;
	}

	
	copy_to_user( ( void* )buf, ( void* )rx_buf, count );
	
	printk ("\nSONUC : %d ",*rx_buf);
	return count;
}




static ssize_t cpm_spi_write( struct file *fp, const char *buf, size_t count, loff_t *f_pos )
{
	
	volatile cpm8xx_t *cp; // comm proc pointer
	unsigned long int i;
	unsigned long flags;

//	save_flags(flags); cli () ;
	
	if ( count > CONFIG_CPM_SPI_BDSIZE ) // if the length of data is greater than buffer size print error
	{
	    printk( "SPI : Invalid Size\n" );
	    return -EINVAL;
	}
	
	cp = cpmp; // pointer to the comm proc
	

/*	if ( ( tx_bdf->cbd_sc & BD_SC_READY ) || ( rx_bdf->cbd_sc & BD_SC_EMPTY ) ) // if buffer is in use exit with error
	{
	    printk( "SPI : Buffer Descriptor is in use\n" );
	    return -EFAULT;
	}
*/	
	tx_bdf->cbd_sc = 0xB800; // trans. reg statuses
	tx_bdf->cbd_datlen = count; // data length
	
	rx_bdf->cbd_sc = 0xB000;
	rx_bdf->cbd_datlen = count;
	
	if ( copy_from_user( (void *)tx_buf,(void *) buf, count ) )
	{
	    printk ( "SPI : Error copying from user space\n" );
	    return -EFAULT;
	}
	
	// start SPI transfer
	
	printk ( "SPI : Before transfer regs : rx_bdf->cbd_sc = %08x : tx_bdf->cbd_sc = %08x \n",rx_bdf->cbd_sc, tx_bdf->cbd_sc );
	printk ( "SPI : Before transfer regs : rx_bdf->datlen = %08x : tx_bdf->datlen = %08x \n",rx_bdf->cbd_datlen, tx_bdf->cbd_datlen );
	
	cp->cp_spcom |= 0x80; // start transfer
	
	for ( i=0; i<1000000; i++) // 10000 us wait to complete transfer
	{
	    udelay( 1 );
	    if ( (tx_bdf->cbd_sc & BD_SC_READY) == 0 ) // this bit turns to 0 when the transfer completes
		break;
	}
	
	if ( i == 1000000 ) // is data sent in 10000 us period ?
	{
	    printk ( "SPI : Transfer Timeout : rx_bdf->cdb_sc = %08x : tx_bdf->cbd_sc = %08x \n",rx_bdf->cbd_sc, tx_bdf->cbd_sc );
	    return -EIO;
	}
	
	printk ("RECEIVED : %d ", *rx_buf);
//	restore_flags(flags);
	return count;
	
}


static int cpm_spi_init(void)
{
	int err = 0; // error variable
        volatile spi_t *spi; // spi  type pointer
	volatile cpic8xx_t *cpi;
	
//	unsigned long imtemp; 

        printk (KERN_INFO "CPM SPI Driver by Yigit Can\n"); // print driver info
	
	
	/* Global pointer to internal registers */
/*	if ( !cpmp )
	{
	    asm( "mfspr %0,638": "=r"(imtemp) : );
	    imtemp &= 0xFFFF0000;
	    immap = (immap_t *)&imtemp;
	} 
*/
	immap = ( immap_t * )IMAP_ADDR;	// pointer to immr reg
	cpi = ( cpic8xx_t *)&(immap->im_cpic);
	
	if ( !immap ) // if immr can not be found print error and exit
	{
	    printk ( "\n Unable to locate immr\n ");
	    return -1;
	}
		
	cpmp = ( cpm8xx_t * ) &( immap->im_cpm ); // pointer to comm proc
	
//	printk (" CPMP "); 
	spi = ( spi_t * ) &cpmp->cp_dparam[ PROFF_SPI ];   // pointer to spi

// if IIC microcode patched to the kernel use relocated rpbase 	
#ifdef  USE_IIC_PATCH 
        if ( spi->spi_rpbase ) 
	{
	    spi = ( spi_t * ) &cpmp->cp_dpmem[ spi->spi_rpbase ];
	    printk ( "SPI : Using IIC patch\n" );
	}
#endif
	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;


	/* 
	 * Initiliaze port pins for SPI
	 *                  par dir odr
	 * PB28 -> SPIMISO:  1   1   0
         * PB29 -> SPIMOSI:  1   1   0
         * PB30 -> SPICLK :  1   1   0
	 */
	 cpmp->cp_pbodr  &= ~(0x000e);
         cpmp->cp_pbdir  |=   0x000e;
         cpmp->cp_pbpar  |=   0x000e; // these pins configured as in the datasheet





        dp_addr = m8xx_cpm_dpalloc( sizeof( cbd_t ) * 2 ); //buffer malloc in dp ram
        if ( dp_addr == CPM_DP_NOSPACE ) 
	{
		printk( "cpm_spi: m8xx_cpm_dpalloc() failed\n" );
                return -ENOMEM;
        }

        spi->spi_rbase = r_rbase = dp_addr; // first part of buffer is receive base
        spi->spi_tbase = t_tbase = dp_addr + sizeof( cbd_t ); // sec part is transmit

	spi->spi_rbptr = r_rbase; //start addr of rec buffer is receive buf pointer
        spi->spi_tbptr = t_tbase; // start addr of trans buf is transmit buf pointer
	

//	cpmp->cp_cpcr |=0x0051; // as mentioned in the data sheet 
	cpmp->cp_cpcr |= mk_cr_cmd( CPM_CR_CH_SPI, CPM_CR_INIT_TRX ) | CPM_CR_FLG;
	
        while(cpmp->cp_cpcr & CPM_CR_FLG) ; // if the command completed
//	printk ("\n COMM PROC COMMAND COMPLETED \n");


        immap->im_siu_conf.sc_sdcr = 0x0001;


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


        // Set maximum receive size
        spi->spi_mrblr = 0x0010; // max receive buf size


	


	/* Clear all pending SPI events and mask all possible SPI
	   interrupts. For the moment we don't use interrupts.
	 */
	cpmp->cp_spie = 0xff;
	cpmp->cp_spim = 0x00;
	
	

        cpmp->cp_spmode = 0x4370;

//        cpmp->cp_spmode = 0x0370 | SPMODE_LOOP; // spi mode reg as mentioned in the data sheet
	 /* 
	  I will PUT OTHER PORT INIT STRINGS HERE !
	  */
	
	 /* Tx and Rx buffer descriptors. */
    
    	 tx_bdf = ( volatile cbd_t * )&cpmp->cp_dpmem[ t_tbase ];
	 rx_bdf = ( volatile cbd_t * )&cpmp->cp_dpmem[ r_rbase ];

	 printk ( "\nADRESSES : tx_bdf = %d, rx_bdf = %d   ",tx_bdf,rx_bdf);
	 /* Initialize Tx and Tx BD's */
//	 rx_bdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP;
//	 tx_bdf->cbd_sc = BD_SC_LAST | BD_SC_WRAP;
	 memset( (void*)tx_bdf, 0, sizeof(cbd_t) );
	 memset( (void*)rx_bdf, 0, sizeof(cbd_t) );
	 

	 /* Allocate memory for Rx and Tx buffers */
	 rx_buf = ( u_char * )m8xx_cpm_hostalloc( CONFIG_CPM_SPI_BDSIZE );
	 tx_buf = ( u_char * )m8xx_cpm_hostalloc( CONFIG_CPM_SPI_BDSIZE );

	 if ( rx_buf == NULL || tx_buf == NULL )
	 { 
	    printk ( "SPI : Error getting memory for rx_buf" );
	    return -ENOMEM;
	 }
	 
	 /* Set the bd's rx and tx buffer address pointers */
	 
	 tx_bdf->cbd_bufaddr = __pa( tx_buf );
	 rx_bdf->cbd_bufaddr = __pa( rx_buf );
	 
	 
	printk ( "\nBUFADDR ADRESSES (cbd_bufaddr): tx_bdf = %d, rx_bdf = %d   ",tx_bdf->cbd_bufaddr,rx_bdf->cbd_bufaddr);
	 
	 /* 
	  * Finally register the driver.
	  */
	 
	 err = register_chrdev( CPM_SPI_MAJOR, DRIVER_NAME, &cpm_spi_fops );
	 if ( err < 0 ) {
		 printk( "\ncpm_spi: Couldn't register driver ( major=%d )\n", CPM_SPI_MAJOR );
		 return err;
	 }
	else
	    printk ("SPI DEVICE REGISTERED\n");
	return 0;
}

static void cpm_spi_cleanup( void )
{
	unregister_chrdev( CPM_SPI_MAJOR, DRIVER_NAME );
	m8xx_cpm_dpfree( dp_addr );	
}

module_init(cpm_spi_init);
module_exit(cpm_spi_cleanup);

             reply	other threads:[~2003-06-26  5:35 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2003-06-26  5:35 Yigit Can [this message]
  -- strict thread matches above, loose matches on Subject: below --
2003-06-25 14:29 SPI driver Yigit Can
2003-06-25 16:27 ` Jan-Benedict Glaw

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='004201c33ba4$b4345630$3002a8c0@yigitcan' \
    --to=yigit.can@karel.com.tr \
    --cc=kernelnewbies@nl.linux.org \
    --cc=linux-c-programming@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).