Hi,

I attached a driver for the SPI controller of MPC875 that I'm developing. It's 
not yet complete, actually, in the mpc8xx_transfer () method simply tries to 
transmit 3 bytes (the SPI controller is looped) and thus I expect to receive 
the same three bytes. It works sometimes, but not always and I don't know why. 
In the init I configure the SPI controller, to do this, I do a ioremap_nocache 
of  physaddr IMMAP address and then write the relevant registers. I also tried 
to put __iomem qualifier to the SPI registers pointers thinking that the writes 
to the SPI registers were cached. No success! What could be the problem?

Bye,
Melinda.


 
---------------------------------
Everyone is raving about the all-new Yahoo! Mail beta.
/*
 * MPC83xx SPI controller driver.
 *
 * Maintainer: Kumar Gala
 *
 * Copyright (C) 2006 Polycom, Inc.
 *
 * 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.
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/completion.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <linux/platform_device.h>
#include <linux/fsl_devices.h>
#include <linux/fs_spi_pd.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/commproc.h>


/* MPC8xx SPI Controller mode register definitions are in commproc.h */
#define	SPMODE_LEN(x)		    ((x-1) << 4)
#define	SPMODE_PM(x)		    ((x))

#define SPI_MAX_BUFFER_SIZE         32
/*
 * Default for SPI Mode:
 * 	SPI MODE 0 (inactive low, phase middle, MSB, 8-bit length, slow clk
 */
#define	SPMODE_INIT_VAL (SPMODE_CI | SPMODE_DIV16 | SPMODE_REV | \
			 SPMODE_MS | SPMODE_LEN(7) | SPMODE_PM(0xf))

/* SPIE/SPIM register values */
#define	SPIE_MME		0x20	/* Multimaster error */
#define	SPIE_TXE		0x10	/* Tx error */
#define	SPIE_BSY		0x04    /* No rx buffer available */
#define	SPIE_TXB		0x02    /* Tx buffer transmitted */
#define	SPIE_NF		        0x01    /* Rx buffer filled */

/* SPCOM values */
#define SPCOM_START             0x80    /* Start transmission command */

#ifndef BD_SC_ME
#define BD_SC_ME        ((ushort)0x0001)    /* Multi Master Error */
#endif

/*************************************************/


int j = 0;
static u8* rxbuffer;
static u8* txbuffer;
void spi_activate_cs(u8 cs, u8 polarity);
void spi_deactivate_cs(u8 cs, u8 polarity);

static car8xx_t     *carp; 
static spi_t	    *spi;  
static immap_t      *immap;
static cpic8xx_t    *cpi;  
static cpm8xx_t     *cp;   
static iop8xx_t     *iop;  
static cbd_t        *tbdf;
static cbd_t        *rbdf;

static  unsigned short r_tbase, r_rbase;

/* SPI Controller driver's private data. */
struct mpc8xx_spi {

	struct completion done;

        dma_addr_t scratchbuf;

        /* lock to avoid contemporaneous transmission */
        spinlock_t lock;

	unsigned int count;
	u32 irq;

	unsigned nsecs;		/* (clock cycle time)/2 */

	u32 inpclk;

        u16 mode; /* spi mode in hardware specific way */

	void (*activate_cs) (u8 cs, u8 polarity);
	void (*deactivate_cs) (u8 cs, u8 polarity);
                
};



static 
u16 mpc8xx_spi_hwmode(u8 mode, int len, u32 inpclk, u32 speedhz)
{
        /* DFBRG is zero */
        u16 modehw = SPMODE_MSTR ;
        
        /* setup spi mode */
        if (mode & SPI_CPOL)
            modehw |= SPMODE_CI;   
        if (mode & SPI_CPHA)
            modehw |= SPMODE_CP; 
        if (!(mode & SPI_LSB_FIRST))
            modehw |= SPMODE_REV;   
       
        modehw |= SPMODE_LEN(len); 

        /* Setting desired speed */
        modehw |= SPMODE_PM((u8)((inpclk/speedhz)/4-1));
                 
        return modehw;
}

