spi_bfin5xx.c

来自「linux 内核源代码」· C语言 代码 · 共 1,489 行 · 第 1/3 页

C
1,489
字号
/* * File:	drivers/spi/bfin5xx_spi.c * Maintainer: *		Bryan Wu <bryan.wu@analog.com> * Original Author: *		Luke Yang (Analog Devices Inc.) * * Created:	March. 10th 2006 * Description:	SPI controller driver for Blackfin BF5xx * Bugs:	Enter bugs at http://blackfin.uclinux.org/ * * Modified: *	March 10, 2006  bfin5xx_spi.c Created. (Luke Yang) *      August 7, 2006  added full duplex mode (Axel Weiss & Luke Yang) *      July  17, 2007  add support for BF54x SPI0 controller (Bryan Wu) *      July  30, 2007  add platfrom_resource interface to support multi-port *                      SPI controller (Bryan Wu) * * Copyright 2004-2007 Analog Devices 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, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY ;  without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program ;  see the file COPYING. * If not, write to the Free Software Foundation, * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <linux/init.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/device.h>#include <linux/io.h>#include <linux/ioport.h>#include <linux/irq.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/spi/spi.h>#include <linux/workqueue.h>#include <asm/dma.h>#include <asm/portmux.h>#include <asm/bfin5xx_spi.h>#define DRV_NAME	"bfin-spi"#define DRV_AUTHOR	"Bryan Wu, Luke Yang"#define DRV_DESC	"Blackfin BF5xx on-chip SPI Controller Driver"#define DRV_VERSION	"1.0"MODULE_AUTHOR(DRV_AUTHOR);MODULE_DESCRIPTION(DRV_DESC);MODULE_LICENSE("GPL");#define IS_DMA_ALIGNED(x) (((u32)(x)&0x07) == 0)#define START_STATE	((void *)0)#define RUNNING_STATE	((void *)1)#define DONE_STATE	((void *)2)#define ERROR_STATE	((void *)-1)#define QUEUE_RUNNING	0#define QUEUE_STOPPED	1struct driver_data {	/* Driver model hookup */	struct platform_device *pdev;	/* SPI framework hookup */	struct spi_master *master;	/* Regs base of SPI controller */	void __iomem *regs_base;	/* Pin request list */	u16 *pin_req;	/* BFIN hookup */	struct bfin5xx_spi_master *master_info;	/* Driver message queue */	struct workqueue_struct *workqueue;	struct work_struct pump_messages;	spinlock_t lock;	struct list_head queue;	int busy;	int run;	/* Message Transfer pump */	struct tasklet_struct pump_transfers;	/* Current message transfer state info */	struct spi_message *cur_msg;	struct spi_transfer *cur_transfer;	struct chip_data *cur_chip;	size_t len_in_bytes;	size_t len;	void *tx;	void *tx_end;	void *rx;	void *rx_end;	/* DMA stuffs */	int dma_channel;	int dma_mapped;	int dma_requested;	dma_addr_t rx_dma;	dma_addr_t tx_dma;	size_t rx_map_len;	size_t tx_map_len;	u8 n_bytes;	int cs_change;	void (*write) (struct driver_data *);	void (*read) (struct driver_data *);	void (*duplex) (struct driver_data *);};struct chip_data {	u16 ctl_reg;	u16 baud;	u16 flag;	u8 chip_select_num;	u8 n_bytes;	u8 width;		/* 0 or 1 */	u8 enable_dma;	u8 bits_per_word;	/* 8 or 16 */	u8 cs_change_per_word;	u16 cs_chg_udelay;	/* Some devices require > 255usec delay */	void (*write) (struct driver_data *);	void (*read) (struct driver_data *);	void (*duplex) (struct driver_data *);};#define DEFINE_SPI_REG(reg, off) \static inline u16 read_##reg(struct driver_data *drv_data) \	{ return bfin_read16(drv_data->regs_base + off); } \static inline void write_##reg(struct driver_data *drv_data, u16 v) \	{ bfin_write16(drv_data->regs_base + off, v); }DEFINE_SPI_REG(CTRL, 0x00)DEFINE_SPI_REG(FLAG, 0x04)DEFINE_SPI_REG(STAT, 0x08)DEFINE_SPI_REG(TDBR, 0x0C)DEFINE_SPI_REG(RDBR, 0x10)DEFINE_SPI_REG(BAUD, 0x14)DEFINE_SPI_REG(SHAW, 0x18)static void bfin_spi_enable(struct driver_data *drv_data){	u16 cr;	cr = read_CTRL(drv_data);	write_CTRL(drv_data, (cr | BIT_CTL_ENABLE));}static void bfin_spi_disable(struct driver_data *drv_data){	u16 cr;	cr = read_CTRL(drv_data);	write_CTRL(drv_data, (cr & (~BIT_CTL_ENABLE)));}/* Caculate the SPI_BAUD register value based on input HZ */static u16 hz_to_spi_baud(u32 speed_hz){	u_long sclk = get_sclk();	u16 spi_baud = (sclk / (2 * speed_hz));	if ((sclk % (2 * speed_hz)) > 0)		spi_baud++;	return spi_baud;}static int flush(struct driver_data *drv_data){	unsigned long limit = loops_per_jiffy << 1;	/* wait for stop and clear stat */	while (!(read_STAT(drv_data) & BIT_STAT_SPIF) && limit--)		cpu_relax();	write_STAT(drv_data, BIT_STAT_CLR);	return limit;}/* Chip select operation functions for cs_change flag */static void cs_active(struct driver_data *drv_data, struct chip_data *chip){	u16 flag = read_FLAG(drv_data);	flag |= chip->flag;	flag &= ~(chip->flag << 8);	write_FLAG(drv_data, flag);}static void cs_deactive(struct driver_data *drv_data, struct chip_data *chip){	u16 flag = read_FLAG(drv_data);	flag |= (chip->flag << 8);	write_FLAG(drv_data, flag);	/* Move delay here for consistency */	if (chip->cs_chg_udelay)		udelay(chip->cs_chg_udelay);}#define MAX_SPI_SSEL	7/* stop controller and re-config current chip*/static int restore_state(struct driver_data *drv_data){	struct chip_data *chip = drv_data->cur_chip;	int ret = 0;	/* Clear status and disable clock */	write_STAT(drv_data, BIT_STAT_CLR);	bfin_spi_disable(drv_data);	dev_dbg(&drv_data->pdev->dev, "restoring spi ctl state\n");	/* Load the registers */	write_CTRL(drv_data, chip->ctl_reg);	write_BAUD(drv_data, chip->baud);	bfin_spi_enable(drv_data);	cs_active(drv_data, chip);	if (ret)		dev_dbg(&drv_data->pdev->dev,			": request chip select number %d failed\n",			chip->chip_select_num);	return ret;}/* used to kick off transfer in rx mode */static unsigned short dummy_read(struct driver_data *drv_data){	unsigned short tmp;	tmp = read_RDBR(drv_data);	return tmp;}static void null_writer(struct driver_data *drv_data){	u8 n_bytes = drv_data->n_bytes;	while (drv_data->tx < drv_data->tx_end) {		write_TDBR(drv_data, 0);		while ((read_STAT(drv_data) & BIT_STAT_TXS))			cpu_relax();		drv_data->tx += n_bytes;	}}static void null_reader(struct driver_data *drv_data){	u8 n_bytes = drv_data->n_bytes;	dummy_read(drv_data);	while (drv_data->rx < drv_data->rx_end) {		while (!(read_STAT(drv_data) & BIT_STAT_RXS))			cpu_relax();		dummy_read(drv_data);		drv_data->rx += n_bytes;	}}static void u8_writer(struct driver_data *drv_data){	dev_dbg(&drv_data->pdev->dev,		"cr8-s is 0x%x\n", read_STAT(drv_data));	/* poll for SPI completion before start */	while (!(read_STAT(drv_data) & BIT_STAT_SPIF))		cpu_relax();	while (drv_data->tx < drv_data->tx_end) {		write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));		while (read_STAT(drv_data) & BIT_STAT_TXS)			cpu_relax();		++drv_data->tx;	}}static void u8_cs_chg_writer(struct driver_data *drv_data){	struct chip_data *chip = drv_data->cur_chip;	/* poll for SPI completion before start */	while (!(read_STAT(drv_data) & BIT_STAT_SPIF))		cpu_relax();	while (drv_data->tx < drv_data->tx_end) {		cs_active(drv_data, chip);		write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));		while (read_STAT(drv_data) & BIT_STAT_TXS)			cpu_relax();		cs_deactive(drv_data, chip);		++drv_data->tx;	}}static void u8_reader(struct driver_data *drv_data){	dev_dbg(&drv_data->pdev->dev,		"cr-8 is 0x%x\n", read_STAT(drv_data));	/* poll for SPI completion before start */	while (!(read_STAT(drv_data) & BIT_STAT_SPIF))		cpu_relax();	/* clear TDBR buffer before read(else it will be shifted out) */	write_TDBR(drv_data, 0xFFFF);	dummy_read(drv_data);	while (drv_data->rx < drv_data->rx_end - 1) {		while (!(read_STAT(drv_data) & BIT_STAT_RXS))			cpu_relax();		*(u8 *) (drv_data->rx) = read_RDBR(drv_data);		++drv_data->rx;	}	while (!(read_STAT(drv_data) & BIT_STAT_RXS))		cpu_relax();	*(u8 *) (drv_data->rx) = read_SHAW(drv_data);	++drv_data->rx;}static void u8_cs_chg_reader(struct driver_data *drv_data){	struct chip_data *chip = drv_data->cur_chip;	/* poll for SPI completion before start */	while (!(read_STAT(drv_data) & BIT_STAT_SPIF))		cpu_relax();	/* clear TDBR buffer before read(else it will be shifted out) */	write_TDBR(drv_data, 0xFFFF);	cs_active(drv_data, chip);	dummy_read(drv_data);	while (drv_data->rx < drv_data->rx_end - 1) {		cs_deactive(drv_data, chip);		while (!(read_STAT(drv_data) & BIT_STAT_RXS))			cpu_relax();		cs_active(drv_data, chip);		*(u8 *) (drv_data->rx) = read_RDBR(drv_data);		++drv_data->rx;	}	cs_deactive(drv_data, chip);	while (!(read_STAT(drv_data) & BIT_STAT_RXS))		cpu_relax();	*(u8 *) (drv_data->rx) = read_SHAW(drv_data);	++drv_data->rx;}static void u8_duplex(struct driver_data *drv_data){	/* poll for SPI completion before start */	while (!(read_STAT(drv_data) & BIT_STAT_SPIF))		cpu_relax();	/* in duplex mode, clk is triggered by writing of TDBR */	while (drv_data->rx < drv_data->rx_end) {		write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));		while (read_STAT(drv_data) & BIT_STAT_TXS)			cpu_relax();		while (!(read_STAT(drv_data) & BIT_STAT_RXS))			cpu_relax();		*(u8 *) (drv_data->rx) = read_RDBR(drv_data);		++drv_data->rx;		++drv_data->tx;	}}static void u8_cs_chg_duplex(struct driver_data *drv_data){	struct chip_data *chip = drv_data->cur_chip;	/* poll for SPI completion before start */	while (!(read_STAT(drv_data) & BIT_STAT_SPIF))		cpu_relax();	while (drv_data->rx < drv_data->rx_end) {		cs_active(drv_data, chip);		write_TDBR(drv_data, (*(u8 *) (drv_data->tx)));		while (read_STAT(drv_data) & BIT_STAT_TXS)			cpu_relax();		while (!(read_STAT(drv_data) & BIT_STAT_RXS))			cpu_relax();		*(u8 *) (drv_data->rx) = read_RDBR(drv_data);		cs_deactive(drv_data, chip);		++drv_data->rx;		++drv_data->tx;	}}static void u16_writer(struct driver_data *drv_data){	dev_dbg(&drv_data->pdev->dev,		"cr16 is 0x%x\n", read_STAT(drv_data));	/* poll for SPI completion before start */	while (!(read_STAT(drv_data) & BIT_STAT_SPIF))		cpu_relax();	while (drv_data->tx < drv_data->tx_end) {		write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));		while ((read_STAT(drv_data) & BIT_STAT_TXS))			cpu_relax();		drv_data->tx += 2;	}}static void u16_cs_chg_writer(struct driver_data *drv_data){	struct chip_data *chip = drv_data->cur_chip;	/* poll for SPI completion before start */	while (!(read_STAT(drv_data) & BIT_STAT_SPIF))		cpu_relax();	while (drv_data->tx < drv_data->tx_end) {		cs_active(drv_data, chip);		write_TDBR(drv_data, (*(u16 *) (drv_data->tx)));		while ((read_STAT(drv_data) & BIT_STAT_TXS))			cpu_relax();		cs_deactive(drv_data, chip);		drv_data->tx += 2;	}}static void u16_reader(struct driver_data *drv_data){	dev_dbg(&drv_data->pdev->dev,		"cr-16 is 0x%x\n", read_STAT(drv_data));	/* poll for SPI completion before start */	while (!(read_STAT(drv_data) & BIT_STAT_SPIF))		cpu_relax();	/* clear TDBR buffer before read(else it will be shifted out) */	write_TDBR(drv_data, 0xFFFF);	dummy_read(drv_data);	while (drv_data->rx < (drv_data->rx_end - 2)) {		while (!(read_STAT(drv_data) & BIT_STAT_RXS))			cpu_relax();		*(u16 *) (drv_data->rx) = read_RDBR(drv_data);		drv_data->rx += 2;	}	while (!(read_STAT(drv_data) & BIT_STAT_RXS))		cpu_relax();	*(u16 *) (drv_data->rx) = read_SHAW(drv_data);	drv_data->rx += 2;}static void u16_cs_chg_reader(struct driver_data *drv_data){	struct chip_data *chip = drv_data->cur_chip;	/* poll for SPI completion before start */	while (!(read_STAT(drv_data) & BIT_STAT_SPIF))		cpu_relax();	/* clear TDBR buffer before read(else it will be shifted out) */

⌨️ 快捷键说明

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