241 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			241 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0-or-later
 | |
| /*
 | |
|  * netup_unidvb_spi.c
 | |
|  *
 | |
|  * Internal SPI driver for NetUP Universal Dual DVB-CI
 | |
|  *
 | |
|  * Copyright (C) 2014 NetUP Inc.
 | |
|  * Copyright (C) 2014 Sergey Kozlov <serjk@netup.ru>
 | |
|  * Copyright (C) 2014 Abylay Ospan <aospan@netup.ru>
 | |
|  */
 | |
| 
 | |
| #include "netup_unidvb.h"
 | |
| #include <linux/spi/spi.h>
 | |
| #include <linux/spi/flash.h>
 | |
| #include <linux/mtd/partitions.h>
 | |
| #include <mtd/mtd-abi.h>
 | |
| 
 | |
| #define NETUP_SPI_CTRL_IRQ	0x1000
 | |
| #define NETUP_SPI_CTRL_IMASK	0x2000
 | |
| #define NETUP_SPI_CTRL_START	0x8000
 | |
| #define NETUP_SPI_CTRL_LAST_CS	0x4000
 | |
| 
 | |
| #define NETUP_SPI_TIMEOUT	6000
 | |
| 
 | |
| enum netup_spi_state {
 | |
| 	SPI_STATE_START,
 | |
| 	SPI_STATE_DONE,
 | |
| };
 | |
| 
 | |
| struct netup_spi_regs {
 | |
| 	__u8	data[1024];
 | |
| 	__le16	control_stat;
 | |
| 	__le16	clock_divider;
 | |
| } __packed __aligned(1);
 | |
| 
 | |
| struct netup_spi {
 | |
| 	struct device			*dev;
 | |
| 	struct spi_master		*master;
 | |
| 	struct netup_spi_regs __iomem	*regs;
 | |
| 	u8 __iomem			*mmio;
 | |
| 	spinlock_t			lock;
 | |
| 	wait_queue_head_t		waitq;
 | |
| 	enum netup_spi_state		state;
 | |
| };
 | |
| 
 | |
| static char netup_spi_name[64] = "fpga";
 | |
| 
 | |
| static struct mtd_partition netup_spi_flash_partitions = {
 | |
| 	.name = netup_spi_name,
 | |
| 	.size = 0x1000000, /* 16MB */
 | |
| 	.offset = 0,
 | |
| 	.mask_flags = MTD_CAP_ROM
 | |
| };
 | |
| 
 | |
| static struct flash_platform_data spi_flash_data = {
 | |
| 	.name = "netup0_m25p128",
 | |
| 	.parts = &netup_spi_flash_partitions,
 | |
| 	.nr_parts = 1,
 | |
| };
 | |
| 
 | |
| static struct spi_board_info netup_spi_board = {
 | |
| 	.modalias = "m25p128",
 | |
| 	.max_speed_hz = 11000000,
 | |
| 	.chip_select = 0,
 | |
| 	.mode = SPI_MODE_0,
 | |
| 	.platform_data = &spi_flash_data,
 | |
| };
 | |
| 
 | |
| irqreturn_t netup_spi_interrupt(struct netup_spi *spi)
 | |
| {
 | |
| 	u16 reg;
 | |
| 	unsigned long flags;
 | |
| 
 | |
| 	if (!spi)
 | |
| 		return IRQ_NONE;
 | |
| 
 | |
| 	spin_lock_irqsave(&spi->lock, flags);
 | |
| 	reg = readw(&spi->regs->control_stat);
 | |
| 	if (!(reg & NETUP_SPI_CTRL_IRQ)) {
 | |
| 		spin_unlock_irqrestore(&spi->lock, flags);
 | |
| 		dev_dbg(&spi->master->dev,
 | |
| 			"%s(): not mine interrupt\n", __func__);
 | |
| 		return IRQ_NONE;
 | |
| 	}
 | |
| 	writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat);
 | |
| 	reg = readw(&spi->regs->control_stat);
 | |
| 	writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat);
 | |
| 	spi->state = SPI_STATE_DONE;
 | |
| 	wake_up(&spi->waitq);
 | |
| 	spin_unlock_irqrestore(&spi->lock, flags);
 | |
| 	dev_dbg(&spi->master->dev,
 | |
| 		"%s(): SPI interrupt handled\n", __func__);
 | |
| 	return IRQ_HANDLED;
 | |
| }
 | |
| 
 | |
| static int netup_spi_transfer(struct spi_master *master,
 | |
| 			      struct spi_message *msg)
 | |
