cregit-Linux how code gets into the kernel

Release 4.7 drivers/media/pci/netup_unidvb/netup_unidvb_spi.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; }

Contributors

PersonTokensPropCommitsCommitProp
kozlov sergeykozlov sergey167100.00%1100.00%
Total167100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
kozlov sergeykozlov sergey390100.00%1100.00%
Total390100.00%1100.00%


static int netup_spi_setup(struct spi_device *spi) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
kozlov sergeykozlov sergey14100.00%1100.00%
Total14100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
kozlov sergeykozlov sergey28499.65%150.00%
mauro carvalho chehabmauro carvalho chehab10.35%150.00%
Total285100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
kozlov sergeykozlov sergey118100.00%1100.00%
Total118100.00%1100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
kozlov sergeykozlov sergey116799.83%150.00%
mauro carvalho chehabmauro carvalho chehab20.17%150.00%
Total1169100.00%2100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}