static
int mpc8xx_transmit(u8* tx_buf, u8* rx_buf, int tx_size, int rx_size)
{
	int result, i;
	
        // copy data to be sent into the transmit buffer
	if (tx_buf)
          memcpy((void*)(txbuffer), (void*)tx_buf, tx_size);
    
        // BD data length register
        tbdf->cbd_datlen = rbdf->cbd_datlen = tx_size + rx_size;
    

        printk("SPI TX: ");
        for (i = 0; i < tx_size; i++)
          printk("%02X ", txbuffer[i]);
        printk("\n");

	 // BD status/control register
	tbdf->cbd_sc = BD_SC_READY | BD_SC_WRAP | BD_SC_LAST;
	rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP | BD_SC_INTRPT;

	cp->cp_spmode = 0x4678;   // spi mode setting
	cp->cp_spmode |= 0x0100;                // enable SPI
	cp->cp_spie   = 0xff;                   // clear all spi events
	cp->cp_spim   = 0x37;                   // mask all SPI events


	udelay(10); // Wait 5 microsecs

	cp->cp_spcom = 0x80;   // start the transfer

	udelay(100000);

	cp->cp_spmode = 0x00;   // reset spi mode

	udelay(10); // Wait 10 microsecs


	// test receive and transmit buffer descriptor for errors
	if (rbdf->cbd_sc & (BD_SC_EMPTY | BD_SC_OV | BD_SC_ME))
	{
        	result = -EIO;
	}
	if (tbdf->cbd_sc & (BD_SC_READY | BD_SC_UN | BD_SC_ME))
	{
        	result = -EIO;
	}
	
	// copy the received data into the receive buffer
     	if (rx_buf)
	  memcpy((void*)rx_buf, ((u8*)rxbuffer) + tx_size, rx_size);
	
	printk("SPI RX: ");
	for (i = 0; i < tx_size; i++)
	  printk("%02X ", *((u_char*)(((u_char*)rxbuffer) /*+ tx_size*/ + i)));
	printk("\n");

        return result;        
}

static
int mpc8xx_spi_transfer(struct spi_device *spi, struct spi_message *msg)
{
	struct mpc8xx_spi *mpc8xx_spi;
	struct spi_transfer	*t = NULL;
        int res = 0, i;
        
        printk("%s\n", __FUNCTION__);
                
	mpc8xx_spi = spi_master_get_devdata(spi->master);
      
        mpc8xx_spi->activate_cs(spi->chip_select, 0);
        udelay(5);

        //list_for_each_entry(t, &msg->transfers, transfer_list) 
        {
	   txbuffer[0] = j;
	   txbuffer[1] = j;
	   txbuffer[2] = j;
	   j++;
	   rxbuffer[0] = 0x55;
	   rxbuffer[1] = 0x55;
	   rxbuffer[2] = 0x55;
	   
	   mpc8xx_transmit(NULL, NULL, 3, 0);	   

	}

        mpc8xx_spi->deactivate_cs(spi->chip_select, 0);
        udelay(5);

	msg->status = -EMSGSIZE;
	msg->complete(msg->context);
        
	return res;
}


static int mpc8xx_spi_setup(struct spi_device *spi)
{
	struct mpc8xx_spi *mpc8xx_spi;

	if (!spi->max_speed_hz)
		return -EINVAL;

	mpc8xx_spi = spi_master_get_devdata(spi->master);


	if (!spi->bits_per_word)
		spi->bits_per_word = 8;

        
	dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec\n",
		__FUNCTION__, spi->mode & (SPI_CPOL | SPI_CPHA),
		spi->bits_per_word, 2 * mpc8xx_spi->nsecs);

               

	/* NOTE we _need_ to call chipselect() early, ideally with adapter
	 * setup, unless the hardware defaults cooperate to avoid confusion
	 * between normal (active low) and inverted chipselects.
	 */

	spin_lock(mpc8xx_spi->lock);
	
        mpc8xx_spi->deactivate_cs(spi->chip_select, 0);

        ndelay(mpc8xx_spi->nsecs);
	spin_unlock(mpc8xx_spi->lock);
        
	return 0;
}


irqreturn_t mpc8xx_spi_irq(s32 irq, void *context_data)
{
	struct mpc8xx_spi *mpc8xx_spi = context_data;
	u8 event;
        printk("%s\n", __FUNCTION__);
        event = cp->cp_spie;

        cp->cp_spie    = 0xff;      // reset spi event register
        cpi->cpic_cisr = 0x20;      // clear spi interrupt mask

        complete(&mpc8xx_spi->done);

	return IRQ_HANDLED;
}

