Em Mon, 02 Feb 2015 12:22:32 +0300 Kozlov Sergey <serjk@xxxxxxxx> escreveu: > > NetUP Dual Universal CI PCIe board driver. > The board has > - two CI slots > - two I2C adapters > - SPI master bus for accessing flash memory containing > FPGA firmware > > Signed-off-by: Kozlov Sergey <serjk@xxxxxxxx> > --- > MAINTAINERS | 9 + > drivers/media/pci/Kconfig | 1 + > drivers/media/pci/Makefile | 3 +- > drivers/media/pci/netup/Kconfig | 12 + > drivers/media/pci/netup/Makefile | 9 + > drivers/media/pci/netup/netup_unidvb.h | 232 +++++++ > drivers/media/pci/netup/netup_unidvb_ci.c | 248 ++++++++ > drivers/media/pci/netup/netup_unidvb_core.c | 919 +++++++++++++++++++++++++++ > drivers/media/pci/netup/netup_unidvb_i2c.c | 350 ++++++++++ > drivers/media/pci/netup/netup_unidvb_spi.c | 272 ++++++++ > 10 files changed, 2054 insertions(+), 1 deletion(-) > create mode 100644 drivers/media/pci/netup/Kconfig > create mode 100644 drivers/media/pci/netup/Makefile > create mode 100644 drivers/media/pci/netup/netup_unidvb.h > create mode 100644 drivers/media/pci/netup/netup_unidvb_ci.c > create mode 100644 drivers/media/pci/netup/netup_unidvb_core.c > create mode 100644 drivers/media/pci/netup/netup_unidvb_i2c.c > create mode 100644 drivers/media/pci/netup/netup_unidvb_spi.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index a52001c..e2febd3 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -6572,6 +6572,15 @@ F: include/net/netrom.h > F: include/uapi/linux/netrom.h > F: net/netrom/ > > +NETUP VIDEO4LINUX DRIVER I would call it: MEDIA DRIVERS FOR NETUP PCI UNIVERSAL DVB devices > +M: Sergey Kozlov <serjk@xxxxxxxx> > +L: linux-media@xxxxxxxxxxxxxxx > +W: http://linuxtv.org/ > +W: http://netup.tv/ > +T: git git://linuxtv.org/media_tree.git > +S: Supported > +F: drivers/media/pci/netup/* Just drivers/media/pci/netup directory doesn't seem to be a good idea, as NetUp might have, in the future, other devices built using different chipsets. So, I would prefer if you use the name of the current NetUp platform here (and on the driver's name). Perhaps unidvb or netup_unidvb? > + > NETWORK BLOCK DEVICE (NBD) > M: Paul Clements <Paul.Clements@xxxxxxxxxxxx> > S: Maintained > diff --git a/drivers/media/pci/Kconfig b/drivers/media/pci/Kconfig > index 218144a..f1ec01e 100644 > --- a/drivers/media/pci/Kconfig > +++ b/drivers/media/pci/Kconfig > @@ -47,6 +47,7 @@ source "drivers/media/pci/mantis/Kconfig" > source "drivers/media/pci/ngene/Kconfig" > source "drivers/media/pci/ddbridge/Kconfig" > source "drivers/media/pci/smipcie/Kconfig" > +source "drivers/media/pci/netup/Kconfig" > endif > > endif #MEDIA_PCI_SUPPORT > diff --git a/drivers/media/pci/Makefile b/drivers/media/pci/Makefile > index 0baf0d2..92be70e 100644 > --- a/drivers/media/pci/Makefile > +++ b/drivers/media/pci/Makefile > @@ -12,7 +12,8 @@ obj-y += ttpci/ \ > ngene/ \ > ddbridge/ \ > saa7146/ \ > - smipcie/ > + smipcie/ \ > + netup/ > > obj-$(CONFIG_VIDEO_IVTV) += ivtv/ > obj-$(CONFIG_VIDEO_ZORAN) += zoran/ > diff --git a/drivers/media/pci/netup/Kconfig b/drivers/media/pci/netup/Kconfig > new file mode 100644 > index 0000000..3ad3cc3 > --- /dev/null > +++ b/drivers/media/pci/netup/Kconfig > @@ -0,0 +1,12 @@ > +config DVB_NETUP_UNIDVB > + tristate "NetUP Universal DVB card support" > + depends on DVB_CORE && VIDEO_DEV && PCI && I2C && SPI_MASTER > + select VIDEOBUF_DVB > + select VIDEOBUF_VMALLOC Videobuf version 1 is deprecated. We're not adding new drivers to it, as the goal is to remove it from the Kernel. Please use, instead, Videobuf2. > + select DVB_HORUS3A if MEDIA_SUBDRV_AUTOSELECT > + select DVB_ASCOT2E if MEDIA_SUBDRV_AUTOSELECT > + select DVB_LNBH25 if MEDIA_SUBDRV_AUTOSELECT > + select DVB_CXD2841ER if MEDIA_SUBDRV_AUTOSELECT > + ---help--- > + Support for NetUP PCI express Universal DVB card. > + > diff --git a/drivers/media/pci/netup/Makefile b/drivers/media/pci/netup/Makefile > new file mode 100644 > index 0000000..ee6ae05 > --- /dev/null > +++ b/drivers/media/pci/netup/Makefile > @@ -0,0 +1,9 @@ > +netup-unidvb-objs += netup_unidvb_core.o > +netup-unidvb-objs += netup_unidvb_i2c.o > +netup-unidvb-objs += netup_unidvb_ci.o > +netup-unidvb-objs += netup_unidvb_spi.o > + > +obj-$(CONFIG_DVB_NETUP_UNIDVB) += netup-unidvb.o > + > +ccflags-y += -Idrivers/media/dvb-core > +ccflags-y += -Idrivers/media/dvb-frontends > diff --git a/drivers/media/pci/netup/netup_unidvb.h b/drivers/media/pci/netup/netup_unidvb.h > new file mode 100644 > index 0000000..4d0e1eb > --- /dev/null > +++ b/drivers/media/pci/netup/netup_unidvb.h > @@ -0,0 +1,232 @@ > +/* > + * netup_unidvb.h > + * > + * Data type definitions for NetUP Universal Dual DVB-CI > + * > + * Copyright (C) 2014 NetUP Inc. > + * Copyright (C) 2014 Sergey Kozlov <serjk@xxxxxxxx> > + * Copyright (C) 2014 Abylay Ospan <aospan@xxxxxxxx> > + * > + * 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 <linux/pci.h> > +#include <linux/i2c.h> > +#include <linux/workqueue.h> > +#include <media/v4l2-common.h> > +#include <media/v4l2-device.h> > +#include <media/videobuf-dma-sg.h> > +#include <media/videobuf-dvb.h> > +#include <dvb_ca_en50221.h> > + > +#define NETUP_UNIDVB_NAME "netup_unidvb" > +#define NETUP_UNIDVB_VERSION "0.0.1" > +#define NETUP_VENDOR_ID 0x1b55 > +#define NETUP_PCI_DEV_REVISION 0x2 > + > +/* Avalon-MM PCI-E registers */ > +#define AVL_PCIE_IENR 0x50 > +#define AVL_PCIE_ISR 0x40 > +#define AVL_IRQ_ENABLE 0x80 > +#define AVL_IRQ_ASSERTED 0x80 > + > +/* IRQ-related regisers */ > +#define REG_ISR 0x4890 > +#define REG_ISR_MASKED 0x4892 > +#define REG_IMASK_SET 0x4894 > +#define REG_IMASK_CLEAR 0x4896 > + > +#define NETUP_UNIDVB_IRQ_SPI (1 << 0) > +#define NETUP_UNIDVB_IRQ_I2C0 (1 << 1) > +#define NETUP_UNIDVB_IRQ_I2C1 (1 << 2) > +#define NETUP_UNIDVB_IRQ_FRA0 (1 << 4) /* FrontendA IRQ0 */ > +#define NETUP_UNIDVB_IRQ_FRA1 (1 << 5) /* FrontendA IRQ1 */ > +#define NETUP_UNIDVB_IRQ_FRB0 (1 << 6) /* FrontendB IRQ0 */ > +#define NETUP_UNIDVB_IRQ_FRB1 (1 << 7) /* FrontendB IRQ1 */ > +#define NETUP_UNIDVB_IRQ_DMA1 (1 << 8) /* DMA1 */ > +#define NETUP_UNIDVB_IRQ_DMA2 (1 << 9) /* DMA2 */ > +#define NETUP_UNIDVB_IRQ_CI (1 << 10) /* CI MAIN MODULE */ > +#define NETUP_UNIDVB_IRQ_CAM0 (1 << 11) /* CI CAM0 */ > +#define NETUP_UNIDVB_IRQ_CAM1 (1 << 12) /* CI CAM1 */ > + > +/* GPIO registers */ > +#define GPIO_REG_IO 0x4880 > +#define GPIO_REG_IO_TOGGLE 0x4882 > +#define GPIO_REG_IO_SET 0x4884 > +#define GPIO_REG_IO_CLEAR 0x4886 > +/* GPIO pins */ > +#define GPIO_FEA_RESET (1 << 0) > +#define GPIO_FEB_RESET (1 << 1) > +#define GPIO_RFA_CTL (1 << 2) > +#define GPIO_RFB_CTL (1 << 3) > +#define GPIO_FEA_TU_RESET (1 << 4) > +#define GPIO_FEB_TU_RESET (1 << 5) > + > +/* DMA section */ > +#define NETUP_DMA0_ADDR 0x4900 > +#define NETUP_DMA1_ADDR 0x4940 > +/* DMA part. 8 blocks for 120Mbps per port is ok */ > +#define NETUP_DMA_BLOCKS_COUNT 8 > +#define NETUP_DMA_PACKETS_COUNT 128 > +/* DMA block status */ > +#define NETUP_DMA_UNK 1 > +#define NETUP_DMA_DONE 2 > +#define BIT_DMA_RUN 1 > +#define BIT_DMA_ERROR 2 > +#define BIT_DMA_IRQ 0x200 > + > +struct netup_dma_regs { > + /* see BIT_DMA_* defines; > + * [24-31] - dma_current_length; > + * [16-23] - dma_current_block_cnt */ > + u32 ctrlstat_set; > + u32 ctrlstat_clear; > + /* dma start address, low */ > + u32 start_addr_lo; > + /* dma start address, high */ > + u32 start_addr_hi; > + /* [0-7] dma_packet_size (188); > + * [16-23] packets count in block (128); > + * [24-31] dma_length - blocks count in dma */ > + u32 size; > + /* 8ns intervals; > + * for example 3sec => timeout = 375 000 000 */ > + u32 timeout; > + /* dma current address, low */ > + u32 curr_addr_lo; > + /* dma current address, high */ > + u32 curr_addr_hi; > + u32 stat_pkt_received; > + u32 stat_pkt_accepted; > + u32 stat_pkt_overruns; > + u32 stat_pkt_underruns; > + u32 stat_fifo_overruns; > +} __packed __aligned(1); > + > +struct netup_unidvb_buffer { > + struct videobuf_buffer vb; > + u32 size; > +}; > + > +struct netup_dma { > + u8 num; > + spinlock_t lock; > + struct netup_unidvb_dev *ndev; > + struct netup_dma_regs *regs; > + u32 ring_buffer_size; > + u8 *addr_virt; > + dma_addr_t addr_phys; > + u64 addr_last; > + u32 high_addr; > + u32 data_offset; > + u32 data_size; > + struct list_head free_buffers; > + struct work_struct work; > + struct timer_list timeout; > +}; > + > +/* I2C bus section */ > +#define NETUP_I2C_BUS0_ADDR 0x4800 > +#define NETUP_I2C_BUS1_ADDR 0x4840 > +#define NETUP_I2C_TIMEOUT HZ > + > +struct netup_i2c_fifo_regs { > + union { > + u8 data8; > + u16 data16; > + u32 data32; > + }; > + u8 padding[4]; > + u16 stat_ctrl; > +} __packed __aligned(1); > + > +struct netup_i2c_regs { > + u16 clkdiv; > + u16 twi_ctrl0_stat; > + u16 twi_addr_ctrl1; > + u16 length; > + u8 padding1[8]; > + struct netup_i2c_fifo_regs tx_fifo; > + u8 padding2[6]; > + struct netup_i2c_fifo_regs rx_fifo; > +} __packed __aligned(1); The above structures are harware/firmware oriented, right? You need to specify if the integers are either big endian or little endian, declaring them using __be32/__le32 and so on. Those macros will produce a warning if you try to access the fields without the proper endiannes conversion routines: be32_to_cpu() / le32_to_cpu(), with is a good thing, as it will warrant that your board would work on a system using a different endiannes than x86. > + > +enum netup_i2c_state { > + STATE_DONE, > + STATE_WAIT, > + STATE_WANT_READ, > + STATE_WANT_WRITE, > + STATE_ERROR > +}; > + > +struct netup_i2c { > + spinlock_t lock; > + wait_queue_head_t wq; > + struct i2c_adapter adap; > + struct netup_unidvb_dev *dev; > + struct netup_i2c_regs *regs; > + struct i2c_msg *msg; > + enum netup_i2c_state state; > + u32 xmit_size; > +}; > + > +/* CI section */ > +struct netup_ci_state { > + struct dvb_ca_en50221 ca; > + u8 __iomem *membase8_config; > + u8 __iomem *membase8_io; > + struct netup_unidvb_dev *dev; > + int status; > + int nr; > +}; > + > +#define CAM_CTRLSTAT_READ_SET 0x4980 > +#define CAM_CTRLSTAT_CLR 0x4982 > + > +/* SPI flash section */ > +struct netup_spi; > + > +/* main device context */ > + > +struct netup_unidvb_dev { > + struct pci_dev *pci_dev; > + int pci_bus; > + int pci_slot; > + int pci_func; > + int board_num; > + int old_fw; > + /* MMIO */ > + u32 __iomem *lmmio0; > + u8 __iomem *bmmio0; > + u32 __iomem *lmmio1; > + u8 __iomem *bmmio1; > + struct videobuf_dvb_frontends frontends[2]; > + /* I2C buses */ > + struct netup_i2c i2c[2]; > + /* DMA */ > + struct workqueue_struct *wq; > + struct netup_dma dma[2]; > + /* CI slots */ > + struct netup_ci_state ci[2]; > + /* SPI */ > + struct netup_spi *spi; > +}; > + > +int netup_i2c_register(struct netup_unidvb_dev *ndev); > +void netup_i2c_unregister(struct netup_unidvb_dev *ndev); > +irqreturn_t netup_i2c_interrupt(struct netup_i2c *i2c); > +irqreturn_t netup_spi_interrupt(struct netup_spi *spi); > +irqreturn_t netup_dma_interrupt(struct netup_dma *dma); > +int netup_unidvb_ci_register( > + struct netup_unidvb_dev *dev, int num, struct pci_dev *pci_dev); > +void netup_unidvb_ci_unregister(struct netup_unidvb_dev *dev, int num); > +int netup_spi_init(struct netup_unidvb_dev *ndev); > +void netup_spi_release(struct netup_unidvb_dev *ndev); > diff --git a/drivers/media/pci/netup/netup_unidvb_ci.c b/drivers/media/pci/netup/netup_unidvb_ci.c > new file mode 100644 > index 0000000..3610634 > --- /dev/null > +++ b/drivers/media/pci/netup/netup_unidvb_ci.c > @@ -0,0 +1,248 @@ > +/* > + * netup_unidvb_ci.c > + * > + * DVB CAM support for NetUP Universal Dual DVB-CI > + * > + * Copyright (C) 2014 NetUP Inc. > + * Copyright (C) 2014 Sergey Kozlov <serjk@xxxxxxxx> > + * Copyright (C) 2014 Abylay Ospan <aospan@xxxxxxxx> > + * > + * 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 <linux/init.h> > +#include <linux/module.h> > +#include <linux/moduleparam.h> > +#include <linux/kmod.h> > +#include <linux/kernel.h> > +#include <linux/slab.h> > +#include <linux/interrupt.h> > +#include <linux/delay.h> > +#include "netup_unidvb.h" > + > +static int ci_debug; > + > +#define dprintk(args...) \ > +do { \ > + if (ci_debug) \ > + dev_dbg(&dev->pci_dev->dev, args); \ > +} while (0) > + > +module_param(ci_debug, int, 0644); This is not very nice, as, in order to enable debug, both this parameter and dynamic printk should be enabled via /sys/kernel/debug/dynamic_debug. Just use dev_dbg() directly. > + > +/* CI */ > +#define CAM0_CONFIG 0x0 > +#define CAM0_IO 0x8000 > +#define CAM0_MEM 0x10000 > +#define CAM0_SZ 32 > + > +#define CAM1_CONFIG 0x20000 > +#define CAM1_IO 0x28000 > +#define CAM1_MEM 0x30000 > +#define CAM1_SZ 32 > + > +#define CAM_CTRLSTAT_READ_SET 0x4980 > +#define CAM_CTRLSTAT_CLR 0x4982 > + > +#define BIT_CAM_STCHG (1<<0) > +#define BIT_CAM_PRESENT (1<<1) > +#define BIT_CAM_RESET (1<<2) > +#define BIT_CAM_BYPASS (1<<3) > +#define BIT_CAM_READY (1<<4) > +#define BIT_CAM_ERROR (1<<5) > +#define BIT_CAM_OVERCURR (1<<6) > + > +#define CAM1_SHIFT 8 > + > + > +static int netup_unidvb_ci_slot_ts_ctl( > + struct dvb_ca_en50221 *en50221, int slot) > +{ > + struct netup_ci_state *state = en50221->data; > + struct netup_unidvb_dev *dev = state->dev; > + u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0; > + > + dprintk("%s(): CAM_CTRLSTAT=0x%x\n", > + __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET)); > + if (slot != 0) > + return -EINVAL; > + /* pass data to CAM module */ > + writew(BIT_CAM_BYPASS << shift, dev->bmmio0 + CAM_CTRLSTAT_CLR); > + dprintk("%s(): CAM_CTRLSTAT=0x%x done\n", > + __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET)); > + return 0; > +} > + > +static int netup_unidvb_ci_slot_shutdown( > + struct dvb_ca_en50221 *en50221, int slot) > +{ > + struct netup_ci_state *state = en50221->data; > + struct netup_unidvb_dev *dev = state->dev; > + > + dprintk("%s()\n", __func__); > + return 0; > +} > + > +static int netup_unidvb_ci_slot_reset( > + struct dvb_ca_en50221 *en50221, int slot) > +{ > + struct netup_ci_state *state = en50221->data; > + struct netup_unidvb_dev *dev = state->dev; > + unsigned long timeout = 0; > + u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0; > + u16 ci_stat = 0; > + int reset_counter = 3; > + > + dprintk("%s(): CAM_CTRLSTAT_READ_SET=0x%x\n", > + __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET)); > +reset: > + timeout = jiffies + 5*HZ; Please use msecs_to_jiffies instead. > + /* start reset */ > + writew(BIT_CAM_RESET << shift, dev->bmmio0 + CAM_CTRLSTAT_READ_SET); > + dprintk("%s(): waiting for reset\n", __func__); > + /* wait until reset done */ > + while (time_before(jiffies, timeout)) { > + ci_stat = readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET); > + if (ci_stat & (BIT_CAM_READY << shift)) > + break; > + udelay(1000); > + } > + if (!(ci_stat & (BIT_CAM_READY << shift)) && reset_counter > 0) { > + dprintk("%s(): CAMP reset timeout! Will try again..\n", > + __func__); > + reset_counter--; > + goto reset; > + } > + return 0; > +} > + > +static int netup_unidvb_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, > + int slot, int open) > +{ > + struct netup_ci_state *state = en50221->data; > + struct netup_unidvb_dev *dev = state->dev; > + u16 shift = (state->nr == 1) ? CAM1_SHIFT : 0; > + u16 ci_stat = 0; > + > + dprintk("%s(): CAM_CTRLSTAT_READ_SET=0x%x\n", > + __func__, readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET)); > + ci_stat = readw(dev->bmmio0 + CAM_CTRLSTAT_READ_SET); > + if (ci_stat & (BIT_CAM_READY << shift)) { > + state->status = DVB_CA_EN50221_POLL_CAM_PRESENT | > + DVB_CA_EN50221_POLL_CAM_READY; > + } else if (ci_stat & (BIT_CAM_PRESENT << shift)) { > + state->status = DVB_CA_EN50221_POLL_CAM_PRESENT; > + } else { > + state->status = 0; > + } > + return state->status; > +} > + > +static int netup_unidvb_ci_read_attribute_mem( > + struct dvb_ca_en50221 *en50221, int slot, int addr) > +{ > + struct netup_ci_state *state = en50221->data; > + struct netup_unidvb_dev *dev = state->dev; > + u8 val = state->membase8_config[addr]; > + > + dprintk("%s(): addr=0x%x val=0x%x\n", __func__, addr, val); > + return val; > +} > + > +static int netup_unidvb_ci_write_attribute_mem( > + struct dvb_ca_en50221 *en50221, int slot, int addr, u8 data) > +{ > + struct netup_ci_state *state = en50221->data; > + struct netup_unidvb_dev *dev = state->dev; > + > + dprintk("%s(): addr=0x%x data=0x%x\n", __func__, addr, data); > + state->membase8_config[addr] = data; > + return 0; > +} > + > +static int netup_unidvb_ci_read_cam_ctl( > + struct dvb_ca_en50221 *en50221, int slot, u8 addr) > +{ > + struct netup_ci_state *state = en50221->data; > + struct netup_unidvb_dev *dev = state->dev; > + u8 val = state->membase8_io[addr]; > + > + dprintk("%s(): addr=0x%x val=0x%x\n", __func__, addr, val); > + return val; > +} > + > +static int netup_unidvb_ci_write_cam_ctl( > + struct dvb_ca_en50221 *en50221, int slot, u8 addr, u8 data) > +{ > + struct netup_ci_state *state = en50221->data; > + struct netup_unidvb_dev *dev = state->dev; > + > + dprintk("%s(): addr=0x%x data=0x%x\n", __func__, addr, data); > + state->membase8_io[addr] = data; > + return 0; > +} > + > +int netup_unidvb_ci_register( > + struct netup_unidvb_dev *dev, int num, struct pci_dev *pci_dev) > +{ > + int result; > + struct netup_ci_state *state; > + > + if (num < 0 || num > 1) { > + dev_err(&pci_dev->dev, "%s(): invalid CI adapter %d\n", > + __func__, num); > + return -EINVAL; > + } > + state = &dev->ci[num]; > + state->nr = num; > + state->membase8_config = dev->bmmio1 + > + ((num == 0) ? CAM0_CONFIG : CAM1_CONFIG); > + state->membase8_io = dev->bmmio1 + > + ((num == 0) ? CAM0_IO : CAM1_IO); > + state->dev = dev; > + state->ca.owner = THIS_MODULE; > + state->ca.read_attribute_mem = netup_unidvb_ci_read_attribute_mem; > + state->ca.write_attribute_mem = netup_unidvb_ci_write_attribute_mem; > + state->ca.read_cam_control = netup_unidvb_ci_read_cam_ctl; > + state->ca.write_cam_control = netup_unidvb_ci_write_cam_ctl; > + state->ca.slot_reset = netup_unidvb_ci_slot_reset; > + state->ca.slot_shutdown = netup_unidvb_ci_slot_shutdown; > + state->ca.slot_ts_enable = netup_unidvb_ci_slot_ts_ctl; > + state->ca.poll_slot_status = netup_unidvb_poll_ci_slot_status; > + state->ca.data = state; > + result = dvb_ca_en50221_init( > + &dev->frontends[num].adapter, &state->ca, 0, 1); > + if (result < 0) { > + dev_err(&pci_dev->dev, > + "%s(): dvb_ca_en50221_init result %d\n", > + __func__, result); > + return result; > + } > + writew(NETUP_UNIDVB_IRQ_CI, (u16 *)(dev->bmmio0 + REG_IMASK_SET)); > + dev_info(&pci_dev->dev, > + "%s(): CI adapter %d init done\n", __func__, num); > + return 0; > +} > + > +void netup_unidvb_ci_unregister(struct netup_unidvb_dev *dev, int num) > +{ > + struct netup_ci_state *state; > + > + dprintk("%s()\n", __func__); > + if (num < 0 || num > 1) { > + dev_err(&dev->pci_dev->dev, "%s(): invalid CI adapter %d\n", > + __func__, num); > + return; > + } > + state = &dev->ci[num]; > + dvb_ca_en50221_release(&state->ca); > +} > + > diff --git a/drivers/media/pci/netup/netup_unidvb_core.c b/drivers/media/pci/netup/netup_unidvb_core.c > new file mode 100644 > index 0000000..2670ca0 > --- /dev/null > +++ b/drivers/media/pci/netup/netup_unidvb_core.c > @@ -0,0 +1,919 @@ > +/* > + * netup_unidvb_core.c > + * > + * Main module for NetUP Universal Dual DVB-CI > + * > + * Copyright (C) 2014 NetUP Inc. > + * Copyright (C) 2014 Sergey Kozlov <serjk@xxxxxxxx> > + * Copyright (C) 2014 Abylay Ospan <aospan@xxxxxxxx> > + * > + * 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 <linux/init.h> > +#include <linux/module.h> > +#include <linux/moduleparam.h> > +#include <linux/kmod.h> > +#include <linux/kernel.h> > +#include <linux/slab.h> > +#include <linux/interrupt.h> > +#include <linux/delay.h> > +#include <linux/list.h> > +#include <media/videobuf-vmalloc.h> > + > +#include "netup_unidvb.h" > +#include "cxd2841er.h" > +#include "horus3a.h" > +#include "ascot2e.h" > +#include "lnbh25.h" > + > +MODULE_DESCRIPTION("Driver for NetUP Dual Universal DVB CI PCIe card"); > +MODULE_AUTHOR("info@xxxxxxxx"); > +MODULE_VERSION(NETUP_UNIDVB_VERSION); > +MODULE_LICENSE("GPL"); > + > +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); > + > +#define DRIVER_NAME "netup_unidvb" > + > +static int debug; > +module_param(debug, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); > +static int spi_enable; > +module_param(spi_enable, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); > + > +#define dprintk(args...) \ > + do { \ > + if (debug) \ > + dev_dbg(&ndev->pci_dev->dev, args); \ > + } while (0) This is not very nice, as, in order to enable debug, both this parameter and dynamic printk should be enabled via /sys/kernel/debug/dynamic_debug. Just use dev_dbg() directly. > + > +static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc); > + > +static struct cxd2841er_config demod_config = { > + .i2c_addr = 0xc8 > +}; > + > +static struct horus3a_config horus3a_conf = { > + .i2c_address = 0xc0, > + .xtal_freq_mhz = 16, > + .set_tuner_callback = netup_unidvb_tuner_ctrl > +}; > + > +static struct ascot2e_config ascot2e_conf = { > + .i2c_address = 0xc2, > + .set_tuner_callback = netup_unidvb_tuner_ctrl > +}; > + > +static struct lnbh25_config lnbh25_conf = { > + .i2c_address = 0x10, > + .data2_config = LNBH25_TEN | LNBH25_EXTM > +}; > + > +static int netup_unidvb_tuner_ctrl(void *priv, int is_dvb_tc) > +{ > + u8 reg, mask; > + struct netup_dma *dma = priv; > + struct netup_unidvb_dev *ndev; > + > + if (!priv) > + return -EINVAL; > + dprintk("%s(): num %d is_dvb_tc %d\n", > + __func__, dma->num, is_dvb_tc); > + ndev = dma->ndev; > + reg = readb(ndev->bmmio0 + GPIO_REG_IO); > + mask = (dma->num == 0) ? GPIO_RFA_CTL : GPIO_RFB_CTL; > + if (!is_dvb_tc) > + reg |= mask; > + else > + reg &= ~mask; > + writeb(reg, ndev->bmmio0 + GPIO_REG_IO); > + return 0; > +} > + > +static void netup_unidvb_dev_enable(struct netup_unidvb_dev *ndev) > +{ > + u16 gpio_reg; > + > + /* enable PCI-E interrupts */ > + writel(AVL_IRQ_ENABLE, ndev->bmmio0 + AVL_PCIE_IENR); > + /* unreset frontends bits[0:1] */ > + writeb(0x00, ndev->bmmio0 + GPIO_REG_IO); > + msleep(100); > + gpio_reg = > + GPIO_FEA_RESET | GPIO_FEB_RESET | > + GPIO_FEA_TU_RESET | GPIO_FEB_TU_RESET | > + GPIO_RFA_CTL | GPIO_RFB_CTL; > + writeb(gpio_reg, ndev->bmmio0 + GPIO_REG_IO); > + dev_dbg(&ndev->pci_dev->dev, > + "%s(): AVL_PCIE_IENR 0x%x GPIO_REG_IO 0x%x\n", > + __func__, readl(ndev->bmmio0 + AVL_PCIE_IENR), > + (int)readb(ndev->bmmio0 + GPIO_REG_IO)); > + > +} > + > +static void netup_unidvb_dma_enable(struct netup_dma *dma, int enable) > +{ > + u32 irq_mask = (dma->num == 0 ? > + NETUP_UNIDVB_IRQ_DMA1 : NETUP_UNIDVB_IRQ_DMA2); > + > + dev_dbg(&dma->ndev->pci_dev->dev, > + "%s(): DMA%d enable %d\n", __func__, dma->num, enable); > + if (enable) { > + writel(BIT_DMA_RUN, &dma->regs->ctrlstat_set); > + writew(irq_mask, > + (u16 *)(dma->ndev->bmmio0 + REG_IMASK_SET)); > + } else { > + writel(BIT_DMA_RUN, &dma->regs->ctrlstat_clear); > + writew(irq_mask, > + (u16 *)(dma->ndev->bmmio0 + REG_IMASK_CLEAR)); > + } > +} > + > +irqreturn_t netup_dma_interrupt(struct netup_dma *dma) > +{ > + u64 addr_curr; > + u32 size; > + unsigned long flags; > + struct device *dev = &dma->ndev->pci_dev->dev; > + > + spin_lock_irqsave(&dma->lock, flags); > + addr_curr = ((u64)readl(&dma->regs->curr_addr_hi) << 32) | > + (u64)readl(&dma->regs->curr_addr_lo) | dma->high_addr; > + /* clear IRQ */ > + writel(BIT_DMA_IRQ, &dma->regs->ctrlstat_clear); > + /* sanity check */ > + if (addr_curr < dma->addr_phys || > + addr_curr > dma->addr_phys + dma->ring_buffer_size) { > + if (addr_curr != 0) { > + dev_err(dev, "%s(): DMA current address (0x%llx) not in range 0x%llx - 0x%llx\n", > + __func__, > + addr_curr, (u64)dma->addr_phys, > + (u64)(dma->addr_phys + dma->ring_buffer_size)); > + } > + goto irq_handled; > + } > + size = (addr_curr >= dma->addr_last) ? > + (u32)(addr_curr - dma->addr_last) : > + (u32)(dma->ring_buffer_size - (dma->addr_last - addr_curr)); > + if (dma->data_size != 0) { > + printk_ratelimited("%s(): lost interrupt, data size %d\n", > + __func__, dma->data_size); > + dma->data_size += size; > + } > + if (dma->data_size == 0 || dma->data_size > dma->ring_buffer_size) { > + dma->data_size = size; > + dma->data_offset = (u32)(dma->addr_last - dma->addr_phys); > + } > + dma->addr_last = addr_curr; > + queue_work(dma->ndev->wq, &dma->work); > +irq_handled: > + spin_unlock_irqrestore(&dma->lock, flags); > + return IRQ_HANDLED; > +} > + > +static irqreturn_t netup_unidvb_isr(int irq, void *dev_id) > +{ > + struct pci_dev *pci_dev = (struct pci_dev *)dev_id; > + struct netup_unidvb_dev *ndev = pci_get_drvdata(pci_dev); > + u32 reg40, reg_isr; > + irqreturn_t iret = IRQ_NONE; > + > + /* disable interrupts */ > + writel(0, ndev->bmmio0 + AVL_PCIE_IENR); > + /* check IRQ source */ > + reg40 = readl(ndev->bmmio0 + AVL_PCIE_ISR); > + if ((reg40 & AVL_IRQ_ASSERTED) != 0) { > + /* IRQ is being signaled */ > + reg_isr = readw(ndev->bmmio0 + REG_ISR); > + if (reg_isr & NETUP_UNIDVB_IRQ_I2C0) { > + iret = netup_i2c_interrupt(&ndev->i2c[0]); > + } else if (reg_isr & NETUP_UNIDVB_IRQ_I2C1) { > + iret = netup_i2c_interrupt(&ndev->i2c[1]); > + } else if (reg_isr & NETUP_UNIDVB_IRQ_SPI) { > + iret = netup_spi_interrupt(ndev->spi); > + } else if (reg_isr & NETUP_UNIDVB_IRQ_DMA1) { > + iret = netup_dma_interrupt(&ndev->dma[0]); > + } else if (reg_isr & NETUP_UNIDVB_IRQ_DMA2) { > + iret = netup_dma_interrupt(&ndev->dma[1]); > + } else if (reg_isr & NETUP_UNIDVB_IRQ_CI) { > + writew(0x101, ndev->bmmio0 + CAM_CTRLSTAT_CLR); > + iret = IRQ_HANDLED; > + } else { > + dev_err(&pci_dev->dev, > + "%s(): unknown interrupt 0x%x\n", > + __func__, reg_isr); > + } > + } > + /* re-enable interrupts */ > + writel(AVL_IRQ_ENABLE, ndev->bmmio0 + AVL_PCIE_IENR); > + return iret; > +} > + > +static int netup_unidvb_buf_setup(struct videobuf_queue *q, > + unsigned int *count, unsigned int *size) > +{ > + struct netup_dma *dma = q->priv_data; > + struct netup_unidvb_dev *ndev = dma->ndev; > + > + dprintk("%s()\n", __func__); > + *count = NETUP_DMA_BLOCKS_COUNT * 10; > + *size = NETUP_DMA_PACKETS_COUNT * 188; > + return 0; > +} > + > +static int netup_unidvb_buf_prepare(struct videobuf_queue *q, > + struct videobuf_buffer *vb, enum v4l2_field field) > +{ > + struct netup_dma *dma = q->priv_data; > + struct device *dev = &dma->ndev->pci_dev->dev; > + struct netup_unidvb_buffer *buf = (struct netup_unidvb_buffer *)vb; > + > + dev_dbg(dev, "%s(): %p\n", __func__, buf); > + if (buf->vb.baddr != 0 && > + buf->vb.size < NETUP_DMA_PACKETS_COUNT * 188) { > + dev_err(dev, "%s(): invalid buffer size %d\n", > + __func__, (unsigned int)buf->vb.size); > + return -EINVAL; > + } > + if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { > + buf->vb.width = 188; > + buf->vb.height = NETUP_DMA_PACKETS_COUNT; > + buf->vb.size = NETUP_DMA_PACKETS_COUNT * 188; > + buf->vb.field = field; > + if (videobuf_iolock(q, &buf->vb, NULL)) { > + dev_err(dev, "%s(): unable to lock buffer\n", > + __func__); > + return -EINVAL; > + } > + } > + buf->vb.state = VIDEOBUF_PREPARED; > + buf->size = 0; > + return 0; > +} > + > +static void netup_unidvb_buf_queue(struct videobuf_queue *q, > + struct videobuf_buffer *vb) > +{ > + struct netup_dma *dma = q->priv_data; > + struct device *dev = &dma->ndev->pci_dev->dev; > + struct netup_unidvb_buffer *buf = (struct netup_unidvb_buffer *)vb; > + > + dev_dbg(dev, "%s(): %p\n", __func__, buf); > + buf->vb.state = VIDEOBUF_ACTIVE; > + buf->size = 0; > + list_add_tail(&buf->vb.queue, &dma->free_buffers); > + mod_timer(&dma->timeout, jiffies + HZ); > +} > + > +static void netup_unidvb_buf_release(struct videobuf_queue *q, > + struct videobuf_buffer *vb) > +{ > + struct netup_dma *dma = q->priv_data; > + struct device *dev = &dma->ndev->pci_dev->dev; > + struct netup_unidvb_buffer *buf = (struct netup_unidvb_buffer *)vb; > + > + dev_dbg(dev, "%s(): %p\n", __func__, buf); > + videobuf_waiton(q, &buf->vb, 0, 0); > + videobuf_vmalloc_free(&buf->vb); > + buf->vb.state = VIDEOBUF_NEEDS_INIT; > +} > + > +static struct videobuf_queue_ops dvb_qops = { > + .buf_setup = netup_unidvb_buf_setup, > + .buf_prepare = netup_unidvb_buf_prepare, > + .buf_queue = netup_unidvb_buf_queue, > + .buf_release = netup_unidvb_buf_release, > +}; > + > +static int netup_unidvb_dvb_init( > + struct netup_unidvb_dev *ndev, int num) > +{ > + struct videobuf_dvb_frontend *fe0, *fe1, *fe2; > + > + if (num < 0 || num > 1) { > + dprintk("%s(): unable to init DVB bus %d\n", > + __func__, num); > + return -ENODEV; > + } > + mutex_init(&ndev->frontends[num].lock); > + INIT_LIST_HEAD(&ndev->frontends[num].felist); > + if (videobuf_dvb_alloc_frontend( > + &ndev->frontends[num], 1) == NULL || > + videobuf_dvb_alloc_frontend( > + &ndev->frontends[num], 2) == NULL || > + videobuf_dvb_alloc_frontend( > + &ndev->frontends[num], 3) == NULL) { > + dprintk("%s(): unabto to alllocate videobuf_dvb_frontend\n", > + __func__); > + return -ENOMEM; > + } > + fe0 = videobuf_dvb_get_frontend(&ndev->frontends[num], 1); > + fe1 = videobuf_dvb_get_frontend(&ndev->frontends[num], 2); > + fe2 = videobuf_dvb_get_frontend(&ndev->frontends[num], 3); > + if (fe0 == NULL || fe1 == NULL || fe2 == NULL) { > + dprintk("%s(): frontends has not been allocated\n", > + __func__); > + return -EINVAL; > + } > + fe0->dvb.name = "netup_fe0"; > + fe1->dvb.name = "netup_fe1"; > + fe2->dvb.name = "netup_fe2"; > + videobuf_queue_vmalloc_init(&fe0->dvb.dvbq, &dvb_qops, > + &ndev->pci_dev->dev, &ndev->dma[num].lock, > + V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP, > + sizeof(struct netup_unidvb_buffer), &ndev->dma[num], NULL); > + videobuf_queue_vmalloc_init(&fe1->dvb.dvbq, &dvb_qops, > + &ndev->pci_dev->dev, &ndev->dma[num].lock, > + V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP, > + sizeof(struct netup_unidvb_buffer), &ndev->dma[num], NULL); > + videobuf_queue_vmalloc_init(&fe2->dvb.dvbq, &dvb_qops, > + &ndev->pci_dev->dev, &ndev->dma[num].lock, > + V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_TOP, > + sizeof(struct netup_unidvb_buffer), &ndev->dma[num], NULL); > + fe0->dvb.frontend = dvb_attach( > + cxd2841er_attach_s, &demod_config, &ndev->i2c[num].adap); > + if (fe0->dvb.frontend == NULL) { > + dprintk("%s(): unable to attach DVB-S/S2 frontend\n", > + __func__); > + goto frontend_detach; > + } > + horus3a_conf.set_tuner_priv = &ndev->dma[num]; > + if (!dvb_attach(horus3a_attach, fe0->dvb.frontend, > + &horus3a_conf, &ndev->i2c[num].adap)) { > + dprintk("%s(): unable to attach DVB-S/S2 tuner frontend\n", > + __func__); > + goto frontend_detach; > + } > + if (!dvb_attach(lnbh25_attach, fe0->dvb.frontend, > + &lnbh25_conf, &ndev->i2c[num].adap)) { > + dprintk("%s(): unable to attach SEC frontend\n", > + __func__); > + goto frontend_detach; > + } > + /* DVB-T/T2 frontend */ > + fe1->dvb.frontend = dvb_attach( > + cxd2841er_attach_t, &demod_config, &ndev->i2c[num].adap); > + if (fe1->dvb.frontend == NULL) { > + dprintk("%s(): unable to attach DVB-T frontend\n", > + __func__); > + goto frontend_detach; > + } > + fe1->dvb.frontend->id = 1; > + ascot2e_conf.set_tuner_priv = &ndev->dma[num]; > + if (!dvb_attach(ascot2e_attach, fe1->dvb.frontend, > + &ascot2e_conf, &ndev->i2c[num].adap)) { > + dprintk("%s(): unable to attach DVB-T tuner frontend\n", > + __func__); > + goto frontend_detach; > + } > + /* DVB-C/C2 frontend */ > + fe2->dvb.frontend = dvb_attach( > + cxd2841er_attach_c, &demod_config, &ndev->i2c[num].adap); > + if (fe2->dvb.frontend == NULL) { > + dprintk("%s(): unable to attach DVB-C frontend\n", > + __func__); > + goto frontend_detach; > + } > + fe2->dvb.frontend->id = 2; > + if (!dvb_attach(ascot2e_attach, fe2->dvb.frontend, > + &ascot2e_conf, &ndev->i2c[num].adap)) { > + dprintk("%s(): unable to attach DVB-T/C tuner frontend\n", > + __func__); > + goto frontend_detach; > + } > + > + if (videobuf_dvb_register_bus( > + &ndev->frontends[num], THIS_MODULE, NULL, > + &ndev->pci_dev->dev, adapter_nr, 1)) { > + dprintk("%s(): unable to register DVB bus %d\n", > + __func__, num); > + goto frontend_detach; > + } > + dev_info(&ndev->pci_dev->dev, "DVB init done, num=%d\n", num); > + return 0; > +frontend_detach: > + videobuf_dvb_dealloc_frontends(&ndev->frontends[num]); > + return -EINVAL; > +} > + > +static void netup_unidvb_dvb_fini(struct netup_unidvb_dev *ndev, int num) > +{ > + if (num < 0 || num > 1) { > + dev_err(&ndev->pci_dev->dev, > + "%s(): unable to unregister DVB bus %d\n", > + __func__, num); > + return; > + } > + videobuf_dvb_unregister_bus(&ndev->frontends[num]); > + dev_info(&ndev->pci_dev->dev, > + "%s(): DVB bus %d unregistered\n", __func__, num); > +} > + > +static int netup_unidvb_dvb_setup(struct netup_unidvb_dev *ndev) > +{ > + int res; > + > + res = netup_unidvb_dvb_init(ndev, 0); > + if (res) > + return res; > + res = netup_unidvb_dvb_init(ndev, 1); > + if (res) { > + netup_unidvb_dvb_fini(ndev, 0); > + return res; > + } > + return 0; > +} > + > +static int netup_unidvb_ring_copy( > + struct netup_dma *dma, struct netup_unidvb_buffer *buf) > +{ > + u32 copy_bytes, ring_bytes, buff_bytes = buf->vb.size - buf->size; > + u8 *p = videobuf_to_vmalloc(&buf->vb) + buf->size; > + struct netup_unidvb_dev *ndev = dma->ndev; > + > + if (p == NULL) { > + dprintk("%s(): buffer is NULL\n", __func__); > + return -EINVAL; > + } > + if (dma->data_offset + dma->data_size > dma->ring_buffer_size) { > + ring_bytes = dma->ring_buffer_size - dma->data_offset; > + copy_bytes = (ring_bytes > buff_bytes) ? > + buff_bytes : ring_bytes; > + memcpy_fromio(p, dma->addr_virt + dma->data_offset, copy_bytes); > + p += copy_bytes; > + buf->size += copy_bytes; > + buff_bytes -= copy_bytes; > + dma->data_size -= copy_bytes; > + dma->data_offset += copy_bytes; > + if (dma->data_offset == dma->ring_buffer_size) > + dma->data_offset = 0; > + } > + if (buff_bytes > 0) { > + ring_bytes = dma->data_size; > + copy_bytes = (ring_bytes > buff_bytes) ? > + buff_bytes : ring_bytes; > + memcpy_fromio(p, dma->addr_virt + dma->data_offset, copy_bytes); > + buf->size += copy_bytes; > + dma->data_size -= copy_bytes; > + dma->data_offset += copy_bytes; > + if (dma->data_offset == dma->ring_buffer_size) > + dma->data_offset = 0; > + } > + return 0; > +} > + > +static void netup_unidvb_dma_worker(struct work_struct *work) > +{ > + struct netup_dma *dma = container_of(work, struct netup_dma, work); > + struct netup_unidvb_dev *ndev = dma->ndev; > + struct netup_unidvb_buffer *buf; > + unsigned long flags; > + > + spin_lock_irqsave(&dma->lock, flags); > + if (dma->data_size == 0) { > + dprintk("%s(): data_size == 0\n", __func__); > + goto work_done; > + } > + while (dma->data_size > 0) { > + if (list_empty(&dma->free_buffers)) { > + dprintk("%s(): no free buffers\n", __func__); > + goto work_done; > + } > + buf = list_first_entry(&dma->free_buffers, > + struct netup_unidvb_buffer, vb.queue); > + if (buf->vb.size != NETUP_DMA_PACKETS_COUNT * 188) { > + dprintk("%s(): invalid buffer size %d\n", > + __func__, (unsigned int)buf->vb.size); > + goto work_done; > + } > + if (buf->size >= buf->vb.size) { > + dprintk("%s(): buffer overflow, size %d\n", > + __func__, buf->size); > + goto work_done; > + } > + if (netup_unidvb_ring_copy(dma, buf)) > + goto work_done; > + if (buf->size == buf->vb.size) { > + list_del(&buf->vb.queue); > + do_gettimeofday(&buf->vb.ts); > + buf->vb.state = VIDEOBUF_DONE; > + wake_up(&buf->vb.done); > + } > + } > +work_done: > + dma->data_size = 0; > + spin_unlock_irqrestore(&dma->lock, flags); > +} > + > +static void netup_unidvb_dma_timeout(unsigned long data) > +{ > + struct netup_dma *dma = (struct netup_dma *)data; > + struct netup_unidvb_dev *ndev = dma->ndev; > + struct netup_unidvb_buffer *buf; > + unsigned long flags; > + > + dprintk("%s()\n", __func__); > + spin_lock_irqsave(&dma->lock, flags); > + while (!list_empty(&dma->free_buffers)) { > + buf = list_first_entry(&dma->free_buffers, > + struct netup_unidvb_buffer, vb.queue); > + list_del(&buf->vb.queue); > + buf->vb.state = VIDEOBUF_ERROR; > + wake_up(&buf->vb.done); > + } > + spin_unlock_irqrestore(&dma->lock, flags); > +} > + > +static int netup_unidvb_dma_init(struct netup_unidvb_dev *ndev, int num) > +{ > + struct netup_dma *dma; > + struct device *dev = &ndev->pci_dev->dev; > + > + if (num < 0 || num > 1) { > + dev_err(dev, "%s(): unable to register DMA%d\n", > + __func__, num); > + return -ENODEV; > + } > + dma = &ndev->dma[num]; > + dev_info(dev, "%s(): starting DMA%d\n", __func__, num); > + dma->num = num; > + dma->ndev = ndev; > + spin_lock_init(&dma->lock); > + INIT_WORK(&dma->work, netup_unidvb_dma_worker); > + INIT_LIST_HEAD(&dma->free_buffers); > + dma->timeout.function = netup_unidvb_dma_timeout; > + dma->timeout.data = (unsigned long)dma; > + init_timer(&dma->timeout); > + dma->ring_buffer_size = > + NETUP_DMA_BLOCKS_COUNT * NETUP_DMA_PACKETS_COUNT * 188; > + dma->addr_virt = dma_alloc_coherent( > + &ndev->pci_dev->dev, dma->ring_buffer_size, > + &dma->addr_phys, GFP_KERNEL); > + if (!dma->addr_virt) { > + dev_err(dev, "%s(): unable to allocate DMA%d buffer\n", > + __func__, num); > + return -ENOMEM; > + } > + dev_info(dev, "%s(): DMA%d buffer virt/phys 0x%p/0x%llx size %d\n", > + __func__, num, dma->addr_virt, > + (unsigned long long)dma->addr_phys, > + dma->ring_buffer_size); > + memset_io(dma->addr_virt, 0, dma->ring_buffer_size); > + dma->addr_last = dma->addr_phys; > + dma->high_addr = (u32)(dma->addr_phys & 0xC0000000); > + dma->regs = (struct netup_dma_regs *)(num == 0 ? > + ndev->bmmio0 + NETUP_DMA0_ADDR : > + ndev->bmmio0 + NETUP_DMA1_ADDR); > + writel((NETUP_DMA_BLOCKS_COUNT << 24) | > + (NETUP_DMA_PACKETS_COUNT << 8) | 188, &dma->regs->size); > + writel((u32)(dma->addr_phys & 0x3FFFFFFF), &dma->regs->start_addr_lo); > + writel((u32)((u64)dma->addr_phys >> 32), &dma->regs->start_addr_hi); > + writel(375000000, &dma->regs->timeout); > + msleep(1000); > + writel(BIT_DMA_IRQ, &dma->regs->ctrlstat_clear); > + writel(dma->high_addr, ndev->bmmio0 + 0x1000); > + return 0; > +} > + > +static void netup_unidvb_dma_fini(struct netup_unidvb_dev *ndev, int num) > +{ > + struct netup_dma *dma; > + > + if (num < 0 || num > 1) > + return; > + dprintk("%s(): num %d\n", __func__, num); > + dma = &ndev->dma[num]; > + netup_unidvb_dma_enable(dma, 0); > + msleep(50); > + cancel_work_sync(&dma->work); > + del_timer(&dma->timeout); > + dma_free_coherent( > + &ndev->pci_dev->dev, dma->ring_buffer_size, > + dma->addr_virt, dma->addr_phys); > +} > + > +static int netup_unidvb_dma_setup(struct netup_unidvb_dev *ndev) > +{ > + int res; > + > + res = netup_unidvb_dma_init(ndev, 0); > + if (res) > + return res; > + res = netup_unidvb_dma_init(ndev, 1); > + if (res) { > + netup_unidvb_dma_fini(ndev, 0); > + return res; > + } > + netup_unidvb_dma_enable(&ndev->dma[0], 1); > + netup_unidvb_dma_enable(&ndev->dma[1], 1); > + return 0; > +} > + > +static int netup_unidvb_ci_setup( > + struct netup_unidvb_dev *ndev, struct pci_dev *pci_dev) > +{ > + int res; > + > + writew(NETUP_UNIDVB_IRQ_CI, ndev->bmmio0 + REG_IMASK_SET); > + res = netup_unidvb_ci_register(ndev, 0, pci_dev); > + if (res) > + return res; > + res = netup_unidvb_ci_register(ndev, 1, pci_dev); > + if (res) > + netup_unidvb_ci_unregister(ndev, 0); > + return res; > +} > + > +static int netup_unidvb_request_mmio(struct pci_dev *pci_dev) > +{ > + if (!request_mem_region(pci_resource_start(pci_dev, 0), > + pci_resource_len(pci_dev, 0), DRIVER_NAME)) { > + dev_err(&pci_dev->dev, > + "%s(): unable to request MMIO bar 0 at 0x%llx\n", > + __func__, > + (unsigned long long)pci_resource_start(pci_dev, 0)); > + return -EBUSY; > + } > + if (!request_mem_region(pci_resource_start(pci_dev, 1), > + pci_resource_len(pci_dev, 1), DRIVER_NAME)) { > + dev_err(&pci_dev->dev, > + "%s(): unable to request MMIO bar 1 at 0x%llx\n", > + __func__, > + (unsigned long long)pci_resource_start(pci_dev, 1)); > + release_mem_region(pci_resource_start(pci_dev, 0), > + pci_resource_len(pci_dev, 0)); > + return -EBUSY; > + } > + return 0; > +} > + > +static int netup_unidvb_request_modules(struct device *dev) > +{ > + static const char * const modules[] = { > + "lnbh25", "ascot2e", "horus3a", "cxd2841er", NULL > + }; > + const char * const *curr_mod = modules; > + int err; > + > + while (*curr_mod != NULL) { > + err = request_module(*curr_mod); > + if (err) { > + dev_warn(dev, "request_module(%s) failed: %d\n", > + *curr_mod, err); > + } > + ++curr_mod; > + } > + return 0; > +} > + > +static int netup_unidvb_initdev( > + struct pci_dev *pci_dev, const struct pci_device_id *pci_id) > +{ > + u8 board_revision; > + u16 board_vendor; > + struct netup_unidvb_dev *ndev; > + int old_firmware = 0; > + > + netup_unidvb_request_modules(&pci_dev->dev); > + > + /* Check card revision */ > + if (pci_dev->revision != NETUP_PCI_DEV_REVISION) { > + dev_err(&pci_dev->dev, > + "netup_unidvb: expected card revision %d, got %d\n", > + NETUP_PCI_DEV_REVISION, pci_dev->revision); > + dev_err(&pci_dev->dev, > + "Please upgrade firmware!\n"); > + dev_err(&pci_dev->dev, > + "Instructions on http://www.netup.tv\n"); > + old_firmware = 1; > + spi_enable = 1; > + } > + > + /* allocate device context */ > + ndev = kzalloc(sizeof(*ndev), GFP_KERNEL); > + > + if (!ndev) > + goto dev_alloc_err; > + memset(ndev, 0, sizeof(*ndev)); > + ndev->old_fw = old_firmware; > + ndev->wq = create_singlethread_workqueue(DRIVER_NAME); > + if (!ndev->wq) { > + dev_err(&pci_dev->dev, > + "%s(): unable to create workqueue\n", __func__); > + goto wq_create_err; > + } > + ndev->pci_dev = pci_dev; > + ndev->pci_bus = pci_dev->bus->number; > + ndev->pci_slot = PCI_SLOT(pci_dev->devfn); > + ndev->pci_func = PCI_FUNC(pci_dev->devfn); > + ndev->board_num = ndev->pci_bus*10 + ndev->pci_slot; > + pci_set_drvdata(pci_dev, ndev); > + /* PCI init */ > + dev_info(&pci_dev->dev, "%s(): PCI device (%d). Bus:0x%x Slot:0x%x\n", > + __func__, ndev->board_num, ndev->pci_bus, ndev->pci_slot); > + > + if (pci_enable_device(pci_dev)) { > + dev_err(&pci_dev->dev, "%s(): pci_enable_device failed\n", > + __func__); > + goto pci_enable_err; > + } > + /* read PCI info */ > + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &board_revision); > + pci_read_config_word(pci_dev, PCI_VENDOR_ID, &board_vendor); > + if (board_vendor != NETUP_VENDOR_ID) { > + dev_err(&pci_dev->dev, "%s(): unknown board vendor 0x%x", > + __func__, board_vendor); > + goto pci_detect_err; > + } > + dev_info(&pci_dev->dev, > + "%s(): board vendor 0x%x, revision 0x%x\n", > + __func__, board_vendor, board_revision); > + pci_set_master(pci_dev); > + if (!pci_dma_supported(pci_dev, 0xffffffff)) { > + dev_err(&pci_dev->dev, "%s(): 32bit PCI DMA is not supported\n", > + __func__); > + goto pci_detect_err; > + } > + dev_info(&pci_dev->dev, "%s(): using 32bit PCI DMA\n", __func__); > + /* Clear "no snoop" and "relaxed ordering" bits, use default MRRS. */ > + pcie_capability_clear_and_set_word(pci_dev, PCI_EXP_DEVCTL, > + PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_RELAX_EN | > + PCI_EXP_DEVCTL_NOSNOOP_EN, 0); > + /* Adjust PCIe completion timeout. */ > + pcie_capability_clear_and_set_word( > + pci_dev, PCI_EXP_DEVCTL2, 0xf, 0x2); > + > + if (netup_unidvb_request_mmio(pci_dev)) { > + dev_err(&pci_dev->dev, > + "%s(): unable to request MMIO regions\n", __func__); > + goto pci_detect_err; > + } > + ndev->lmmio0 = ioremap(pci_resource_start(pci_dev, 0), > + pci_resource_len(pci_dev, 0)); > + if (!ndev->lmmio0) { > + dev_err(&pci_dev->dev, > + "%s(): unable to remap MMIO bar 0\n", __func__); > + goto pci_bar0_error; > + } > + ndev->lmmio1 = ioremap(pci_resource_start(pci_dev, 1), > + pci_resource_len(pci_dev, 1)); > + if (!ndev->lmmio1) { > + dev_err(&pci_dev->dev, > + "%s(): unable to remap MMIO bar 1\n", __func__); > + goto pci_bar1_error; > + } > + ndev->bmmio0 = (u8 __iomem *)ndev->lmmio0; > + ndev->bmmio1 = (u8 __iomem *)ndev->lmmio1; > + dev_info(&pci_dev->dev, > + "%s(): PCI MMIO at 0x%p (%d); 0x%p (%d); IRQ %d", > + __func__, > + ndev->lmmio0, (u32)pci_resource_len(pci_dev, 0), > + ndev->lmmio1, (u32)pci_resource_len(pci_dev, 1), > + pci_dev->irq); > + if (request_irq(pci_dev->irq, netup_unidvb_isr, > + IRQF_SHARED | IRQF_DISABLED, > + "netup_unidvb", pci_dev) < 0) { > + dev_err(&pci_dev->dev, > + "%s(): can't get IRQ %d\n", __func__, pci_dev->irq); > + goto irq_request_err; > + } > + netup_unidvb_dev_enable(ndev); > + if (spi_enable && netup_spi_init(ndev)) { > + dev_warn(&pci_dev->dev, > + "netup_unidvb: SPI flash setup failed\n"); > + goto spi_setup_err; > + } > + if (old_firmware) { > + dev_err(&pci_dev->dev, > + "netup_unidvb: card initialization was incomplete\n"); > + return 0; > + } > + if (netup_i2c_register(ndev)) { > + dev_err(&pci_dev->dev, "netup_unidvb: I2C setup failed\n"); > + goto i2c_setup_err; > + } > + /* enable I2C IRQs */ > + writew(NETUP_UNIDVB_IRQ_I2C0 | NETUP_UNIDVB_IRQ_I2C1, > + ndev->bmmio0 + REG_IMASK_SET); > + usleep_range(5000, 10000); > + if (netup_unidvb_dvb_setup(ndev)) { > + dev_err(&pci_dev->dev, "netup_unidvb: DVB setup failed\n"); > + goto dvb_setup_err; > + } > + if (netup_unidvb_ci_setup(ndev, pci_dev)) { > + dev_err(&pci_dev->dev, "netup_unidvb: CI setup failed\n"); > + goto ci_setup_err; > + } > + if (netup_unidvb_dma_setup(ndev)) { > + dev_err(&pci_dev->dev, "netup_unidvb: DMA setup failed\n"); > + goto dma_setup_err; > + } > + dev_info(&pci_dev->dev, "netup_unidvb: device has been initialized\n"); > + return 0; > +dma_setup_err: > + netup_unidvb_ci_unregister(ndev, 0); > + netup_unidvb_ci_unregister(ndev, 1); > +ci_setup_err: > + netup_unidvb_dvb_fini(ndev, 0); > + netup_unidvb_dvb_fini(ndev, 1); > +dvb_setup_err: > + netup_i2c_unregister(ndev); > +i2c_setup_err: > + if (ndev->spi) > + netup_spi_release(ndev); > +spi_setup_err: > + free_irq(pci_dev->irq, pci_dev); > +irq_request_err: > + iounmap(ndev->lmmio1); > +pci_bar1_error: > + iounmap(ndev->lmmio0); > +pci_bar0_error: > + release_mem_region( > + pci_resource_start(pci_dev, 0), > + pci_resource_len(pci_dev, 0)); > + release_mem_region( > + pci_resource_start(pci_dev, 1), > + pci_resource_len(pci_dev, 1)); > +pci_detect_err: > + pci_disable_device(pci_dev); > +pci_enable_err: > + pci_set_drvdata(pci_dev, NULL); > + destroy_workqueue(ndev->wq); > +wq_create_err: > + kfree(ndev); > +dev_alloc_err: > + dev_err(&pci_dev->dev, > + "%s(): failed to initizalize device\n", __func__); > + return -EIO; > +} > + > +static void netup_unidvb_finidev(struct pci_dev *pci_dev) > +{ > + struct netup_unidvb_dev *ndev = pci_get_drvdata(pci_dev); > + > + dev_info(&pci_dev->dev, "%s(): trying to stop device\n", __func__); > + if (!ndev->old_fw) { > + netup_unidvb_dma_fini(ndev, 0); > + netup_unidvb_dma_fini(ndev, 1); > + netup_unidvb_ci_unregister(ndev, 0); > + netup_unidvb_ci_unregister(ndev, 1); > + netup_unidvb_dvb_fini(ndev, 0); > + netup_unidvb_dvb_fini(ndev, 1); > + netup_i2c_unregister(ndev); > + } > + if (ndev->spi) > + netup_spi_release(ndev); > + writew(0xffff, ndev->bmmio0 + REG_IMASK_CLEAR); > + free_irq(pci_dev->irq, pci_dev); > + iounmap(ndev->lmmio0); > + iounmap(ndev->lmmio1); > + release_mem_region( > + pci_resource_start(pci_dev, 0), > + pci_resource_len(pci_dev, 0)); > + release_mem_region( > + pci_resource_start(pci_dev, 1), > + pci_resource_len(pci_dev, 1)); > + pci_disable_device(pci_dev); > + pci_set_drvdata(pci_dev, NULL); > + destroy_workqueue(ndev->wq); > + kfree(ndev); > + dev_info(&pci_dev->dev, > + "%s(): device has been successfully stopped\n", __func__); > +} > + > + > +static struct pci_device_id netup_unidvb_pci_tbl[] = { > + { PCI_DEVICE(0x1b55, 0x18f6) }, > + { 0, } > +}; > +MODULE_DEVICE_TABLE(pci, netup_unidvb_pci_tbl); > + > +static struct pci_driver netup_unidvb_pci_driver = { > + .name = "netup_unidvb", > + .id_table = netup_unidvb_pci_tbl, > + .probe = netup_unidvb_initdev, > + .remove = netup_unidvb_finidev, > + .suspend = NULL, > + .resume = NULL, > +}; > + > +static int __init netup_unidvb_init(void) > +{ > + return pci_register_driver(&netup_unidvb_pci_driver); > +} > + > +static void __exit netup_unidvb_fini(void) > +{ > + pci_unregister_driver(&netup_unidvb_pci_driver); > +} > + > +module_init(netup_unidvb_init); > +module_exit(netup_unidvb_fini); > diff --git a/drivers/media/pci/netup/netup_unidvb_i2c.c b/drivers/media/pci/netup/netup_unidvb_i2c.c > new file mode 100644 > index 0000000..7ebe652 > --- /dev/null > +++ b/drivers/media/pci/netup/netup_unidvb_i2c.c > @@ -0,0 +1,350 @@ > +/* > + * netup_unidvb_i2c.c > + * > + * Internal I2C bus driver for NetUP Universal Dual DVB-CI > + * > + * Copyright (C) 2014 NetUP Inc. > + * Copyright (C) 2014 Sergey Kozlov <serjk@xxxxxxxx> > + * Copyright (C) 2014 Abylay Ospan <aospan@xxxxxxxx> > + * > + * 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 <linux/module.h> > +#include <linux/moduleparam.h> > +#include <linux/init.h> > +#include <linux/delay.h> > +#include "netup_unidvb.h" > + > +static int i2c_debug; > +module_param(i2c_debug, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); > +MODULE_PARM_DESC(i2c_debug, "Debug NetUP I2C (default:no)"); > + > +#define dprintk(args...) \ > +do { \ > + if (i2c_debug) \ > + dev_dbg(i2c->adap.dev.parent, args); \ > +} while (0) This is not very nice, as, in order to enable debug, both this parameter and dynamic printk should be enabled via /sys/kernel/debug/dynamic_debug. Just use dev_dbg() directly. > + > +/* twi_ctrl0_stat reg bits */ > +#define TWI_IRQEN_COMPL 0x1 > +#define TWI_IRQEN_ANACK 0x2 > +#define TWI_IRQEN_DNACK 0x4 > +#define TWI_IRQ_COMPL (TWI_IRQEN_COMPL << 8) > +#define TWI_IRQ_ANACK (TWI_IRQEN_ANACK << 8) > +#define TWI_IRQ_DNACK (TWI_IRQEN_DNACK << 8) > +#define TWI_IRQ_TX 0x800 > +#define TWI_IRQ_RX 0x1000 > +#define TWI_IRQEN (TWI_IRQEN_COMPL | TWI_IRQEN_ANACK | TWI_IRQEN_DNACK) > +/* twi_addr_ctrl1 reg bits*/ > +#define TWI_TRANSFER 0x100 > +#define TWI_NOSTOP 0x200 > +#define TWI_SOFT_RESET 0x2000 > +/* twi_clkdiv reg value */ > +#define TWI_CLKDIV 156 > +/* fifo_stat_ctrl reg bits */ > +#define FIFO_IRQEN 0x8000 > +#define FIFO_RESET 0x4000 > + > +#define FIFO_SIZE 16 > + > +irqreturn_t netup_i2c_interrupt(struct netup_i2c *i2c) > +{ > + u16 reg, tmp; > + unsigned long flags; > + irqreturn_t iret = IRQ_HANDLED; > + > + spin_lock_irqsave(&i2c->lock, flags); > + reg = readw(&i2c->regs->twi_ctrl0_stat); > + writew(reg & ~TWI_IRQEN, &i2c->regs->twi_ctrl0_stat); > + dprintk("%s(): twi_ctrl0_state 0x%x\n", __func__, reg); > + if ((reg & TWI_IRQEN_COMPL) != 0 && (reg & TWI_IRQ_COMPL)) { > + dprintk("%s(): TWI_IRQEN_COMPL\n", __func__); > + i2c->state = STATE_DONE; > + goto irq_ok; > + } > + if ((reg & TWI_IRQEN_ANACK) != 0 && (reg & TWI_IRQ_ANACK)) { > + dprintk("%s(): TWI_IRQEN_ANACK\n", __func__); > + i2c->state = STATE_ERROR; > + goto irq_ok; > + } > + if ((reg & TWI_IRQEN_DNACK) != 0 && (reg & TWI_IRQ_DNACK)) { > + dprintk("%s(): TWI_IRQEN_DNACK\n", __func__); > + i2c->state = STATE_ERROR; > + goto irq_ok; > + } > + if ((reg & TWI_IRQ_RX) != 0) { > + tmp = readw(&i2c->regs->rx_fifo.stat_ctrl); > + writew(tmp & ~FIFO_IRQEN, &i2c->regs->rx_fifo.stat_ctrl); > + i2c->state = STATE_WANT_READ; > + dprintk("%s(): want read\n", __func__); > + goto irq_ok; > + } > + if ((reg & TWI_IRQ_TX) != 0) { > + tmp = readw(&i2c->regs->tx_fifo.stat_ctrl); > + writew(tmp & ~FIFO_IRQEN, &i2c->regs->tx_fifo.stat_ctrl); > + i2c->state = STATE_WANT_WRITE; > + dprintk("%s(): want write\n", __func__); > + goto irq_ok; > + } > + dev_warn(&i2c->adap.dev, "%s(): not mine interrupt\n", __func__); > + iret = IRQ_NONE; > +irq_ok: > + spin_unlock_irqrestore(&i2c->lock, flags); > + if (iret == IRQ_HANDLED) > + wake_up(&i2c->wq); > + return iret; > +} > + > +static void netup_i2c_reset(struct netup_i2c *i2c) > +{ > + dprintk("%s()\n", __func__); > + i2c->state = STATE_DONE; > + writew(TWI_SOFT_RESET, &i2c->regs->twi_addr_ctrl1); > + writew(TWI_CLKDIV, &i2c->regs->clkdiv); > + writew(FIFO_RESET, &i2c->regs->tx_fifo.stat_ctrl); > + writew(FIFO_RESET, &i2c->regs->rx_fifo.stat_ctrl); > + writew(0x800, &i2c->regs->tx_fifo.stat_ctrl); > + writew(0x800, &i2c->regs->rx_fifo.stat_ctrl); > +} > + > +static void netup_i2c_fifo_tx(struct netup_i2c *i2c) > +{ > + u8 data; > + u32 fifo_space = FIFO_SIZE - > + (readw(&i2c->regs->tx_fifo.stat_ctrl) & 0x3f); > + u32 msg_length = i2c->msg->len - i2c->xmit_size; > + > + msg_length = (msg_length < fifo_space ? msg_length : fifo_space); > + while (msg_length--) { > + data = i2c->msg->buf[i2c->xmit_size++]; > + writeb(data, &i2c->regs->tx_fifo.data8); > + dprintk("%s(): write 0x%02x\n", __func__, data); > + } > + if (i2c->xmit_size < i2c->msg->len) { > + dprintk("%s(): TX IRQ enabled\n", __func__); > + writew(readw(&i2c->regs->tx_fifo.stat_ctrl) | FIFO_IRQEN, > + &i2c->regs->tx_fifo.stat_ctrl); > + } > +} > + > +static void netup_i2c_fifo_rx(struct netup_i2c *i2c) > +{ > + u8 data; > + u32 fifo_size = readw(&i2c->regs->rx_fifo.stat_ctrl) & 0x3f; > + > + dprintk("%s(): RX fifo size %d\n", __func__, fifo_size); > + while (fifo_size--) { > + data = readb(&i2c->regs->rx_fifo.data8); > + if ((i2c->msg->flags & I2C_M_RD) != 0 && > + i2c->xmit_size < i2c->msg->len) { > + i2c->msg->buf[i2c->xmit_size++] = data; > + dprintk("%s(): read 0x%02x\n", __func__, data); > + } > + } > + if (i2c->xmit_size < i2c->msg->len) { > + dprintk("%s(): RX IRQ enabled\n", __func__); > + writew(readw(&i2c->regs->rx_fifo.stat_ctrl) | FIFO_IRQEN, > + &i2c->regs->rx_fifo.stat_ctrl); > + } > +} > + > +static void netup_i2c_start_xfer(struct netup_i2c *i2c) > +{ > + u16 rdflag = ((i2c->msg->flags & I2C_M_RD) ? 1 : 0); > + u16 reg = readw(&i2c->regs->twi_ctrl0_stat); > + > + writew(TWI_IRQEN | reg, &i2c->regs->twi_ctrl0_stat); > + writew(i2c->msg->len, &i2c->regs->length); > + writew(TWI_TRANSFER | (i2c->msg->addr << 1) | rdflag, > + &i2c->regs->twi_addr_ctrl1); > + dprintk("%s(): length %d twi_addr_ctrl1 0x%x twi_ctrl0_stat 0x%x\n", > + __func__, > + readw(&i2c->regs->length), > + readw(&i2c->regs->twi_addr_ctrl1), > + readw(&i2c->regs->twi_ctrl0_stat)); > + i2c->state = STATE_WAIT; > + i2c->xmit_size = 0; > + if (!rdflag) > + netup_i2c_fifo_tx(i2c); > + else > + writew(FIFO_IRQEN | readw(&i2c->regs->rx_fifo.stat_ctrl), > + &i2c->regs->rx_fifo.stat_ctrl); > +} > + > +static int netup_i2c_xfer( > + struct i2c_adapter *adap, struct i2c_msg *msgs, int num) > +{ > + unsigned long flags; > + int i, trans_done, res = num; > + struct netup_i2c *i2c = i2c_get_adapdata(adap); > + u16 reg; > + > + if (num <= 0) { > + dprintk("%s(): num == %d\n", __func__, num); > + return -EINVAL; > + } > + spin_lock_irqsave(&i2c->lock, flags); > + if (i2c->state != STATE_DONE) { > + dprintk("%s(): i2c->state == %d, resetting I2C\n", > + __func__, i2c->state); > + netup_i2c_reset(i2c); > + } > + dprintk("%s() num %d\n", __func__, num); > + for (i = 0; i < num; i++) { > + i2c->msg = &msgs[i]; > + netup_i2c_start_xfer(i2c); > + trans_done = 0; > + while (!trans_done) { > + spin_unlock_irqrestore(&i2c->lock, flags); > + if (wait_event_timeout( > + i2c->wq, > + i2c->state != STATE_WAIT, > + NETUP_I2C_TIMEOUT)) { > + spin_lock_irqsave(&i2c->lock, flags); > + switch (i2c->state) { > + case STATE_WANT_READ: > + netup_i2c_fifo_rx(i2c); > + break; > + case STATE_WANT_WRITE: > + netup_i2c_fifo_tx(i2c); > + break; > + case STATE_DONE: > + if ((i2c->msg->flags & I2C_M_RD) != 0 && > + i2c->xmit_size != i2c->msg->len) > + netup_i2c_fifo_rx(i2c); > + dprintk("%s(): msg %d OK\n", > + __func__, i); > + trans_done = 1; > + break; > + case STATE_ERROR: > + res = -EIO; > + dprintk("%s(): error state\n", > + __func__); > + goto done; > + default: > + dprintk("%s(): invalid state %d\n", > + __func__, i2c->state); > + res = -EINVAL; > + goto done; > + } > + if (!trans_done) { > + i2c->state = STATE_WAIT; > + reg = readw( > + &i2c->regs->twi_ctrl0_stat); > + writew(TWI_IRQEN | reg, > + &i2c->regs->twi_ctrl0_stat); > + } > + spin_unlock_irqrestore(&i2c->lock, flags); > + } else { > + spin_lock_irqsave(&i2c->lock, flags); > + dprintk("%s(): wait timeout\n", __func__); > + res = -ETIMEDOUT; > + goto done; > + } > + spin_lock_irqsave(&i2c->lock, flags); > + } > + } > +done: spin_unlock_irqrestore(&i2c->lock, flags); > + dprintk("%s(): result %d\n", __func__, res); > + return res; > +} > + > +static u32 netup_i2c_func(struct i2c_adapter *adap) > +{ > + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; > +} > + > +static const struct i2c_algorithm netup_i2c_algorithm = { > + .master_xfer = netup_i2c_xfer, > + .functionality = netup_i2c_func, > +}; > + > +static struct i2c_adapter netup_i2c_adapter = { > + .owner = THIS_MODULE, > + .name = NETUP_UNIDVB_NAME, > + .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, > + .algo = &netup_i2c_algorithm, > +}; > + > +static int netup_i2c_init(struct netup_unidvb_dev *ndev, int bus_num) > +{ > + int ret; > + struct netup_i2c *i2c; > + > + if (bus_num < 0 || bus_num > 1) { > + dev_err(&ndev->pci_dev->dev, > + "%s(): invalid bus_num %d\n", __func__, bus_num); > + return -EINVAL; > + } > + i2c = &ndev->i2c[bus_num]; > + spin_lock_init(&i2c->lock); > + init_waitqueue_head(&i2c->wq); > + i2c->regs = (struct netup_i2c_regs *)(ndev->bmmio0 + > + (bus_num == 0 ? NETUP_I2C_BUS0_ADDR : NETUP_I2C_BUS1_ADDR)); > + netup_i2c_reset(i2c); > + i2c->adap = netup_i2c_adapter; > + i2c->adap.dev.parent = &ndev->pci_dev->dev; > + i2c_set_adapdata(&i2c->adap, i2c); > + ret = i2c_add_adapter(&i2c->adap); > + if (ret) { > + dev_err(&ndev->pci_dev->dev, > + "%s(): failed to add I2C adapter\n", __func__); > + return ret; > + } > + dev_info(&ndev->pci_dev->dev, > + "%s(): registered I2C bus %d at 0x%x\n", > + __func__, > + bus_num, (bus_num == 0 ? > + NETUP_I2C_BUS0_ADDR : > + NETUP_I2C_BUS1_ADDR)); > + return 0; > +} > + > +static void netup_i2c_remove(struct netup_unidvb_dev *ndev, int bus_num) > +{ > + struct netup_i2c *i2c; > + > + if (bus_num < 0 || bus_num > 1) { > + dev_err(&ndev->pci_dev->dev, > + "%s(): invalid bus number %d\n", __func__, bus_num); > + return; > + } > + i2c = &ndev->i2c[bus_num]; > + netup_i2c_reset(i2c); > + msleep(20); This sleep here seems to be a hack... why this is needed? Is it due to some race condition? If so, the best would be to use kref() and put the i2c_del_adapter() code as the removal code, to be called after all users of the i2c bus to release it. > + /* remove adapter */ > + i2c_del_adapter(&i2c->adap); > + dev_info(&ndev->pci_dev->dev, > + "netup_i2c_remove: unregistered I2C bus %d\n", bus_num); > +} > + > +int netup_i2c_register(struct netup_unidvb_dev *ndev) > +{ > + int ret; > + > + ret = netup_i2c_init(ndev, 0); > + if (ret) > + return ret; > + ret = netup_i2c_init(ndev, 1); > + if (ret) { > + netup_i2c_remove(ndev, 0); > + return ret; > + } > + return 0; > +} > + > +void netup_i2c_unregister(struct netup_unidvb_dev *ndev) > +{ > + netup_i2c_remove(ndev, 0); > + netup_i2c_remove(ndev, 1); > +} > + > diff --git a/drivers/media/pci/netup/netup_unidvb_spi.c b/drivers/media/pci/netup/netup_unidvb_spi.c > new file mode 100644 > index 0000000..d22158c > --- /dev/null > +++ b/drivers/media/pci/netup/netup_unidvb_spi.c > @@ -0,0 +1,272 @@ > +/* > + * netup_unidvb_spi.c > + * > + * Internal SPI driver for NetUP Universal Dual DVB-CI > + * > + * Copyright (C) 2014 NetUP Inc. > + * Copyright (C) 2014 Sergey Kozlov <serjk@xxxxxxxx> > + * Copyright (C) 2014 Abylay Ospan <aospan@xxxxxxxx> > + * > + * 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 (6*HZ) > + > +static int spi_debug; > +module_param(spi_debug, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); > +MODULE_PARM_DESC(i2c_debug, "Debug NetUP SPI bus (default:no)"); > + > +#define dprintk(args...) \ > + do { \ > + if (spi_debug) \ > + dev_dbg(&spi->master->dev, args); \ > + } while (0) This is not very nice, as, in order to enable debug, both this parameter and dynamic printk should be enabled via /sys/kernel/debug/dynamic_debug. Just use dev_dbg() directly. > + > +enum netup_spi_state { > + SPI_STATE_START, > + SPI_STATE_DONE, > +}; > + > +struct netup_spi_regs { > + u8 data[1024]; > + u16 control_stat; > + u16 clock_divider; > +} __packed __aligned(1); > + > +struct netup_spi { > + struct device *dev; > + struct spi_master *master; > + struct netup_spi_regs *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) { > + dprintk("%s(): SPI not initialized\n", __func__); > + 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); > + dprintk("%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); > + dprintk("%s(): SPI interrupt handled\n", __func__); > + return IRQ_HANDLED; > +} > + > +static void netup_spi_dump(struct netup_spi *spi, u8 *buf, u32 len, int tx) > +{ > + u32 i; > + > + if (!spi_debug) > + return; > + dprintk("%s(): %s data len %d\n", > + __func__, (tx == 0 ? "RX" : "TX"), len); > + for (i = 0; i < len; i++) > + dprintk("%02x\n", *(buf + i)); > +} > + > +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); > + netup_spi_dump(spi, > + (u8 *)t->tx_buf + frag_offset, > + frag_size, 1); > + } 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); > + dprintk("%s(): control_stat 0x%04x\n", > + __func__, readw(&spi->regs->control_stat)); > + wait_event_timeout(spi->waitq, > + spi->state != SPI_STATE_START, > + 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); > + netup_spi_dump(spi, > + (u8 *)t->rx_buf + frag_offset, > + frag_size, 0); > + } > + } else { > + if (spi->state == SPI_STATE_START) { > + dprintk("%s(): transfer timeout\n", > + __func__); > + } else { > + dprintk("%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 *)(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) { > + dprintk("%s(): SPI not initialized\n", __func__); > + 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; > +} > + > + -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html