mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
5.9 KiB
240 lines
5.9 KiB
// 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 <[email protected]> |
|
* Copyright (C) 2014 Abylay Ospan <[email protected]> |
|
*/ |
|
|
|
#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; |
|
} |
|
|
|
|
|
|