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

📄 vr_spi.c

📁 TI的达芬奇系列dm355使用的spi模块驱动
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *  drivers/spi/vr_spi.c * *  SPI master controller driver for the Vermilion Range SPI controller. * *  Copyright (C) 2007 MontaVista Software, Inc. * *  Derived from drivers/spi/pxa2xx_spi.c *  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 version 2 as *  published by the Free Software Foundation. */#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/dma-mapping.h>#include <linux/spi/spi.h>#include <linux/workqueue.h>#include <linux/errno.h>#include <linux/delay.h>#include <linux/pci.h>#include "vr_spi.h"/* for testing SSCR1 changes that require SSP restart, basically * everything except the interrupt enables */#define SSCR1_CHANGE_MASK (SSCR1_STRF | SSCR1_EFWR |SSCR1_RFT \				| SSCR1_TFT | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)#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 pci_dev *dev;	/* SPI framework hookup */	struct spi_master *master;	/* SSP register addresses */	void __iomem *ioaddr;	/* SSP masks */	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;	u8 n_bytes;	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);};struct chip_data {	u32 cr0;	u32 cr1;	u32 psp;	u32 timeout;	u8 n_bytes;	u32 threshold;	u8 bits_per_word;	u8 chip_select;	u32 speed_hz;	int (*write) (struct driver_data * drv_data);	int (*read) (struct driver_data * drv_data);};static void vr_spi_pump_messages(void *data);static int flush(struct driver_data *drv_data){	unsigned long limit = loops_per_jiffy << 1;	void __iomem *reg = drv_data->ioaddr;	do {		while (readl(reg + SSSR) & SSSR_RNE) {			readl(reg + SSDR);		}	} while ((readl(reg + SSSR) & SSSR_BSY) && limit--);	writel(readl(reg + SSSR) | SSSR_ROR, reg + SSSR);	return limit;}static int null_writer(struct driver_data *drv_data){	void __iomem *reg = drv_data->ioaddr;	u8 n_bytes = drv_data->n_bytes;	if (((readl(reg + SSSR) & 0x00000f00) == 0x00000f00)	    || (drv_data->tx == drv_data->tx_end))		return 0;	writel(0, reg + SSDR);	drv_data->tx += n_bytes;	return 1;}static int null_reader(struct driver_data *drv_data){	void __iomem *reg = drv_data->ioaddr;	u8 n_bytes = drv_data->n_bytes;	while ((readl(reg + SSSR) & SSSR_RNE)	       && (drv_data->rx < drv_data->rx_end)) {		readl(reg + SSDR);		drv_data->rx += n_bytes;	}	return drv_data->rx == drv_data->rx_end;}static int u8_writer(struct driver_data *drv_data){	void __iomem *reg = drv_data->ioaddr;	if (((readl(reg + SSSR) & 0x00000f00) == 0x00000f00)	    || (drv_data->tx == drv_data->tx_end))		return 0;	writel(*(u8 *) (drv_data->tx), reg + SSDR);	++drv_data->tx;	return 1;}static int u8_reader(struct driver_data *drv_data){	void __iomem *reg = drv_data->ioaddr;	while ((readl(reg + SSSR) & SSSR_RNE)	       && (drv_data->rx < drv_data->rx_end)) {		*(u8 *) (drv_data->rx) = readl(reg + SSDR);		++drv_data->rx;	}	return drv_data->rx == drv_data->rx_end;}static int u16_writer(struct driver_data *drv_data){	void __iomem *reg = drv_data->ioaddr;	if (((readl(reg + SSSR) & 0x00000f00) == 0x00000f00)	    || (drv_data->tx == drv_data->tx_end))		return 0;	writel(*(u16 *) (drv_data->tx), reg + SSDR);	drv_data->tx += 2;	return 1;}static int u16_reader(struct driver_data *drv_data){	void __iomem *reg = drv_data->ioaddr;	while ((readl(reg + SSSR) & SSSR_RNE)	       && (drv_data->rx < drv_data->rx_end)) {		*(u16 *) (drv_data->rx) = readl(reg + SSDR);		drv_data->rx += 2;	}	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;}/* 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);	msg->state = NULL;	if (msg->complete)		msg->complete(msg->context);}static int wait_ssp_rx_stall(void __iomem * ioaddr){	unsigned long limit = loops_per_jiffy << 1;	while ((readl(ioaddr + SSSR) & SSSR_BSY) && limit--)		cpu_relax();	return limit;}static void int_error_stop(struct driver_data *drv_data, const char *msg){	void __iomem *reg = drv_data->ioaddr;	/* Stop and reset SSP */	writel(readl(reg + SSSR) | drv_data->clear_sr, reg + SSSR);	writel(readl(reg + SSCR1) & ~drv_data->int_cr1, reg + SSCR1);	flush(drv_data);	writel(readl(reg + SSCR0) & ~SSCR0_SSE, reg + SSCR0);	dev_err(&drv_data->dev->dev, "%s\n", msg);	drv_data->cur_msg->state = ERROR_STATE;	tasklet_schedule(&drv_data->pump_transfers);}static void int_transfer_complete(struct driver_data *drv_data){	void __iomem *reg = drv_data->ioaddr;	/* Stop SSP */	writel(readl(reg + SSSR) | drv_data->clear_sr, reg + SSSR);	writel(readl(reg + SSCR1) & ~drv_data->int_cr1, reg + SSCR1);	/* Update total byte transfered return count actual bytes read */	drv_data->cur_msg->actual_length += drv_data->len -	    (drv_data->rx_end - drv_data->rx);	/* Move to next transfer */	drv_data->cur_msg->state = next_transfer(drv_data);	/* Schedule transfer tasklet */	tasklet_schedule(&drv_data->pump_transfers);}static irqreturn_t interrupt_transfer(struct driver_data *drv_data){	void __iomem *reg = drv_data->ioaddr;	u32 irq_mask = (readl(reg + SSCR1) & SSCR1_TIE) ?	    drv_data->mask_sr : drv_data->mask_sr & ~SSSR_TFS;	u32 irq_status = readl(reg + SSSR) & irq_mask;	if (irq_status & SSSR_ROR) {		int_error_stop(drv_data, "interrupt_transfer: fifo overrun");		return IRQ_HANDLED;	}	/* Drain rx fifo, Fill tx fifo and prevent overruns */	do {		if (drv_data->read(drv_data)) {			int_transfer_complete(drv_data);			return IRQ_HANDLED;		}	} while (drv_data->write(drv_data));	if (drv_data->read(drv_data)) {		int_transfer_complete(drv_data);		return IRQ_HANDLED;	}	if (drv_data->tx == drv_data->tx_end) {		writel(readl(reg + SSCR1) & ~SSCR1_TIE, reg + SSCR1);		/* read trailing bytes */		if (!wait_ssp_rx_stall(reg)) {			int_error_stop(drv_data, "interrupt_transfer: "				       "rx stall failed");			return IRQ_HANDLED;		}		if (!drv_data->read(drv_data)) {			int_error_stop(drv_data,				       "interrupt_transfer: "				       "trailing byte read failed");			return IRQ_HANDLED;		}		int_transfer_complete(drv_data);	}	/* We did something */	return IRQ_HANDLED;}static irqreturn_t vr_spi_int(int irq, void *dev_id, struct pt_regs *pt_regs){	struct driver_data *drv_data = dev_id;	void __iomem *reg = drv_data->ioaddr;	if (!(readl(reg + SSSR) & drv_data->mask_sr)) {		/*		 * This isn't our interrupt.  It must be for another device		 * sharing this IRQ.		 */		return IRQ_NONE;	}	if (!drv_data->cur_msg) {		writel(readl(reg + SSCR0) & ~SSCR0_SSE, reg + SSCR0);		writel(readl(reg + SSCR1) & ~drv_data->int_cr1, reg + SSCR1);		writel(readl(reg + SSSR) | drv_data->clear_sr, reg + SSSR);		dev_err(&drv_data->dev->dev, "bad message state "			"in interrupt handler\n");		/* Never fail */		return IRQ_HANDLED;	}	return drv_data->transfer_handler(drv_data);}static void vr_spi_pump_transfers(unsigned long data){	struct driver_data *drv_data = (struct driver_data *)data;	struct spi_message *message = NULL;	struct spi_transfer *transfer = NULL;	struct spi_transfer *previous = NULL;	struct chip_data *chip = NULL;	void __iomem *reg = drv_data->ioaddr;	u32 cr0;	u32 cr1;	/* Get current state information */	message = drv_data->cur_msg;	transfer = drv_data->cur_transfer;	chip = drv_data->cur_chip;	/* Handle for abort */	if (message->state == ERROR_STATE) {		message->status = -EIO;		giveback(drv_data);		return;	}	/* Handle end of message */	if (message->state == DONE_STATE) {		message->status = 0;		giveback(drv_data);		return;	}	/* Delay if requested at end of transfer */	if (message->state == RUNNING_STATE) {		previous = list_entry(transfer->transfer_list.prev,				      struct spi_transfer, transfer_list);		if (previous->delay_usecs)			udelay(previous->delay_usecs);	}	/* Check transfer length */	if (transfer->len > 8191) {		dev_warn(&drv_data->dev->dev, "pump_transfers: transfer "			 "length greater than 8191\n");		message->status = -EINVAL;		giveback(drv_data);		return;	}	/* Setup the transfer state based on the type of transfer */	if (flush(drv_data) == 0) {		dev_err(&drv_data->dev->dev, "pump_transfers: flush failed\n");		message->status = -EIO;		giveback(drv_data);		return;	}	drv_data->n_bytes = chip->n_bytes;	drv_data->tx = (void *)transfer->tx_buf;	drv_data->tx_end = drv_data->tx + transfer->len;	drv_data->rx = transfer->rx_buf;	drv_data->rx_end = drv_data->rx + transfer->len;	drv_data->len = transfer->len;	drv_data->write = drv_data->tx ? chip->write : null_writer;	drv_data->read = drv_data->rx ? chip->read : null_reader;	drv_data->cs_change = transfer->cs_change;	cr0 = chip->cr0;	message->state = RUNNING_STATE;	/* Ensure we have the correct interrupt handler */	drv_data->transfer_handler = interrupt_transfer;	/* Clear status  */	cr1 = chip->cr1 | chip->threshold | drv_data->int_cr1;	writel(drv_data->clear_sr | (chip->chip_select ? SSSR_ALT_FRM : 0),	       reg + SSSR);	/* see if we need to reload the config registers */	if ((readl(reg + SSCR0) != cr0)	    || (readl(reg + SSCR1) & SSCR1_CHANGE_MASK) !=	    (cr1 & SSCR1_CHANGE_MASK)) {		writel(cr0 & ~SSCR0_SSE, reg + SSCR0);		writel(cr1, reg + SSCR1);		writel(cr0, reg + SSCR0);	} else {		writel(cr1, reg + SSCR1);	}}static void vr_spi_pump_messages(void *data)

⌨️ 快捷键说明

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