netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card driver
PATCH 5/5 netup_unidvb: NetUP Universal DVB-S/S2/T/T2/C PCI-E card driver
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
--- 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
MAINTAINERS +9 -0
@@ -6572,6 +6572,15 @@ F: include/net/netrom.h F: include/uapi/linux/netrom.h F: net/netrom/ +NETUP VIDEO4LINUX DRIVER +M: Sergey Kozlov <[email protected]> +L: [email protected] +W: http://linuxtv.org/ +W: http://netup.tv/ +T: git git://linuxtv.org/media_tree.git +S: Supported +F: drivers/media/pci/netup/* + NETWORK BLOCK DEVICE (NBD) M: Paul Clements <[email protected]> S: Maintained
drivers/media/pci/Kconfig +1 -0
@@ -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
drivers/media/pci/Makefile +2 -1
@@ -12,7 +12,8 @@ obj-y += ttpci/ \ ngene/ \ ddbridge/ \ saa7146/ \ - smipcie/ + smipcie/ \ + netup/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_ZORAN) += zoran/
drivers/media/pci/netup/Kconfig new file +12 -0
@@ -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 + 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. +
drivers/media/pci/netup/Makefile new file +9 -0
@@ -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
drivers/media/pci/netup/netup_unidvb.h new file +232 -0
232 lines not on this page · raw message
drivers/media/pci/netup/netup_unidvb_ci.c new file +248 -0
248 lines not on this page · raw message
drivers/media/pci/netup/netup_unidvb_core.c new file +919 -0
919 lines not on this page · raw message
drivers/media/pci/netup/netup_unidvb_i2c.c new file +350 -0
350 lines not on this page · raw message
drivers/media/pci/netup/netup_unidvb_spi.c new file +272 -0
272 lines not on this page · raw message
Signature · 1 line
1.7.10.4
Em Mon, 02 Feb 2015 12:22:32 +0300
Kozlov Sergey <[email protected]> escreveu:
Quoted 34 lines
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 firmwareSigned-off-by Kozlov Sergey <[email protected]>--- 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.cMAINTAINERS +0
-0
MAINTAINERS F: include/net/netrom.h · line 6575
Quoted 4 lines
MAINTAINERS +1
-0@@ -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
MAINTAINERS F: include/net/netrom.h · line 6582
Quoted 7 lines
MAINTAINERS +7
-0+M: Sergey Kozlov <[email protected]> +L: [email protected] +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?
Quoted 36 lines
MAINTAINERS +1
-0+ NETWORK BLOCK DEVICE (NBD) M: Paul Clements <[email protected]> S: Maintaineddrivers/media/pci/Kconfig +1
-0@@ -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_SUPPORTdrivers/media/pci/Makefile +2
-1@@ -12,7 +12,8 @@ obj-y += ttpci/ \ ngene/ \ ddbridge/ \ saa7146/ \ - smipcie/ + smipcie/ \ + netup/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_ZORAN) += zoran/drivers/media/pci/netup/Kconfig new file +0
-0
drivers/media/pci/netup/Kconfig line 5
Quoted 5 lines
drivers/media/pci/netup/Kconfig new file +5
-0@@ -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.
Quoted 178 lines
drivers/media/pci/netup/Kconfig new file +7
-0+ 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. +drivers/media/pci/netup/Makefile new file +9
-0@@ -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-frontendsdrivers/media/pci/netup/netup_unidvb.h new file +150
-0@@ -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 <[email protected]> +* Copyright (C) 2014 Abylay Ospan <[email protected]> +* +* 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) ; +
drivers/media/pci/netup/netup_unidvb.h line 160
Quoted 10 lines
drivers/media/pci/netup/netup_unidvb.h new file +10
-0+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.
Quoted 107 lines
drivers/media/pci/netup/netup_unidvb.h new file +72
-0+ +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);drivers/media/pci/netup/netup_unidvb_ci.c new file +29
-0@@ -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 <[email protected]> +* Copyright (C) 2014 Abylay Ospan <[email protected]> +* +* 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"
drivers/media/pci/netup/netup_unidvb_ci.c line 39
Quoted 10 lines
drivers/media/pci/netup/netup_unidvb_ci.c new file +10
-0+ +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.
Quoted 58 lines
drivers/media/pci/netup/netup_unidvb_ci.c new file +58
-0+ +/* 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;
drivers/media/pci/netup/netup_unidvb_ci.c line 107
Quoted 10 lines
drivers/media/pci/netup/netup_unidvb_ci.c new file +10
-0+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.
Quoted 193 lines
drivers/media/pci/netup/netup_unidvb_ci.c new file +141
-0+/* 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); +} +drivers/media/pci/netup/netup_unidvb_core.c new file +46
-0@@ -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 <[email protected]> +* Copyright (C) 2014 Abylay Ospan <[email protected]> +* +* 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 ("[email protected]" ); +MODULE_VERSION (NETUP_UNIDVB_VERSION ); +MODULE_LICENSE ("GPL" ); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR (adapter_nr); + +#define DRIVER_NAME "netup_unidvb" +
drivers/media/pci/netup/netup_unidvb_core.c line 56
Quoted 10 lines
drivers/media/pci/netup/netup_unidvb_core.c new file +10
-0+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.
Quoted 894 lines
drivers/media/pci/netup/netup_unidvb_core.c new file +863
-0+ +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);drivers/media/pci/netup/netup_unidvb_i2c.c new file +25
-0@@ -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 <[email protected]> +* Copyright (C) 2014 Abylay Ospan <[email protected]> +* +* 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"
drivers/media/pci/netup/netup_unidvb_i2c.c line 35
Quoted 10 lines
drivers/media/pci/netup/netup_unidvb_i2c.c new file +10
-0+ +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.
Quoted 278 lines
drivers/media/pci/netup/netup_unidvb_i2c.c new file +278
-0+ +/* 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) +{
drivers/media/pci/netup/netup_unidvb_i2c.c line 323
Quoted 10 lines
drivers/media/pci/netup/netup_unidvb_i2c.c new file +10
-0+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.
Quoted 65 lines
drivers/media/pci/netup/netup_unidvb_i2c.c new file +27
-0+/* 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 ); +} +drivers/media/pci/netup/netup_unidvb_spi.c new file +32
-0@@ -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 <[email protected]> +* Copyright (C) 2014 Abylay Ospan <[email protected]> +* +* 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)
drivers/media/pci/netup/netup_unidvb_spi.c line 42
Quoted 10 lines
drivers/media/pci/netup/netup_unidvb_spi.c new file +10
-0+ +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.
Quoted 230 lines
drivers/media/pci/netup/netup_unidvb_spi.c new file +230
-0+ +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 ; +} + +