pxa2xx_spi.c

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

C
1,641
字号
/* * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs * * 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. * * 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; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/init.h>#include <linux/module.h>#include <linux/device.h>#include <linux/ioport.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 <linux/delay.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/hardware.h>#include <asm/delay.h>#include <asm/dma.h>#include <asm/arch/hardware.h>#include <asm/arch/pxa-regs.h>#include <asm/arch/pxa2xx_spi.h>MODULE_AUTHOR("Stephen Street");MODULE_DESCRIPTION("PXA2xx SSP SPI Controller");MODULE_LICENSE("GPL");#define MAX_BUSES 3#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR)#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK)#define IS_DMA_ALIGNED(x) (((u32)(x)&0x07)==0)/* for testing SSCR1 changes that require SSP restart, basically * everything except the service and interrupt enables */#define SSCR1_CHANGE_MASK (SSCR1_TTELP | SSCR1_TTE | SSCR1_EBCEI | SSCR1_SCFR \				| SSCR1_ECRA | SSCR1_ECRB | SSCR1_SCLKDIR \				| SSCR1_RWOT | SSCR1_TRAIL | SSCR1_PINTE \				| SSCR1_STRF | SSCR1_EFWR |SSCR1_RFT \				| SSCR1_TFT | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)#define DEFINE_SSP_REG(reg, off) \static inline u32 read_##reg(void *p) { return __raw_readl(p + (off)); } \static inline void write_##reg(u32 v, void *p) { __raw_writel(v, p + (off)); }DEFINE_SSP_REG(SSCR0, 0x00)DEFINE_SSP_REG(SSCR1, 0x04)DEFINE_SSP_REG(SSSR, 0x08)DEFINE_SSP_REG(SSITR, 0x0c)DEFINE_SSP_REG(SSDR, 0x10)DEFINE_SSP_REG(SSTO, 0x28)DEFINE_SSP_REG(SSPSP, 0x2c)#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 */	enum pxa_ssp_type ssp_type;	struct spi_master *master;	/* PXA hookup */	struct pxa2xx_spi_master *master_info;	/* DMA setup stuff */	int rx_channel;	int tx_channel;	u32 *null_dma_buf;	/* SSP register addresses */	void *ioaddr;	u32 ssdr_physical;	/* SSP masks*/	u32 dma_cr1;	u32 int_cr1;	u32 clear_sr;	u32 mask_sr;	/* 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;	void *tx;	void *tx_end;	void *rx;	void *rx_end;	int dma_mapped;	dma_addr_t rx_dma;	dma_addr_t tx_dma;	size_t rx_map_len;	size_t tx_map_len;	u8 n_bytes;	u32 dma_width;	int cs_change;	int (*write)(struct driver_data *drv_data);	int (*read)(struct driver_data *drv_data);	irqreturn_t (*transfer_handler)(struct driver_data *drv_data);	void (*cs_control)(u32 command);};struct chip_data {	u32 cr0;	u32 cr1;	u32 psp;	u32 timeout;	u8 n_bytes;	u32 dma_width;	u32 dma_burst_size;	u32 threshold;	u32 dma_threshold;	u8 enable_dma;	u8 bits_per_word;	u32 speed_hz;	int (*write)(struct driver_data *drv_data);	int (*read)(struct driver_data *drv_data);	void (*cs_control)(u32 command);};static void pump_messages(struct work_struct *work);static int flush(struct driver_data *drv_data){	unsigned long limit = loops_per_jiffy << 1;	void *reg = drv_data->ioaddr;	do {		while (read_SSSR(reg) & SSSR_RNE) {			read_SSDR(reg);		}	} while ((read_SSSR(reg) & SSSR_BSY) && limit--);	write_SSSR(SSSR_ROR, reg);	return limit;}static void null_cs_control(u32 command){}static int null_writer(struct driver_data *drv_data){	void *reg = drv_data->ioaddr;	u8 n_bytes = drv_data->n_bytes;	if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00)		|| (drv_data->tx == drv_data->tx_end))		return 0;	write_SSDR(0, reg);	drv_data->tx += n_bytes;	return 1;}static int null_reader(struct driver_data *drv_data){	void *reg = drv_data->ioaddr;	u8 n_bytes = drv_data->n_bytes;	while ((read_SSSR(reg) & SSSR_RNE)		&& (drv_data->rx < drv_data->rx_end)) {		read_SSDR(reg);		drv_data->rx += n_bytes;	}	return drv_data->rx == drv_data->rx_end;}static int u8_writer(struct driver_data *drv_data){	void *reg = drv_data->ioaddr;	if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00)		|| (drv_data->tx == drv_data->tx_end))		return 0;	write_SSDR(*(u8 *)(drv_data->tx), reg);	++drv_data->tx;	return 1;}static int u8_reader(struct driver_data *drv_data){	void *reg = drv_data->ioaddr;	while ((read_SSSR(reg) & SSSR_RNE)		&& (drv_data->rx < drv_data->rx_end)) {		*(u8 *)(drv_data->rx) = read_SSDR(reg);		++drv_data->rx;	}	return drv_data->rx == drv_data->rx_end;}static int u16_writer(struct driver_data *drv_data){	void *reg = drv_data->ioaddr;	if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00)		|| (drv_data->tx == drv_data->tx_end))		return 0;	write_SSDR(*(u16 *)(drv_data->tx), reg);	drv_data->tx += 2;	return 1;}static int u16_reader(struct driver_data *drv_data){	void *reg = drv_data->ioaddr;	while ((read_SSSR(reg) & SSSR_RNE)		&& (drv_data->rx < drv_data->rx_end)) {		*(u16 *)(drv_data->rx) = read_SSDR(reg);		drv_data->rx += 2;	}	return drv_data->rx == drv_data->rx_end;}static int u32_writer(struct driver_data *drv_data){	void *reg = drv_data->ioaddr;	if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00)		|| (drv_data->tx == drv_data->tx_end))		return 0;	write_SSDR(*(u32 *)(drv_data->tx), reg);	drv_data->tx += 4;	return 1;}static int u32_reader(struct driver_data *drv_data){	void *reg = drv_data->ioaddr;	while ((read_SSSR(reg) & SSSR_RNE)		&& (drv_data->rx < drv_data->rx_end)) {		*(u32 *)(drv_data->rx) = read_SSDR(reg);		drv_data->rx += 4;	}	return drv_data->rx == drv_data->rx_end;}static void *next_transfer(struct driver_data *drv_data){	struct spi_message *msg = drv_data->cur_msg;	struct spi_transfer *trans = drv_data->cur_transfer;	/* Move to next transfer */	if (trans->transfer_list.next != &msg->transfers) {		drv_data->cur_transfer =			list_entry(trans->transfer_list.next,					struct spi_transfer,					transfer_list);		return RUNNING_STATE;	} else		return DONE_STATE;}static int map_dma_buffers(struct driver_data *drv_data){	struct spi_message *msg = drv_data->cur_msg;	struct device *dev = &msg->spi->dev;	if (!drv_data->cur_chip->enable_dma)		return 0;	if (msg->is_dma_mapped)		return  drv_data->rx_dma && drv_data->tx_dma;	if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx))		return 0;	/* Modify setup if rx buffer is null */	if (drv_data->rx == NULL) {		*drv_data->null_dma_buf = 0;		drv_data->rx = drv_data->null_dma_buf;		drv_data->rx_map_len = 4;	} else		drv_data->rx_map_len = drv_data->len;	/* Modify setup if tx buffer is null */	if (drv_data->tx == NULL) {		*drv_data->null_dma_buf = 0;		drv_data->tx = drv_data->null_dma_buf;		drv_data->tx_map_len = 4;	} else		drv_data->tx_map_len = drv_data->len;	/* Stream map the rx buffer */	drv_data->rx_dma = dma_map_single(dev, drv_data->rx,						drv_data->rx_map_len,						DMA_FROM_DEVICE);	if (dma_mapping_error(drv_data->rx_dma))		return 0;	/* Stream map the tx buffer */	drv_data->tx_dma = dma_map_single(dev, drv_data->tx,						drv_data->tx_map_len,						DMA_TO_DEVICE);	if (dma_mapping_error(drv_data->tx_dma)) {		dma_unmap_single(dev, drv_data->rx_dma,					drv_data->rx_map_len, DMA_FROM_DEVICE);		return 0;	}	return 1;}static void unmap_dma_buffers(struct driver_data *drv_data){	struct device *dev;	if (!drv_data->dma_mapped)		return;	if (!drv_data->cur_msg->is_dma_mapped) {		dev = &drv_data->cur_msg->spi->dev;		dma_unmap_single(dev, drv_data->rx_dma,					drv_data->rx_map_len, DMA_FROM_DEVICE);		dma_unmap_single(dev, drv_data->tx_dma,					drv_data->tx_map_len, DMA_TO_DEVICE);	}	drv_data->dma_mapped = 0;}/* caller already set message->status; dma and pio irqs are blocked */static void giveback(struct driver_data *drv_data){	struct spi_transfer* last_transfer;	unsigned long flags;	struct spi_message *msg;	spin_lock_irqsave(&drv_data->lock, flags);	msg = drv_data->cur_msg;	drv_data->cur_msg = NULL;	drv_data->cur_transfer = NULL;	drv_data->cur_chip = NULL;	queue_work(drv_data->workqueue, &drv_data->pump_messages);	spin_unlock_irqrestore(&drv_data->lock, flags);	last_transfer = list_entry(msg->transfers.prev,					struct spi_transfer,					transfer_list);	if (!last_transfer->cs_change)		drv_data->cs_control(PXA2XX_CS_DEASSERT);	msg->state = NULL;	if (msg->complete)		msg->complete(msg->context);}static int wait_ssp_rx_stall(void *ioaddr){	unsigned long limit = loops_per_jiffy << 1;	while ((read_SSSR(ioaddr) & SSSR_BSY) && limit--)		cpu_relax();	return limit;}static int wait_dma_channel_stop(int channel){	unsigned long limit = loops_per_jiffy << 1;	while (!(DCSR(channel) & DCSR_STOPSTATE) && limit--)		cpu_relax();	return limit;}void dma_error_stop(struct driver_data *drv_data, const char *msg){	void *reg = drv_data->ioaddr;	/* Stop and reset */	DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;	DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;	write_SSSR(drv_data->clear_sr, reg);	write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);	if (drv_data->ssp_type != PXA25x_SSP)		write_SSTO(0, reg);	flush(drv_data);	write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);	unmap_dma_buffers(drv_data);	dev_err(&drv_data->pdev->dev, "%s\n", msg);	drv_data->cur_msg->state = ERROR_STATE;	tasklet_schedule(&drv_data->pump_transfers);}static void dma_transfer_complete(struct driver_data *drv_data){	void *reg = drv_data->ioaddr;	struct spi_message *msg = drv_data->cur_msg;	/* Clear and disable interrupts on SSP and DMA channels*/	write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);	write_SSSR(drv_data->clear_sr, reg);	DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;	DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;	if (wait_dma_channel_stop(drv_data->rx_channel) == 0)		dev_err(&drv_data->pdev->dev,			"dma_handler: dma rx channel stop failed\n");	if (wait_ssp_rx_stall(drv_data->ioaddr) == 0)		dev_err(&drv_data->pdev->dev,			"dma_transfer: ssp rx stall failed\n");	unmap_dma_buffers(drv_data);	/* update the buffer pointer for the amount completed in dma */	drv_data->rx += drv_data->len -			(DCMD(drv_data->rx_channel) & DCMD_LENGTH);	/* read trailing data from fifo, it does not matter how many	 * bytes are in the fifo just read until buffer is full	 * or fifo is empty, which ever occurs first */	drv_data->read(drv_data);	/* return count of what was actually read */	msg->actual_length += drv_data->len -				(drv_data->rx_end - drv_data->rx);	/* Release chip select if requested, transfer delays are	 * handled in pump_transfers */	if (drv_data->cs_change)		drv_data->cs_control(PXA2XX_CS_DEASSERT);	/* Move to next transfer */	msg->state = next_transfer(drv_data);	/* Schedule transfer tasklet */	tasklet_schedule(&drv_data->pump_transfers);}static void dma_handler(int channel, void *data){	struct driver_data *drv_data = data;	u32 irq_status = DCSR(channel) & DMA_INT_MASK;	if (irq_status & DCSR_BUSERR) {		if (channel == drv_data->tx_channel)			dma_error_stop(drv_data,					"dma_handler: "					"bad bus address on tx channel");		else			dma_error_stop(drv_data,					"dma_handler: "					"bad bus address on rx channel");		return;	}	/* PXA255x_SSP has no timeout interrupt, wait for tailing bytes */	if ((channel == drv_data->tx_channel)		&& (irq_status & DCSR_ENDINTR)		&& (drv_data->ssp_type == PXA25x_SSP)) {		/* Wait for rx to stall */		if (wait_ssp_rx_stall(drv_data->ioaddr) == 0)			dev_err(&drv_data->pdev->dev,				"dma_handler: ssp rx stall failed\n");		/* finish this transfer, start the next */		dma_transfer_complete(drv_data);	}}static irqreturn_t dma_transfer(struct driver_data *drv_data){	u32 irq_status;	void *reg = drv_data->ioaddr;	irq_status = read_SSSR(reg) & drv_data->mask_sr;	if (irq_status & SSSR_ROR) {		dma_error_stop(drv_data, "dma_transfer: fifo overrun");		return IRQ_HANDLED;	}	/* Check for false positive timeout */	if ((irq_status & SSSR_TINT)		&& (DCSR(drv_data->tx_channel) & DCSR_RUN)) {		write_SSSR(SSSR_TINT, reg);		return IRQ_HANDLED;	}	if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) {		/* Clear and disable timeout interrupt, do the rest in		 * dma_transfer_complete */		if (drv_data->ssp_type != PXA25x_SSP)			write_SSTO(0, reg);

⌨️ 快捷键说明

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