static
void mpc8xx_spi_controller_init()
{
	u32 dp_addr;

        // get pointer to processors internal memory map
        immap = ioremap_nocache((immap_t *)IMAP_ADDR, 0x10000);
;
        //  printk("*** spi_read: immap = 0x%08x ***\n",(unsigned int)immap);

        // get pointer to cpm interrupt controller
        cpi = (cpic8xx_t*)&(((volatile immap_t*)immap)->im_cpic);
        //  printk("*** spi_read: cpi = 0x%08x ***\n",(unsigned int)cpi);

        // get pointer to input/output port
        iop = (iop8xx_t *)&(((volatile immap_t *)immap)->im_ioport);
        //  printk("*** spi_read: iop = 0x%08x ***\n",(unsigned int)iop);

        // get pointer to communication processor
        cp = (cpm8xx_t *)&(((volatile immap_t *)immap)->im_cpm);
        //  printk("*** spi_init: cp = 0x%08x ***\n",(unsigned int)cp);

        // get pointer to Serial Peripheral Interface parameter RAM
        spi = (spi_t *)&cp->cp_dparam[PROFF_SPI];
        //  printk("*** spi_init: spi = 0x%08x ***\n",(unsigned int)spi);

        // get pointer to clocks and reset
        carp = (car8xx_t *)&((volatile immap_t *)immap)->im_clkrst;
        //  printk("*** spi_init: carp = 0x%08x ***\n",(unsigned int)carp);

        // ------------------------------------------
        //  SCCR[DFBRG] = 0  -->  BRGCLK = vcoout/1         
        // ------------------------------------------
        carp->car_sccr &= 0xFFFFE7FF;

        //  initialize the parameter ram
        //  we need to make sure many things are initialized to zero
        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 = cpm_dpalloc(sizeof(cbd_t) * 2, 8);
        // printk("*** spi_init: dp_addr = 0x%08x ***\n",(unsigned int)dp_addr);

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

        // ***********IMPORTANT******************
        // setting transmit and receive buffer descriptor
        // pointers initially to rbase and tbase. 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 = spi->spi_rbase;
        spi->spi_tbptr = spi->spi_tbase;

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

        spi->spi_mrblr = SPI_MAX_BUFFER_SIZE;    // Set maximum receive size
        //  printk("*** spi_init: spi->spi_mrblr = 0x%04x ***\n",spi->spi_mrblr);

        cp->cp_cpcr |= 0x51;    // Setting CPCR

        immap->im_siu_conf.sc_sdcr = 0x0001;    // sets SDMA configuration register
        cpi->cpic_cicr |= 0x00000580;           // enable cpm interrupts, spi has highest priority
        cpi->cpic_cimr |= 0x00000020;           // enable spi interrupts

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

        //  printk("*** spi_init: iop->iop_pcdat = 0x%04x ***\n", iop->iop_pcdat);

        // ------------------------------------------------
        // initialize Port B SPI pins -> page 34-8 MPC860UM
        // (we are only in master mode !)
        // ------------------------------------------------

        // --------------------------------------------
        // GPIO or per. function
        // PBPAR[28] = 1  -> PERI: (SPIMISO)
        // PBPAR[29] = 1  -> PERI: (SPIMOSI)
        // PBPAR[30] = 1  -> PERI: (SPICLK)
        // --------------------------------------------
        cp->cp_pbpar |= 0x0000000E;  // set bits
       //  printk("*** spi_init: cp->cp_pbpar\n              ADR=0x%08x\n              VAL=0x%08x\n",(unsigned int)&cp->cp_pbpar, cp->cp_pbpar);

        // ----------------------------------------------
        // In/Out or per. function 0/1
        // PBDIR[28] = 1  -> PERI1: SPIMISO
        // PBDIR[29] = 1  -> PERI1: SPIMOSI
        // PBDIR[30] = 1  -> PERI1: SPICLK
        // ----------------------------------------------
        cp->cp_pbdir |= 0x0000000E;
        //printk("*** spi_init: cp->cp_pbdir\n              ADR=0x%08x\n              VAL=0x%08x\n",(unsigned int)&cp->cp_pbdir, cp->cp_pbdir);

        // -------------------------------------------------------------
        // attention: PBODR is a 32-bit-register,
        // but in the memory-map-struct it is handled as
        // 16-bit-register because lower 16 bit are always 0
        //
        // open drain or active output
        // PBODR[28] = 1  -> open drain: SPIMISO
        // PBODR[29] = 0  -> active output SPIMOSI
        // PBODR[30] = 0  -> active output: SPICLK
        // -------------------------------------------------------------
        //  printk("\n*** cp->cp_pbodr = 0x%08x ***\n",cp->cp_pbodr);
        cp->cp_pbodr |= 0x0008;  // set bits
        cp->cp_pbodr &= 0xFFF9;  // reset bits
        //  printk("*** spi_init: cp->cp_pbodr\n              ADR=0x%08x\n              VAL=0x%04x\n",(unsigned int)&cp->cp_pbodr, cp->cp_pbodr);

        //  printk("*** spi_init: cp->cp_pbdat\n              ADR=0x%08x\n              VAL=0x%08x\n",(unsigned int)&cp->cp_pbdat, cp->cp_pbdat);

        // tx and rx buffer descriptors
        tbdf = (cbd_t *)&cp->cp_dpmem[r_tbase];
        rbdf = (cbd_t *)&cp->cp_dpmem[r_rbase];

        // initialize tx and tx bd's
        tbdf->cbd_sc &= ~BD_SC_READY;
        rbdf->cbd_sc &= ~BD_SC_EMPTY;

}

static int __init mpc8xx_spi_probe(struct platform_device *dev)
{
	struct spi_master *master;
	struct mpc8xx_spi *mpc8xx_spi;
	struct fsl_spi_platform_data *pdata;
	struct fs_spi_platform_info *pinfo;
	dma_addr_t physaddr;        
	int status = 0;        

	int ret = 0;

        printk("%s\n", __FUNCTION__);
	/* Get resources(memory, IRQ) associated with the device */
	master = spi_alloc_master(&dev->dev, sizeof(struct mpc8xx_spi));

	if (master == NULL) {
		ret = -ENOMEM;
		goto err;
	}

	platform_set_drvdata(dev, master);
	pinfo = dev->dev.platform_data;

	if (pinfo == NULL) {
		ret = -ENODEV;
		goto free_master;
	}
        
        /* initialize ioports */
	if (pinfo->init_ioports)
		pinfo->init_ioports(pinfo);

        pdata = pinfo->pdata;
        
	mpc8xx_spi = spi_master_get_devdata(master);

        
	mpc8xx_spi->inpclk = pdata->inpclk;
	mpc8xx_spi->activate_cs   = spi_activate_cs;
	mpc8xx_spi->deactivate_cs = spi_deactivate_cs;

	master->transfer = mpc8xx_spi_transfer;
        master->setup = mpc8xx_spi_setup;
        
	init_completion(&mpc8xx_spi->done);


	master->bus_num = pdata->bus_num;
	master->num_chipselect = pdata->max_chipselect;

	mpc8xx_spi_controller_init();
	
	status = spi_register_master(master);
	if (status != 0) {
		printk("problem registering spi master\n");
		goto free_irq;
	}

	mpc8xx_spi->irq = platform_get_irq(dev, 0);

	if (mpc8xx_spi->irq < 0) {
		ret = -ENXIO;
		goto put_master;
	}
                
	printk(KERN_INFO
	       "%s: MPC8xx SPI Controller driver at 0x%p (irq = %d)\n",
	       dev->dev.bus_id, spi, mpc8xx_spi->irq);

        

	/* Register for SPI Interrupt */
	ret = request_irq(mpc8xx_spi->irq, mpc8xx_spi_irq,
			  0, "mpc8xx_spi", mpc8xx_spi);

	if (ret != 0)
		goto free_irq;

    
        dma_alloc_coherent(NULL, PAGE_SIZE, &(mpc8xx_spi->scratchbuf), GFP_KERNEL);
	rxbuffer = __get_free_page(GFP_KERNEL | GFP_DMA);
	txbuffer = rxbuffer + 32;

        tbdf->cbd_bufaddr = __pa(txbuffer); 
        rbdf->cbd_bufaddr = __pa(rxbuffer);         
        
	return ret;

free_irq:
	free_irq(mpc8xx_spi->irq, mpc8xx_spi);
put_master:
	spi_master_put(master);
free_master:
	kfree(master);
err:
	return ret;
}

static int __devexit mpc8xx_spi_remove(struct platform_device *dev)
{
	struct mpc8xx_spi *mpc8xx_spi;
	struct spi_master *master;

	master = platform_get_drvdata(dev);
	mpc8xx_spi = spi_master_get_devdata(master);

	free_irq(mpc8xx_spi->irq, mpc8xx_spi);
	spi_master_put(master);

	return 0;
}

static struct platform_driver mpc8xx_spi_driver = {
	.probe = mpc8xx_spi_probe,
	.remove = __devexit_p(mpc8xx_spi_remove),
	.driver = {
		   .name = "fsl-cpm-spi",
	},
};

static int __init mpc8xx_spi_init(void)
{
	return platform_driver_register(&mpc8xx_spi_driver);
}

static void __exit mpc8xx_spi_exit(void)
{
	platform_driver_unregister(&mpc8xx_spi_driver);
}

module_init(mpc8xx_spi_init);
module_exit(mpc8xx_spi_exit);

MODULE_AUTHOR("Melinda Develey");
MODULE_DESCRIPTION("Simple MPC8xx SPI Driver");
MODULE_LICENSE("GPL");
_______________________________________________
Linuxppc-embedded mailing list
Linuxppc-embedded@ozlabs.org
https://ozlabs.org/mailman/listinfo/linuxppc-embedded

Reply via email to