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 + -
显示快捷键?