18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * cimax2.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * CIMax2(R) SP2 driver in conjunction with NetUp Dual DVB-S2 CI card 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2009 NetUP Inc. 88c2ecf20Sopenharmony_ci * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru> 98c2ecf20Sopenharmony_ci * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "cx23885.h" 138c2ecf20Sopenharmony_ci#include "cimax2.h" 148c2ecf20Sopenharmony_ci#include <media/dvb_ca_en50221.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* Max transfer size done by I2C transfer functions */ 178c2ecf20Sopenharmony_ci#define MAX_XFER_SIZE 64 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/**** Bit definitions for MC417_RWD and MC417_OEN registers *** 208c2ecf20Sopenharmony_ci bits 31-16 218c2ecf20Sopenharmony_ci+-----------+ 228c2ecf20Sopenharmony_ci| Reserved | 238c2ecf20Sopenharmony_ci+-----------+ 248c2ecf20Sopenharmony_ci bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8 258c2ecf20Sopenharmony_ci+-------+-------+-------+-------+-------+-------+-------+-------+ 268c2ecf20Sopenharmony_ci| WR# | RD# | | ACK# | ADHI | ADLO | CS1# | CS0# | 278c2ecf20Sopenharmony_ci+-------+-------+-------+-------+-------+-------+-------+-------+ 288c2ecf20Sopenharmony_ci bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0 298c2ecf20Sopenharmony_ci+-------+-------+-------+-------+-------+-------+-------+-------+ 308c2ecf20Sopenharmony_ci| DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0| 318c2ecf20Sopenharmony_ci+-------+-------+-------+-------+-------+-------+-------+-------+ 328c2ecf20Sopenharmony_ci***/ 338c2ecf20Sopenharmony_ci/* MC417 */ 348c2ecf20Sopenharmony_ci#define NETUP_DATA 0x000000ff 358c2ecf20Sopenharmony_ci#define NETUP_WR 0x00008000 368c2ecf20Sopenharmony_ci#define NETUP_RD 0x00004000 378c2ecf20Sopenharmony_ci#define NETUP_ACK 0x00001000 388c2ecf20Sopenharmony_ci#define NETUP_ADHI 0x00000800 398c2ecf20Sopenharmony_ci#define NETUP_ADLO 0x00000400 408c2ecf20Sopenharmony_ci#define NETUP_CS1 0x00000200 418c2ecf20Sopenharmony_ci#define NETUP_CS0 0x00000100 428c2ecf20Sopenharmony_ci#define NETUP_EN_ALL 0x00001000 438c2ecf20Sopenharmony_ci#define NETUP_CTRL_OFF (NETUP_CS1 | NETUP_CS0 | NETUP_WR | NETUP_RD) 448c2ecf20Sopenharmony_ci#define NETUP_CI_CTL 0x04 458c2ecf20Sopenharmony_ci#define NETUP_CI_RD 1 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci#define NETUP_IRQ_DETAM 0x1 488c2ecf20Sopenharmony_ci#define NETUP_IRQ_IRQAM 0x4 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic unsigned int ci_dbg; 518c2ecf20Sopenharmony_cimodule_param(ci_dbg, int, 0644); 528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ci_dbg, "Enable CI debugging"); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic unsigned int ci_irq_enable; 558c2ecf20Sopenharmony_cimodule_param(ci_irq_enable, int, 0644); 568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ci_irq_enable, "Enable IRQ from CAM"); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define ci_dbg_print(fmt, args...) \ 598c2ecf20Sopenharmony_ci do { \ 608c2ecf20Sopenharmony_ci if (ci_dbg) \ 618c2ecf20Sopenharmony_ci printk(KERN_DEBUG pr_fmt("%s: " fmt), \ 628c2ecf20Sopenharmony_ci __func__, ##args); \ 638c2ecf20Sopenharmony_ci } while (0) 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci#define ci_irq_flags() (ci_irq_enable ? NETUP_IRQ_IRQAM : 0) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* stores all private variables for communication with CI */ 688c2ecf20Sopenharmony_cistruct netup_ci_state { 698c2ecf20Sopenharmony_ci struct dvb_ca_en50221 ca; 708c2ecf20Sopenharmony_ci struct mutex ca_mutex; 718c2ecf20Sopenharmony_ci struct i2c_adapter *i2c_adap; 728c2ecf20Sopenharmony_ci u8 ci_i2c_addr; 738c2ecf20Sopenharmony_ci int status; 748c2ecf20Sopenharmony_ci struct work_struct work; 758c2ecf20Sopenharmony_ci void *priv; 768c2ecf20Sopenharmony_ci u8 current_irq_mode; 778c2ecf20Sopenharmony_ci int current_ci_flag; 788c2ecf20Sopenharmony_ci unsigned long next_status_checked_time; 798c2ecf20Sopenharmony_ci}; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int netup_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg, 838c2ecf20Sopenharmony_ci u8 *buf, int len) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci int ret; 868c2ecf20Sopenharmony_ci struct i2c_msg msg[] = { 878c2ecf20Sopenharmony_ci { 888c2ecf20Sopenharmony_ci .addr = addr, 898c2ecf20Sopenharmony_ci .flags = 0, 908c2ecf20Sopenharmony_ci .buf = ®, 918c2ecf20Sopenharmony_ci .len = 1 928c2ecf20Sopenharmony_ci }, { 938c2ecf20Sopenharmony_ci .addr = addr, 948c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 958c2ecf20Sopenharmony_ci .buf = buf, 968c2ecf20Sopenharmony_ci .len = len 978c2ecf20Sopenharmony_ci } 988c2ecf20Sopenharmony_ci }; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci ret = i2c_transfer(i2c_adap, msg, 2); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci if (ret != 2) { 1038c2ecf20Sopenharmony_ci ci_dbg_print("%s: i2c read error, Reg = 0x%02x, Status = %d\n", 1048c2ecf20Sopenharmony_ci __func__, reg, ret); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci return -1; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci ci_dbg_print("%s: i2c read Addr=0x%04x, Reg = 0x%02x, data = %02x\n", 1108c2ecf20Sopenharmony_ci __func__, addr, reg, buf[0]); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_cistatic int netup_write_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg, 1168c2ecf20Sopenharmony_ci u8 *buf, int len) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci int ret; 1198c2ecf20Sopenharmony_ci u8 buffer[MAX_XFER_SIZE]; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci struct i2c_msg msg = { 1228c2ecf20Sopenharmony_ci .addr = addr, 1238c2ecf20Sopenharmony_ci .flags = 0, 1248c2ecf20Sopenharmony_ci .buf = &buffer[0], 1258c2ecf20Sopenharmony_ci .len = len + 1 1268c2ecf20Sopenharmony_ci }; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (1 + len > sizeof(buffer)) { 1298c2ecf20Sopenharmony_ci pr_warn("%s: i2c wr reg=%04x: len=%d is too big!\n", 1308c2ecf20Sopenharmony_ci KBUILD_MODNAME, reg, len); 1318c2ecf20Sopenharmony_ci return -EINVAL; 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci buffer[0] = reg; 1358c2ecf20Sopenharmony_ci memcpy(&buffer[1], buf, len); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci ret = i2c_transfer(i2c_adap, &msg, 1); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (ret != 1) { 1408c2ecf20Sopenharmony_ci ci_dbg_print("%s: i2c write error, Reg=[0x%02x], Status=%d\n", 1418c2ecf20Sopenharmony_ci __func__, reg, ret); 1428c2ecf20Sopenharmony_ci return -1; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int netup_ci_get_mem(struct cx23885_dev *dev) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci int mem; 1518c2ecf20Sopenharmony_ci unsigned long timeout = jiffies + msecs_to_jiffies(1); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci for (;;) { 1548c2ecf20Sopenharmony_ci mem = cx_read(MC417_RWD); 1558c2ecf20Sopenharmony_ci if ((mem & NETUP_ACK) == 0) 1568c2ecf20Sopenharmony_ci break; 1578c2ecf20Sopenharmony_ci if (time_after(jiffies, timeout)) 1588c2ecf20Sopenharmony_ci break; 1598c2ecf20Sopenharmony_ci udelay(1); 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci cx_set(MC417_RWD, NETUP_CTRL_OFF); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return mem & 0xff; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, 1688c2ecf20Sopenharmony_ci u8 flag, u8 read, int addr, u8 data) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci struct netup_ci_state *state = en50221->data; 1718c2ecf20Sopenharmony_ci struct cx23885_tsport *port = state->priv; 1728c2ecf20Sopenharmony_ci struct cx23885_dev *dev = port->dev; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci u8 store; 1758c2ecf20Sopenharmony_ci int mem; 1768c2ecf20Sopenharmony_ci int ret; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci if (0 != slot) 1798c2ecf20Sopenharmony_ci return -EINVAL; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci if (state->current_ci_flag != flag) { 1828c2ecf20Sopenharmony_ci ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr, 1838c2ecf20Sopenharmony_ci 0, &store, 1); 1848c2ecf20Sopenharmony_ci if (ret != 0) 1858c2ecf20Sopenharmony_ci return ret; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci store &= ~0x0c; 1888c2ecf20Sopenharmony_ci store |= flag; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, 1918c2ecf20Sopenharmony_ci 0, &store, 1); 1928c2ecf20Sopenharmony_ci if (ret != 0) 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci } 1958c2ecf20Sopenharmony_ci state->current_ci_flag = flag; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci mutex_lock(&dev->gpio_lock); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci /* write addr */ 2008c2ecf20Sopenharmony_ci cx_write(MC417_OEN, NETUP_EN_ALL); 2018c2ecf20Sopenharmony_ci cx_write(MC417_RWD, NETUP_CTRL_OFF | 2028c2ecf20Sopenharmony_ci NETUP_ADLO | (0xff & addr)); 2038c2ecf20Sopenharmony_ci cx_clear(MC417_RWD, NETUP_ADLO); 2048c2ecf20Sopenharmony_ci cx_write(MC417_RWD, NETUP_CTRL_OFF | 2058c2ecf20Sopenharmony_ci NETUP_ADHI | (0xff & (addr >> 8))); 2068c2ecf20Sopenharmony_ci cx_clear(MC417_RWD, NETUP_ADHI); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci if (read) { /* data in */ 2098c2ecf20Sopenharmony_ci cx_write(MC417_OEN, NETUP_EN_ALL | NETUP_DATA); 2108c2ecf20Sopenharmony_ci } else /* data out */ 2118c2ecf20Sopenharmony_ci cx_write(MC417_RWD, NETUP_CTRL_OFF | data); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* choose chip */ 2148c2ecf20Sopenharmony_ci cx_clear(MC417_RWD, 2158c2ecf20Sopenharmony_ci (state->ci_i2c_addr == 0x40) ? NETUP_CS0 : NETUP_CS1); 2168c2ecf20Sopenharmony_ci /* read/write */ 2178c2ecf20Sopenharmony_ci cx_clear(MC417_RWD, (read) ? NETUP_RD : NETUP_WR); 2188c2ecf20Sopenharmony_ci mem = netup_ci_get_mem(dev); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci mutex_unlock(&dev->gpio_lock); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci if (!read) 2238c2ecf20Sopenharmony_ci if (mem < 0) 2248c2ecf20Sopenharmony_ci return -EREMOTEIO; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci ci_dbg_print("%s: %s: chipaddr=[0x%x] addr=[0x%02x], %s=%x\n", __func__, 2278c2ecf20Sopenharmony_ci (read) ? "read" : "write", state->ci_i2c_addr, addr, 2288c2ecf20Sopenharmony_ci (flag == NETUP_CI_CTL) ? "ctl" : "mem", 2298c2ecf20Sopenharmony_ci (read) ? mem : data); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci if (read) 2328c2ecf20Sopenharmony_ci return mem; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ciint netup_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221, 2388c2ecf20Sopenharmony_ci int slot, int addr) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci return netup_ci_op_cam(en50221, slot, 0, NETUP_CI_RD, addr, 0); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ciint netup_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221, 2448c2ecf20Sopenharmony_ci int slot, int addr, u8 data) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci return netup_ci_op_cam(en50221, slot, 0, 0, addr, data); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ciint netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, 2508c2ecf20Sopenharmony_ci u8 addr) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL, 2538c2ecf20Sopenharmony_ci NETUP_CI_RD, addr, 0); 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ciint netup_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, 2578c2ecf20Sopenharmony_ci u8 addr, u8 data) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL, 0, addr, data); 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ciint netup_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct netup_ci_state *state = en50221->data; 2658c2ecf20Sopenharmony_ci u8 buf = 0x80; 2668c2ecf20Sopenharmony_ci int ret; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (0 != slot) 2698c2ecf20Sopenharmony_ci return -EINVAL; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci udelay(500); 2728c2ecf20Sopenharmony_ci ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, 2738c2ecf20Sopenharmony_ci 0, &buf, 1); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (ret != 0) 2768c2ecf20Sopenharmony_ci return ret; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci udelay(500); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci buf = 0x00; 2818c2ecf20Sopenharmony_ci ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, 2828c2ecf20Sopenharmony_ci 0, &buf, 1); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci msleep(1000); 2858c2ecf20Sopenharmony_ci dvb_ca_en50221_camready_irq(&state->ca, 0); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ciint netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot) 2928c2ecf20Sopenharmony_ci{ 2938c2ecf20Sopenharmony_ci /* not implemented */ 2948c2ecf20Sopenharmony_ci return 0; 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_cistatic int netup_ci_set_irq(struct dvb_ca_en50221 *en50221, u8 irq_mode) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci struct netup_ci_state *state = en50221->data; 3008c2ecf20Sopenharmony_ci int ret; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (irq_mode == state->current_irq_mode) 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci ci_dbg_print("%s: chipaddr=[0x%x] setting ci IRQ to [0x%x] \n", 3068c2ecf20Sopenharmony_ci __func__, state->ci_i2c_addr, irq_mode); 3078c2ecf20Sopenharmony_ci ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, 3088c2ecf20Sopenharmony_ci 0x1b, &irq_mode, 1); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci if (ret != 0) 3118c2ecf20Sopenharmony_ci return ret; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci state->current_irq_mode = irq_mode; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ciint netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci struct netup_ci_state *state = en50221->data; 3218c2ecf20Sopenharmony_ci u8 buf; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (0 != slot) 3248c2ecf20Sopenharmony_ci return -EINVAL; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci netup_read_i2c(state->i2c_adap, state->ci_i2c_addr, 3278c2ecf20Sopenharmony_ci 0, &buf, 1); 3288c2ecf20Sopenharmony_ci buf |= 0x60; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, 3318c2ecf20Sopenharmony_ci 0, &buf, 1); 3328c2ecf20Sopenharmony_ci} 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci/* work handler */ 3358c2ecf20Sopenharmony_cistatic void netup_read_ci_status(struct work_struct *work) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci struct netup_ci_state *state = 3388c2ecf20Sopenharmony_ci container_of(work, struct netup_ci_state, work); 3398c2ecf20Sopenharmony_ci u8 buf[33]; 3408c2ecf20Sopenharmony_ci int ret; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* CAM module IRQ processing. fast operation */ 3438c2ecf20Sopenharmony_ci dvb_ca_en50221_frda_irq(&state->ca, 0); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* CAM module INSERT/REMOVE processing. slow operation because of i2c 3468c2ecf20Sopenharmony_ci * transfers */ 3478c2ecf20Sopenharmony_ci if (time_after(jiffies, state->next_status_checked_time) 3488c2ecf20Sopenharmony_ci || !state->status) { 3498c2ecf20Sopenharmony_ci ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr, 3508c2ecf20Sopenharmony_ci 0, &buf[0], 33); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci state->next_status_checked_time = jiffies 3538c2ecf20Sopenharmony_ci + msecs_to_jiffies(1000); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (ret != 0) 3568c2ecf20Sopenharmony_ci return; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci ci_dbg_print("%s: Slot Status Addr=[0x%04x], Reg=[0x%02x], data=%02x, TS config = %02x\n", 3598c2ecf20Sopenharmony_ci __func__, state->ci_i2c_addr, 0, buf[0], buf[0]); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci if (buf[0] & 1) 3638c2ecf20Sopenharmony_ci state->status = DVB_CA_EN50221_POLL_CAM_PRESENT | 3648c2ecf20Sopenharmony_ci DVB_CA_EN50221_POLL_CAM_READY; 3658c2ecf20Sopenharmony_ci else 3668c2ecf20Sopenharmony_ci state->status = 0; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci/* CI irq handler */ 3718c2ecf20Sopenharmony_ciint netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct cx23885_tsport *port = NULL; 3748c2ecf20Sopenharmony_ci struct netup_ci_state *state = NULL; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci ci_dbg_print("%s:\n", __func__); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (0 == (pci_status & (PCI_MSK_GPIO0 | PCI_MSK_GPIO1))) 3798c2ecf20Sopenharmony_ci return 0; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (pci_status & PCI_MSK_GPIO0) { 3828c2ecf20Sopenharmony_ci port = &dev->ts1; 3838c2ecf20Sopenharmony_ci state = port->port_priv; 3848c2ecf20Sopenharmony_ci schedule_work(&state->work); 3858c2ecf20Sopenharmony_ci ci_dbg_print("%s: Wakeup CI0\n", __func__); 3868c2ecf20Sopenharmony_ci } 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (pci_status & PCI_MSK_GPIO1) { 3898c2ecf20Sopenharmony_ci port = &dev->ts2; 3908c2ecf20Sopenharmony_ci state = port->port_priv; 3918c2ecf20Sopenharmony_ci schedule_work(&state->work); 3928c2ecf20Sopenharmony_ci ci_dbg_print("%s: Wakeup CI1\n", __func__); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci return 1; 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ciint netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, 3998c2ecf20Sopenharmony_ci int slot, int open) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci struct netup_ci_state *state = en50221->data; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (0 != slot) 4048c2ecf20Sopenharmony_ci return -EINVAL; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci netup_ci_set_irq(en50221, open ? (NETUP_IRQ_DETAM | ci_irq_flags()) 4078c2ecf20Sopenharmony_ci : NETUP_IRQ_DETAM); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return state->status; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ciint netup_ci_init(struct cx23885_tsport *port) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct netup_ci_state *state; 4158c2ecf20Sopenharmony_ci u8 cimax_init[34] = { 4168c2ecf20Sopenharmony_ci 0x00, /* module A control*/ 4178c2ecf20Sopenharmony_ci 0x00, /* auto select mask high A */ 4188c2ecf20Sopenharmony_ci 0x00, /* auto select mask low A */ 4198c2ecf20Sopenharmony_ci 0x00, /* auto select pattern high A */ 4208c2ecf20Sopenharmony_ci 0x00, /* auto select pattern low A */ 4218c2ecf20Sopenharmony_ci 0x44, /* memory access time A */ 4228c2ecf20Sopenharmony_ci 0x00, /* invert input A */ 4238c2ecf20Sopenharmony_ci 0x00, /* RFU */ 4248c2ecf20Sopenharmony_ci 0x00, /* RFU */ 4258c2ecf20Sopenharmony_ci 0x00, /* module B control*/ 4268c2ecf20Sopenharmony_ci 0x00, /* auto select mask high B */ 4278c2ecf20Sopenharmony_ci 0x00, /* auto select mask low B */ 4288c2ecf20Sopenharmony_ci 0x00, /* auto select pattern high B */ 4298c2ecf20Sopenharmony_ci 0x00, /* auto select pattern low B */ 4308c2ecf20Sopenharmony_ci 0x44, /* memory access time B */ 4318c2ecf20Sopenharmony_ci 0x00, /* invert input B */ 4328c2ecf20Sopenharmony_ci 0x00, /* RFU */ 4338c2ecf20Sopenharmony_ci 0x00, /* RFU */ 4348c2ecf20Sopenharmony_ci 0x00, /* auto select mask high Ext */ 4358c2ecf20Sopenharmony_ci 0x00, /* auto select mask low Ext */ 4368c2ecf20Sopenharmony_ci 0x00, /* auto select pattern high Ext */ 4378c2ecf20Sopenharmony_ci 0x00, /* auto select pattern low Ext */ 4388c2ecf20Sopenharmony_ci 0x00, /* RFU */ 4398c2ecf20Sopenharmony_ci 0x02, /* destination - module A */ 4408c2ecf20Sopenharmony_ci 0x01, /* power on (use it like store place) */ 4418c2ecf20Sopenharmony_ci 0x00, /* RFU */ 4428c2ecf20Sopenharmony_ci 0x00, /* int status read only */ 4438c2ecf20Sopenharmony_ci ci_irq_flags() | NETUP_IRQ_DETAM, /* DETAM, IRQAM unmasked */ 4448c2ecf20Sopenharmony_ci 0x05, /* EXTINT=active-high, INT=push-pull */ 4458c2ecf20Sopenharmony_ci 0x00, /* USCG1 */ 4468c2ecf20Sopenharmony_ci 0x04, /* ack active low */ 4478c2ecf20Sopenharmony_ci 0x00, /* LOCK = 0 */ 4488c2ecf20Sopenharmony_ci 0x33, /* serial mode, rising in, rising out, MSB first*/ 4498c2ecf20Sopenharmony_ci 0x31, /* synchronization */ 4508c2ecf20Sopenharmony_ci }; 4518c2ecf20Sopenharmony_ci int ret; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci ci_dbg_print("%s\n", __func__); 4548c2ecf20Sopenharmony_ci state = kzalloc(sizeof(struct netup_ci_state), GFP_KERNEL); 4558c2ecf20Sopenharmony_ci if (!state) { 4568c2ecf20Sopenharmony_ci ci_dbg_print("%s: Unable create CI structure!\n", __func__); 4578c2ecf20Sopenharmony_ci ret = -ENOMEM; 4588c2ecf20Sopenharmony_ci goto err; 4598c2ecf20Sopenharmony_ci } 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci port->port_priv = state; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci switch (port->nr) { 4648c2ecf20Sopenharmony_ci case 1: 4658c2ecf20Sopenharmony_ci state->ci_i2c_addr = 0x40; 4668c2ecf20Sopenharmony_ci break; 4678c2ecf20Sopenharmony_ci case 2: 4688c2ecf20Sopenharmony_ci state->ci_i2c_addr = 0x41; 4698c2ecf20Sopenharmony_ci break; 4708c2ecf20Sopenharmony_ci } 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci state->i2c_adap = &port->dev->i2c_bus[0].i2c_adap; 4738c2ecf20Sopenharmony_ci state->ca.owner = THIS_MODULE; 4748c2ecf20Sopenharmony_ci state->ca.read_attribute_mem = netup_ci_read_attribute_mem; 4758c2ecf20Sopenharmony_ci state->ca.write_attribute_mem = netup_ci_write_attribute_mem; 4768c2ecf20Sopenharmony_ci state->ca.read_cam_control = netup_ci_read_cam_ctl; 4778c2ecf20Sopenharmony_ci state->ca.write_cam_control = netup_ci_write_cam_ctl; 4788c2ecf20Sopenharmony_ci state->ca.slot_reset = netup_ci_slot_reset; 4798c2ecf20Sopenharmony_ci state->ca.slot_shutdown = netup_ci_slot_shutdown; 4808c2ecf20Sopenharmony_ci state->ca.slot_ts_enable = netup_ci_slot_ts_ctl; 4818c2ecf20Sopenharmony_ci state->ca.poll_slot_status = netup_poll_ci_slot_status; 4828c2ecf20Sopenharmony_ci state->ca.data = state; 4838c2ecf20Sopenharmony_ci state->priv = port; 4848c2ecf20Sopenharmony_ci state->current_irq_mode = ci_irq_flags() | NETUP_IRQ_DETAM; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, 4878c2ecf20Sopenharmony_ci 0, &cimax_init[0], 34); 4888c2ecf20Sopenharmony_ci /* lock registers */ 4898c2ecf20Sopenharmony_ci ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, 4908c2ecf20Sopenharmony_ci 0x1f, &cimax_init[0x18], 1); 4918c2ecf20Sopenharmony_ci /* power on slots */ 4928c2ecf20Sopenharmony_ci ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr, 4938c2ecf20Sopenharmony_ci 0x18, &cimax_init[0x18], 1); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci if (0 != ret) 4968c2ecf20Sopenharmony_ci goto err; 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci ret = dvb_ca_en50221_init(&port->frontends.adapter, 4998c2ecf20Sopenharmony_ci &state->ca, 5008c2ecf20Sopenharmony_ci /* flags */ 0, 5018c2ecf20Sopenharmony_ci /* n_slots */ 1); 5028c2ecf20Sopenharmony_ci if (0 != ret) 5038c2ecf20Sopenharmony_ci goto err; 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci INIT_WORK(&state->work, netup_read_ci_status); 5068c2ecf20Sopenharmony_ci schedule_work(&state->work); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci ci_dbg_print("%s: CI initialized!\n", __func__); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_cierr: 5128c2ecf20Sopenharmony_ci ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret); 5138c2ecf20Sopenharmony_ci kfree(state); 5148c2ecf20Sopenharmony_ci return ret; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_civoid netup_ci_exit(struct cx23885_tsport *port) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci struct netup_ci_state *state; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (NULL == port) 5228c2ecf20Sopenharmony_ci return; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci state = (struct netup_ci_state *)port->port_priv; 5258c2ecf20Sopenharmony_ci if (NULL == state) 5268c2ecf20Sopenharmony_ci return; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci if (NULL == state->ca.data) 5298c2ecf20Sopenharmony_ci return; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci dvb_ca_en50221_release(&state->ca); 5328c2ecf20Sopenharmony_ci kfree(state); 5338c2ecf20Sopenharmony_ci} 534