| {
 | |
| 	struct netup_spi *spi = spi_master_get_devdata(master);
 | |
| 	struct spi_transfer *t;
 | |
| 	int result = 0;
 | |
| 	u32 tr_size;
 | |
| 
 | |
| 	/* reset CS */
 | |
| 	writew(NETUP_SPI_CTRL_LAST_CS, &spi->regs->control_stat);
 | |
| 	writew(0, &spi->regs->control_stat);
 | |
| 	list_for_each_entry(t, &msg->transfers, transfer_list) {
 | |
| 		tr_size = t->len;
 | |
| 		while (tr_size) {
 | |
| 			u32 frag_offset = t->len - tr_size;
 | |
| 			u32 frag_size = (tr_size > sizeof(spi->regs->data)) ?
 | |
| 					sizeof(spi->regs->data) : tr_size;
 | |
| 			int frag_last = 0;
 | |
| 
 | |
| 			if (list_is_last(&t->transfer_list,
 | |
| 					&msg->transfers) &&
 | |
| 					frag_offset + frag_size == t->len) {
 | |
| 				frag_last = 1;
 | |
| 			}
 | |
| 			if (t->tx_buf) {
 | |
| 				memcpy_toio(spi->regs->data,
 | |
| 					t->tx_buf + frag_offset,
 | |
| 					frag_size);
 | |
| 			} else {
 | |
| 				memset_io(spi->regs->data,
 | |
| 					0, frag_size);
 | |
| 			}
 | |
| 			spi->state = SPI_STATE_START;
 | |
| 			writew((frag_size & 0x3ff) |
 | |
| 				NETUP_SPI_CTRL_IMASK |
 | |
| 				NETUP_SPI_CTRL_START |
 | |
| 				(frag_last ? NETUP_SPI_CTRL_LAST_CS : 0),
 | |
| 				&spi->regs->control_stat);
 | |
| 			dev_dbg(&spi->master->dev,
 | |
| 				"%s(): control_stat 0x%04x\n",
 | |
| 				__func__, readw(&spi->regs->control_stat));
 | |
| 			wait_event_timeout(spi->waitq,
 | |
| 				spi->state != SPI_STATE_START,
 | |
| 				msecs_to_jiffies(NETUP_SPI_TIMEOUT));
 | |
| 			if (spi->state == SPI_STATE_DONE) {
 | |
| 				if (t->rx_buf) {
 | |
| 					memcpy_fromio(t->rx_buf + frag_offset,
 | |
| 						spi->regs->data, frag_size);
 | |
| 				}
 | |
| 			} else {
 | |
| 				if (spi->state == SPI_STATE_START) {
 | |
| 					dev_dbg(&spi->master->dev,
 | |
| 						"%s(): transfer timeout\n",
 | |
| 						__func__);
 | |
| 				} else {
 | |
| 					dev_dbg(&spi->master->dev,
 | |
| 						"%s(): invalid state %d\n",
 | |
| 						__func__, spi->state);
 | |
| 				}
 | |
| 				result = -EIO;
 | |
| 				goto done;
 | |
| 			}
 | |
| 			tr_size -= frag_size;
 | |
| 			msg->actual_length += frag_size;
 | |
| 		}
 | |
| 	}
 | |
| done:
 | |
| 	msg->status = result;
 | |
| 	spi_finalize_current_message(master);
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| static int netup_spi_setup(struct spi_device *spi)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int netup_spi_init(struct netup_unidvb_dev *ndev)
 | |
| {
 | |
| 	struct spi_master *master;
 | |
| 	struct netup_spi *nspi;
 | |
| 
 | |
| 	master = devm_spi_alloc_master(&ndev->pci_dev->dev,
 | |
| 		sizeof(struct netup_spi));
 | |
| 	if (!master) {
 | |
| 		dev_err(&ndev->pci_dev->dev,
 | |
| 			"%s(): unable to alloc SPI master\n", __func__);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 	nspi = spi_master_get_devdata(master);
 | |
| 	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
 | |
| 	master->bus_num = -1;
 | |
| 	master->num_chipselect = 1;
 | |
| 	master->transfer_one_message = netup_spi_transfer;
 | |
| 	master->setup = netup_spi_setup;
 | |
| 	spin_lock_init(&nspi->lock);
 | |
| 	init_waitqueue_head(&nspi->waitq);
 | |
| 	nspi->master = master;
 | |
| 	nspi->regs = (struct netup_spi_regs __iomem *)(ndev->bmmio0 + 0x4000);
 | |
| 	writew(2, &nspi->regs->clock_divider);
 | |
| 	writew(NETUP_UNIDVB_IRQ_SPI, ndev->bmmio0 + REG_IMASK_SET);
 | |
| 	ndev->spi = nspi;
 | |
| 	if (spi_register_master(master)) {
 | |
| 		ndev->spi = NULL;
 | |
| 		dev_err(&ndev->pci_dev->dev,
 | |
| 			"%s(): unable to register SPI bus\n", __func__);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 	snprintf(netup_spi_name,
 | |
| 		sizeof(netup_spi_name),
 | |
| 		"fpga_%02x:%02x.%01x",
 | |
| 		ndev->pci_bus,
 | |
| 		ndev->pci_slot,
 | |
| 		ndev->pci_func);
 | |
| 	if (!spi_new_device(master, &netup_spi_board)) {
 | |
| 		spi_unregister_master(master);
 | |
| 		ndev->spi = NULL;
 | |
| 		dev_err(&ndev->pci_dev->dev,
 | |
| 			"%s(): unable to create SPI device\n", __func__);
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 	dev_dbg(&ndev->pci_dev->dev, "%s(): SPI init OK\n", __func__);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void netup_spi_release(struct netup_unidvb_dev *ndev)
 | |
| {
 | |
| 	u16 reg;
 | |
| 	unsigned long flags;
 | |
| 	struct netup_spi *spi = ndev->spi;
 | |
| 
 | |
| 	if (!spi)
 | |
| 		return;
 | |
| 
 | |
| 	spi_unregister_master(spi->master);
 | |
| 	spin_lock_irqsave(&spi->lock, flags);
 | |
| 	reg = readw(&spi->regs->control_stat);
 | |
| 	writew(reg | NETUP_SPI_CTRL_IRQ, &spi->regs->control_stat);
 | |
| 	reg = readw(&spi->regs->control_stat);
 | |
| 	writew(reg & ~NETUP_SPI_CTRL_IMASK, &spi->regs->control_stat);
 | |
| 	spin_unlock_irqrestore(&spi->lock, flags);
 | |
| 	ndev->spi = NULL;
 | |
| }
 | |
| 
 | |
| 
 |