b037865754
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJWOJZoAAoJEAhfPr2O5OEVmjYP/0RnfVjvRDtx0RxHDmvsowlt sHyrm5C7VME06b4J3O9qpC7PbMCAalvSkYp+bbxF+b//9EfwjvRER+NR8ebgn1Mw 1NQKMtCusWRf4RzI+9osB3pFYgg/cYG2nKl0QVCXHL6xZszEQ9dBrFHEEHfVe8db JU1fGuF6TQNJdYgsVNMN9rStRB0vj3urfehLjB+E138VzDAnzPNA7I7Z4xsWWJw3 V+J7CWLN1xW9IT59LXtRjbD/aCF9KrAmGigS0nCtDz7XVRPh+ZoXQLD073uLP2L3 uYxOmadvc5+5iVwUP4zSsJ6+vw9kLr6Q30sNtLP7V+VkCSlCQNTOePLavB5T8qVY M2qALvwWjujtoSEjZHr7TqrlEpio98OSy1dNJ8GmuOb3UUAKocNN8sGG8h2nR/BR wv2OL/XPNcyB2LV6HeHZz9JiXB+rTbyXEN8CP2cD8ruGhNM5haak3d2l4FYszRXr /a/5JlYAcNrJii6PAXHyBtm6l0C4GPiAk3HQhII2fTErRr8fpln/G5AfaKjun5H8 1Rbxx5JP+5qSHozmz2hNb4w92qqtPugj7qqu7sHCbwKLhh2Aspwo12GkN9acOIsI Kn1U/DWMRrkyptJAxBihsrEX3BXeQdNOPydKfMYEM7qE8EfTDM0uaIFQ+KVWCmNA Qh2TXAp6CZiuBvaqKzyl =sR0p -----END PGP SIGNATURE----- Merge tag 'media/v4.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media Pull media updates from Mauro Carvalho Chehab: "Media updates, including: - Lots of improvements at the kABI documentation - Split of Videobuf2 into a common part and a V4L2 specific one - Split of the VB2 tracing events into a separate header file - s5p-mfc got support for Exynos 5433 - v4l2 fixes for 64-bits alignment when running 32 bits userspace on ARM - Added support for SDR radio transmitter at core, vivid and hackrf drivers - Some y2038 fixups - Some improvements at V4L2 colorspace support - saa7164 converted to use the V4L2 core control framework - several new boards additions, cleanups and fixups PS: There are two patches for scripts/kernel-doc that are needed by the documentation patches on Media. Jon is OK on merging those via my tree" * tag 'media/v4.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (146 commits) [media] c8sectpfe: Remove select on CONFIG_FW_LOADER_USER_HELPER_FALLBACK [media] DocBook media: update copyright/version numbers [media] ivtv: Convert to get_user_pages_unlocked() [media] media/v4l2-ctrls: fix setting autocluster to manual with VIDIOC_S_CTRL [media] DocBook media: Fix a typo in encoder cmd [media] DocBook: add SDR specific info to G_MODULATOR / S_MODULATOR [media] DocBook: add SDR specific info to G_TUNER / S_TUNER [media] hackrf: do not set human readable name for formats [media] hackrf: add support for transmitter [media] hackrf: switch to single function which configures everything [media] hackrf: add control for RF amplifier [media] DocBook: add modulator type field [media] v4l: add type field to v4l2_modulator struct [media] DocBook: document SDR transmitter [media] v4l2: add support for SDR transmitter [media] DocBook: document tuner RF gain control [media] v4l2: add RF gain control [media] v4l2: rename V4L2_TUNER_ADC to V4L2_TUNER_SDR [media] media/vivid-osd: fix info leak in ioctl [media] media: videobuf2: Move v4l2-specific stuff to videobuf2-v4l2 ...
249 lines
6.4 KiB
C
249 lines
6.4 KiB
C
/*
|
|
* 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>
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#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 = 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)) {
|
|
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;
|
|
|
|
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);
|
|
spi_unregister_master(spi->master);
|
|
ndev->spi = NULL;
|
|
}
|
|
|
|
|