⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cpm_spi.c.bak

📁 一个非常有用的SPI驱动程序,是在LINUX平台下的,但也可以一直到其他平台
💻 BAK
字号:
/*
 * Simple CPM SPI interface for the MPC 8260.
 *
 * based on the version for MPC860 by:
 * 
 * Copyright (c) 2002 Wolfgang Grandegger (wg@denx.de)
 *
 * This interface is partially derived from code copyrighted
 * by Navin Boppuri (nboppuri@trinetcommunication.co) and
 * Prashant Patel (pmpatel@trinetcommunication.com).
 *
 * This driver implements the function "cpm_spi_io()" to be 
 * used by other drivers and a simple read/write interface 
 * for user-land applications. The latter is mainly useful 
 * for debugging purposes. Some further remarks:
 *
 * - Board specific definitions and code should go into
 *   the file "cpm_spi.h".
 *
 * - For the moment, no interrupts are used. This be useful 
 *   for (very) long transfers.
 */

#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/immap_8260.h>
#include <asm/mpc8260.h>

#include <asm/cpm_8260.h>
#include <asm/cpm_spi.h>

#define DRIVER_NAME "cpm_spi"

MODULE_AUTHOR("Wolfgang Grandegger (wg@denx.de)");
MODULE_DESCRIPTION("Simple Driver for the CPM SPI");

#undef DEBUG
#ifdef DEBUG
# define debugk(fmt,args...)    printk(fmt ,##args)
#else
# define debugk(fmt,args...)
#endif

#define CPM_SPI_MAJOR 65	/* "borrowed" from "plink" driver */

static volatile immap_t *immap = (immap_t *)IMAP_ADDR;

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

extern void invalidate_dcache_range(unsigned long start, unsigned long end);

#if CPM_SPI_SWAP_BYTES
static void swap_bytes(u_char *buf, int len)
{
	u_short *sbuf = (u_short *)buf;
	while (len > 0) {
		*sbuf = cpu_to_le16(*sbuf);
		sbuf++; 
		len -= 2;
	}
}
#else
#define swap_bytes(buf, len)
#endif

/*
 * CPM SPI Kernel API function(s)
 */
ssize_t cpm_spi_io (int chip_id, int serial,
		    u_char *tx_buffer, int tx_size, 
		    u_char *rx_buffer, int rx_size)
{
	unsigned long flags;
	int i;

	/* 
	 * Serialize access to the SPI. We have to disable interrupts
	 * because we may need to call it from interrupt handlers.
	 */
	save_flags(flags); cli();

	rx_bdf->cbd_datlen = 0;
	if (serial)
		tx_bdf->cbd_datlen = tx_size + rx_size;
	else
		tx_bdf->cbd_datlen = tx_size > rx_size ? tx_size : rx_size;
	if (tx_bdf->cbd_datlen > CONFIG_CPM_SPI_BDSIZE) {
		printk("cpm_spi_io: Invalid size\n");
		restore_flags(flags);
		return -EINVAL;
	}
	if (tx_size > 0) {
		memcpy(tx_buf, tx_buffer, tx_size);
#ifdef DEBUG
		printk("Tx:");
		for (i = 0; i < tx_size; i++)
			printk(" %02x", tx_buf[i]);
		printk("\n");
#endif
		swap_bytes(tx_buf, tx_size);
	}

	flush_dcache_range((unsigned long) tx_buf, 
			   (unsigned long) (tx_buf+CONFIG_CPM_SPI_BDSIZE-1));
	flush_dcache_range((unsigned long) rx_buf, 
			   (unsigned long) (rx_buf+CONFIG_CPM_SPI_BDSIZE-1));
	invalidate_dcache_range((unsigned long) rx_buf, 
				(unsigned long) (rx_buf+
						 CONFIG_CPM_SPI_BDSIZE-1));

	/* Setting Rx and Tx BD status and data length */
	tx_bdf->cbd_sc = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP;
	rx_bdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP;

	/* Chip select for device */
	cpm_spi_set_cs(immap, chip_id, 1);

	/* Start SPI Tx/Rx transfer */
	immap->im_spi.spi_spcom |= 0x80;

	/* 
	 * Wait until the Tx/Rx transfer is done. 
	 */
	for (i = 0; i < CPM_SPI_POLL_RETRIES; i++) {
                udelay(5);
		if ((tx_bdf->cbd_sc & BD_SC_EMPTY) == 0 && 
		    (rx_bdf->cbd_sc & BD_SC_EMPTY) == 0)
			break;
        }

	/* De-select device */
	cpm_spi_set_cs(immap, chip_id, 0);

	/* Check for timeout */
	if (i == CPM_SPI_POLL_RETRIES) {
		printk("cpm_spi_io: Tx/Rx transfer timeout\n");
		restore_flags(flags);
		return -EIO;
	}

#ifdef DEBUG
	printk("Transfer time approx. %d us\n", i*5);
	if (rx_bdf->cbd_datlen > 0) {
		printk("Rx:");
		for (i = 0; i < rx_bdf->cbd_datlen; i++)
			printk(" %02x", rx_buf[i]);
		printk("\n");
	}
#endif

	/* Copy receive data if appropriate */
	i = rx_bdf->cbd_datlen;
	swap_bytes(rx_buf, i);
	if (rx_size > 0) {
		if (serial) {
			i -= tx_size;
			if (i != rx_size)
				printk("i=%d rx_size=%d\n", i, rx_size);
			memcpy(rx_buffer, rx_buf + tx_size, i);
		} else {
			if (i != (rx_size > rx_size ? tx_size : rx_size))
				printk("i=%d rx_size=%d tx_size=%d\n", 
				       i, tx_size, rx_size);
			memcpy(rx_buffer, rx_buf, i);
		}
	}
	
	restore_flags(flags);
	
	return i;
}

EXPORT_SYMBOL(cpm_spi_io);

/*
 * Prototypes for driver entry functions.
 */
static int
cpm_spi_open(struct inode *inode, struct file *filp);
static int
cpm_spi_release(struct inode *inode, struct file *filp);
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 = {
	owner:   THIS_MODULE,
	open:    cpm_spi_open,
	release: cpm_spi_release,
	read:    cpm_spi_read,
	write:   cpm_spi_write,
};


static int 
cpm_spi_open (struct inode *inode, struct file *filp)
{
	int minor = MINOR(inode->i_rdev);

	if (minor >= CPM_SPI_MAX_CHIPS)
		return -ENODEV;

	filp->private_data = (void *)minor;
	MOD_INC_USE_COUNT;
	return 0;
}

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

static ssize_t 
cpm_spi_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
	u_char tx_kbuf[CONFIG_CPM_SPI_BDSIZE];
	u_char rx_kbuf[CONFIG_CPM_SPI_BDSIZE];
	int chip_id = (int)filp->private_data;
	ssize_t size;
	
	debugk(__FUNCTION__ ": count=%d, chip_id=%d\n", count, chip_id);

	if (count > CONFIG_CPM_SPI_BDSIZE)
		return -ENXIO;
	
	if (copy_from_user(tx_kbuf, buf, count))
		return -EFAULT;
	
	size = cpm_spi_io(chip_id, 0, tx_kbuf, count, rx_kbuf, count);
	if (size < 0)
		return size;
	
	if (copy_to_user(buf, rx_kbuf, size))
		return -EFAULT;
	
	return size;
}

static ssize_t 
cpm_spi_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
	int chip_id = (int)filp->private_data;
	u_char tx_kbuf[CONFIG_CPM_SPI_BDSIZE];

	if (count > CONFIG_CPM_SPI_BDSIZE)
		return -ENXIO;

	if (copy_from_user(tx_kbuf, buf, count))
		return -EFAULT;

	return cpm_spi_io(chip_id, 0, tx_kbuf, count, NULL, 0);
}


static int 
cpm_spi_init(void)
{
	int err = 0;
        volatile spi_t    *spi;

        printk (KERN_INFO "CPM SPI Driver: $Revision: 1.0 $ wd@denx.de\n");

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

	*(ushort *)(&immap->im_dprambase[PROFF_SPI_BASE]) = PROFF_SPI;
	spi = (spi_t *)&immap->im_dprambase[PROFF_SPI];   

        /* 
	 * 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_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 = m8260_cpm_dpalloc(sizeof(cbd_t) * 2, 8);
        if (dp_addr == CPM_DP_NOSPACE) {
		printk("cpm_spi: m8260_cpm_dpalloc() failed\n");
                return -ENOMEM;
        }

        /* 
	 * 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);

	/*
	 * Setting transmit and receive buffer descriptor pointers
	 * intially to rbase and rbase. 
	 */
        spi->spi_rbptr = spi->spi_rbase;
        spi->spi_tbptr = spi->spi_tbase;
	
        /* Setting CPCR */
        cpmp->cp_cpcr |= 
		mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK, 
			  0, CPM_CR_INIT_TRX) | CPM_CR_FLG;
        while(cpmp->cp_cpcr & CPM_CR_FLG) ;

        /* Set to big endian. */
        spi->spi_tfcr = CPMFCR_EB;
        spi->spi_rfcr = CPMFCR_EB;

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

	/* 
	 * Clear all pending SPI events and mask all possible SPI
	 * interrupts. For the moment we don't use interrupts.
	 */
	immap->im_spi.spi_spie = 0xff;
	immap->im_spi.spi_spim = 0x00;

	/* 
	 * Set SPI Mode register. 
	 * Note: the board-specific definitions are in cpm_spi.h. 
	 */
        immap->im_spi.spi_spmode = CPM_SPI_SPMODE | SPMODE_EN;

	/* 
	 * Initiliaze port pins for SPI
	 *                  par dir odr
	 * PD16 -> SPIMISO:  1   0   1
	 * PD17 -> SPIMOSI:  1   0   1
	 * PD18 -> SPICLK :  1   0   1
	 */
	immap->im_ioport.iop_ppard |=  0x0000e000;
	immap->im_ioport.iop_psord |=  0x0000e000;
	immap->im_ioport.iop_pdird &= ~0x0000e000;

	/* 
	 * Initialize board-specific port for chip select etc.
	 * Note: the board-specific definitions are in cpm_spi.h. 
	 */
	cpm_spi_init_ports(immap);

	/* Tx and Rx buffer descriptors. */
	tx_bdf = (cbd_t *)&immap->im_dprambase[r_tbase];
	rx_bdf = (cbd_t *)&immap->im_dprambase[r_rbase];

	/* 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;

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

	/* Set the bd's rx and tx buffer address pointers */
	tx_bdf->cbd_bufaddr = __pa(tx_buf);
	rx_bdf->cbd_bufaddr = __pa(rx_buf);

	/* 
	 * Finally register the driver.
	 */
	err = register_chrdev(CPM_SPI_MAJOR, DRIVER_NAME, &cpm_spi_fops);
	if (err < 0) {
		printk("cpm_spi: Couldn't register driver (major=%d)\n", 
		       CPM_SPI_MAJOR);
		return err;
	}

	return 0;
}

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

module_init(cpm_spi_init);
module_exit(cpm_spi_cleanup);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -