1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Driver for Feature Integration Technology Inc. (aka Fintek) LPC CIR
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2011 Jarod Wilson <jarod@redhat.com>
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Special thanks to Fintek for providing hardware and spec sheets.
8*4882a593Smuzhiyun * This driver is based upon the nuvoton, ite and ene drivers for
9*4882a593Smuzhiyun * similar hardware.
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include <linux/kernel.h>
15*4882a593Smuzhiyun #include <linux/module.h>
16*4882a593Smuzhiyun #include <linux/pnp.h>
17*4882a593Smuzhiyun #include <linux/io.h>
18*4882a593Smuzhiyun #include <linux/interrupt.h>
19*4882a593Smuzhiyun #include <linux/sched.h>
20*4882a593Smuzhiyun #include <linux/slab.h>
21*4882a593Smuzhiyun #include <media/rc-core.h>
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun #include "fintek-cir.h"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun /* write val to config reg */
fintek_cr_write(struct fintek_dev * fintek,u8 val,u8 reg)26*4882a593Smuzhiyun static inline void fintek_cr_write(struct fintek_dev *fintek, u8 val, u8 reg)
27*4882a593Smuzhiyun {
28*4882a593Smuzhiyun fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)",
29*4882a593Smuzhiyun __func__, reg, val, fintek->cr_ip, fintek->cr_dp);
30*4882a593Smuzhiyun outb(reg, fintek->cr_ip);
31*4882a593Smuzhiyun outb(val, fintek->cr_dp);
32*4882a593Smuzhiyun }
33*4882a593Smuzhiyun
34*4882a593Smuzhiyun /* read val from config reg */
fintek_cr_read(struct fintek_dev * fintek,u8 reg)35*4882a593Smuzhiyun static inline u8 fintek_cr_read(struct fintek_dev *fintek, u8 reg)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun u8 val;
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun outb(reg, fintek->cr_ip);
40*4882a593Smuzhiyun val = inb(fintek->cr_dp);
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun fit_dbg("%s: reg 0x%02x, val 0x%02x (ip/dp: %02x/%02x)",
43*4882a593Smuzhiyun __func__, reg, val, fintek->cr_ip, fintek->cr_dp);
44*4882a593Smuzhiyun return val;
45*4882a593Smuzhiyun }
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun /* update config register bit without changing other bits */
fintek_set_reg_bit(struct fintek_dev * fintek,u8 val,u8 reg)48*4882a593Smuzhiyun static inline void fintek_set_reg_bit(struct fintek_dev *fintek, u8 val, u8 reg)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun u8 tmp = fintek_cr_read(fintek, reg) | val;
51*4882a593Smuzhiyun fintek_cr_write(fintek, tmp, reg);
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun /* enter config mode */
fintek_config_mode_enable(struct fintek_dev * fintek)55*4882a593Smuzhiyun static inline void fintek_config_mode_enable(struct fintek_dev *fintek)
56*4882a593Smuzhiyun {
57*4882a593Smuzhiyun /* Enabling Config Mode explicitly requires writing 2x */
58*4882a593Smuzhiyun outb(CONFIG_REG_ENABLE, fintek->cr_ip);
59*4882a593Smuzhiyun outb(CONFIG_REG_ENABLE, fintek->cr_ip);
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun /* exit config mode */
fintek_config_mode_disable(struct fintek_dev * fintek)63*4882a593Smuzhiyun static inline void fintek_config_mode_disable(struct fintek_dev *fintek)
64*4882a593Smuzhiyun {
65*4882a593Smuzhiyun outb(CONFIG_REG_DISABLE, fintek->cr_ip);
66*4882a593Smuzhiyun }
67*4882a593Smuzhiyun
68*4882a593Smuzhiyun /*
69*4882a593Smuzhiyun * When you want to address a specific logical device, write its logical
70*4882a593Smuzhiyun * device number to GCR_LOGICAL_DEV_NO
71*4882a593Smuzhiyun */
fintek_select_logical_dev(struct fintek_dev * fintek,u8 ldev)72*4882a593Smuzhiyun static inline void fintek_select_logical_dev(struct fintek_dev *fintek, u8 ldev)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun fintek_cr_write(fintek, ldev, GCR_LOGICAL_DEV_NO);
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun /* write val to cir config register */
fintek_cir_reg_write(struct fintek_dev * fintek,u8 val,u8 offset)78*4882a593Smuzhiyun static inline void fintek_cir_reg_write(struct fintek_dev *fintek, u8 val, u8 offset)
79*4882a593Smuzhiyun {
80*4882a593Smuzhiyun outb(val, fintek->cir_addr + offset);
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun /* read val from cir config register */
fintek_cir_reg_read(struct fintek_dev * fintek,u8 offset)84*4882a593Smuzhiyun static u8 fintek_cir_reg_read(struct fintek_dev *fintek, u8 offset)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun return inb(fintek->cir_addr + offset);
87*4882a593Smuzhiyun }
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun /* dump current cir register contents */
cir_dump_regs(struct fintek_dev * fintek)90*4882a593Smuzhiyun static void cir_dump_regs(struct fintek_dev *fintek)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun fintek_config_mode_enable(fintek);
93*4882a593Smuzhiyun fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun pr_info("%s: Dump CIR logical device registers:\n", FINTEK_DRIVER_NAME);
96*4882a593Smuzhiyun pr_info(" * CR CIR BASE ADDR: 0x%x\n",
97*4882a593Smuzhiyun (fintek_cr_read(fintek, CIR_CR_BASE_ADDR_HI) << 8) |
98*4882a593Smuzhiyun fintek_cr_read(fintek, CIR_CR_BASE_ADDR_LO));
99*4882a593Smuzhiyun pr_info(" * CR CIR IRQ NUM: 0x%x\n",
100*4882a593Smuzhiyun fintek_cr_read(fintek, CIR_CR_IRQ_SEL));
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun fintek_config_mode_disable(fintek);
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun pr_info("%s: Dump CIR registers:\n", FINTEK_DRIVER_NAME);
105*4882a593Smuzhiyun pr_info(" * STATUS: 0x%x\n",
106*4882a593Smuzhiyun fintek_cir_reg_read(fintek, CIR_STATUS));
107*4882a593Smuzhiyun pr_info(" * CONTROL: 0x%x\n",
108*4882a593Smuzhiyun fintek_cir_reg_read(fintek, CIR_CONTROL));
109*4882a593Smuzhiyun pr_info(" * RX_DATA: 0x%x\n",
110*4882a593Smuzhiyun fintek_cir_reg_read(fintek, CIR_RX_DATA));
111*4882a593Smuzhiyun pr_info(" * TX_CONTROL: 0x%x\n",
112*4882a593Smuzhiyun fintek_cir_reg_read(fintek, CIR_TX_CONTROL));
113*4882a593Smuzhiyun pr_info(" * TX_DATA: 0x%x\n",
114*4882a593Smuzhiyun fintek_cir_reg_read(fintek, CIR_TX_DATA));
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun /* detect hardware features */
fintek_hw_detect(struct fintek_dev * fintek)118*4882a593Smuzhiyun static int fintek_hw_detect(struct fintek_dev *fintek)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun unsigned long flags;
121*4882a593Smuzhiyun u8 chip_major, chip_minor;
122*4882a593Smuzhiyun u8 vendor_major, vendor_minor;
123*4882a593Smuzhiyun u8 portsel, ir_class;
124*4882a593Smuzhiyun u16 vendor, chip;
125*4882a593Smuzhiyun
126*4882a593Smuzhiyun fintek_config_mode_enable(fintek);
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun /* Check if we're using config port 0x4e or 0x2e */
129*4882a593Smuzhiyun portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL);
130*4882a593Smuzhiyun if (portsel == 0xff) {
131*4882a593Smuzhiyun fit_pr(KERN_INFO, "first portsel read was bunk, trying alt");
132*4882a593Smuzhiyun fintek_config_mode_disable(fintek);
133*4882a593Smuzhiyun fintek->cr_ip = CR_INDEX_PORT2;
134*4882a593Smuzhiyun fintek->cr_dp = CR_DATA_PORT2;
135*4882a593Smuzhiyun fintek_config_mode_enable(fintek);
136*4882a593Smuzhiyun portsel = fintek_cr_read(fintek, GCR_CONFIG_PORT_SEL);
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun fit_dbg("portsel reg: 0x%02x", portsel);
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun ir_class = fintek_cir_reg_read(fintek, CIR_CR_CLASS);
141*4882a593Smuzhiyun fit_dbg("ir_class reg: 0x%02x", ir_class);
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun switch (ir_class) {
144*4882a593Smuzhiyun case CLASS_RX_2TX:
145*4882a593Smuzhiyun case CLASS_RX_1TX:
146*4882a593Smuzhiyun fintek->hw_tx_capable = true;
147*4882a593Smuzhiyun break;
148*4882a593Smuzhiyun case CLASS_RX_ONLY:
149*4882a593Smuzhiyun default:
150*4882a593Smuzhiyun fintek->hw_tx_capable = false;
151*4882a593Smuzhiyun break;
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun chip_major = fintek_cr_read(fintek, GCR_CHIP_ID_HI);
155*4882a593Smuzhiyun chip_minor = fintek_cr_read(fintek, GCR_CHIP_ID_LO);
156*4882a593Smuzhiyun chip = chip_major << 8 | chip_minor;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun vendor_major = fintek_cr_read(fintek, GCR_VENDOR_ID_HI);
159*4882a593Smuzhiyun vendor_minor = fintek_cr_read(fintek, GCR_VENDOR_ID_LO);
160*4882a593Smuzhiyun vendor = vendor_major << 8 | vendor_minor;
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun if (vendor != VENDOR_ID_FINTEK)
163*4882a593Smuzhiyun fit_pr(KERN_WARNING, "Unknown vendor ID: 0x%04x", vendor);
164*4882a593Smuzhiyun else
165*4882a593Smuzhiyun fit_dbg("Read Fintek vendor ID from chip");
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun fintek_config_mode_disable(fintek);
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun spin_lock_irqsave(&fintek->fintek_lock, flags);
170*4882a593Smuzhiyun fintek->chip_major = chip_major;
171*4882a593Smuzhiyun fintek->chip_minor = chip_minor;
172*4882a593Smuzhiyun fintek->chip_vendor = vendor;
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun /*
175*4882a593Smuzhiyun * Newer reviews of this chipset uses port 8 instead of 5
176*4882a593Smuzhiyun */
177*4882a593Smuzhiyun if ((chip != 0x0408) && (chip != 0x0804))
178*4882a593Smuzhiyun fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV2;
179*4882a593Smuzhiyun else
180*4882a593Smuzhiyun fintek->logical_dev_cir = LOGICAL_DEV_CIR_REV1;
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun spin_unlock_irqrestore(&fintek->fintek_lock, flags);
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun return 0;
185*4882a593Smuzhiyun }
186*4882a593Smuzhiyun
fintek_cir_ldev_init(struct fintek_dev * fintek)187*4882a593Smuzhiyun static void fintek_cir_ldev_init(struct fintek_dev *fintek)
188*4882a593Smuzhiyun {
189*4882a593Smuzhiyun /* Select CIR logical device and enable */
190*4882a593Smuzhiyun fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
191*4882a593Smuzhiyun fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun /* Write allocated CIR address and IRQ information to hardware */
194*4882a593Smuzhiyun fintek_cr_write(fintek, fintek->cir_addr >> 8, CIR_CR_BASE_ADDR_HI);
195*4882a593Smuzhiyun fintek_cr_write(fintek, fintek->cir_addr & 0xff, CIR_CR_BASE_ADDR_LO);
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun fintek_cr_write(fintek, fintek->cir_irq, CIR_CR_IRQ_SEL);
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun fit_dbg("CIR initialized, base io address: 0x%lx, irq: %d (len: %d)",
200*4882a593Smuzhiyun fintek->cir_addr, fintek->cir_irq, fintek->cir_port_len);
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun /* enable CIR interrupts */
fintek_enable_cir_irq(struct fintek_dev * fintek)204*4882a593Smuzhiyun static void fintek_enable_cir_irq(struct fintek_dev *fintek)
205*4882a593Smuzhiyun {
206*4882a593Smuzhiyun fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS);
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun
fintek_cir_regs_init(struct fintek_dev * fintek)209*4882a593Smuzhiyun static void fintek_cir_regs_init(struct fintek_dev *fintek)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun /* clear any and all stray interrupts */
212*4882a593Smuzhiyun fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun /* and finally, enable interrupts */
215*4882a593Smuzhiyun fintek_enable_cir_irq(fintek);
216*4882a593Smuzhiyun }
217*4882a593Smuzhiyun
fintek_enable_wake(struct fintek_dev * fintek)218*4882a593Smuzhiyun static void fintek_enable_wake(struct fintek_dev *fintek)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun fintek_config_mode_enable(fintek);
221*4882a593Smuzhiyun fintek_select_logical_dev(fintek, LOGICAL_DEV_ACPI);
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun /* Allow CIR PME's to wake system */
224*4882a593Smuzhiyun fintek_set_reg_bit(fintek, ACPI_WAKE_EN_CIR_BIT, LDEV_ACPI_WAKE_EN_REG);
225*4882a593Smuzhiyun /* Enable CIR PME's */
226*4882a593Smuzhiyun fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_EN_REG);
227*4882a593Smuzhiyun /* Clear CIR PME status register */
228*4882a593Smuzhiyun fintek_set_reg_bit(fintek, ACPI_PME_CIR_BIT, LDEV_ACPI_PME_CLR_REG);
229*4882a593Smuzhiyun /* Save state */
230*4882a593Smuzhiyun fintek_set_reg_bit(fintek, ACPI_STATE_CIR_BIT, LDEV_ACPI_STATE_REG);
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun fintek_config_mode_disable(fintek);
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
fintek_cmdsize(u8 cmd,u8 subcmd)235*4882a593Smuzhiyun static int fintek_cmdsize(u8 cmd, u8 subcmd)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun int datasize = 0;
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun switch (cmd) {
240*4882a593Smuzhiyun case BUF_COMMAND_NULL:
241*4882a593Smuzhiyun if (subcmd == BUF_HW_CMD_HEADER)
242*4882a593Smuzhiyun datasize = 1;
243*4882a593Smuzhiyun break;
244*4882a593Smuzhiyun case BUF_HW_CMD_HEADER:
245*4882a593Smuzhiyun if (subcmd == BUF_CMD_G_REVISION)
246*4882a593Smuzhiyun datasize = 2;
247*4882a593Smuzhiyun break;
248*4882a593Smuzhiyun case BUF_COMMAND_HEADER:
249*4882a593Smuzhiyun switch (subcmd) {
250*4882a593Smuzhiyun case BUF_CMD_S_CARRIER:
251*4882a593Smuzhiyun case BUF_CMD_S_TIMEOUT:
252*4882a593Smuzhiyun case BUF_RSP_PULSE_COUNT:
253*4882a593Smuzhiyun datasize = 2;
254*4882a593Smuzhiyun break;
255*4882a593Smuzhiyun case BUF_CMD_SIG_END:
256*4882a593Smuzhiyun case BUF_CMD_S_TXMASK:
257*4882a593Smuzhiyun case BUF_CMD_S_RXSENSOR:
258*4882a593Smuzhiyun datasize = 1;
259*4882a593Smuzhiyun break;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun return datasize;
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun
266*4882a593Smuzhiyun /* process ir data stored in driver buffer */
fintek_process_rx_ir_data(struct fintek_dev * fintek)267*4882a593Smuzhiyun static void fintek_process_rx_ir_data(struct fintek_dev *fintek)
268*4882a593Smuzhiyun {
269*4882a593Smuzhiyun struct ir_raw_event rawir = {};
270*4882a593Smuzhiyun u8 sample;
271*4882a593Smuzhiyun bool event = false;
272*4882a593Smuzhiyun int i;
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun for (i = 0; i < fintek->pkts; i++) {
275*4882a593Smuzhiyun sample = fintek->buf[i];
276*4882a593Smuzhiyun switch (fintek->parser_state) {
277*4882a593Smuzhiyun case CMD_HEADER:
278*4882a593Smuzhiyun fintek->cmd = sample;
279*4882a593Smuzhiyun if ((fintek->cmd == BUF_COMMAND_HEADER) ||
280*4882a593Smuzhiyun ((fintek->cmd & BUF_COMMAND_MASK) !=
281*4882a593Smuzhiyun BUF_PULSE_BIT)) {
282*4882a593Smuzhiyun fintek->parser_state = SUBCMD;
283*4882a593Smuzhiyun continue;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun fintek->rem = (fintek->cmd & BUF_LEN_MASK);
286*4882a593Smuzhiyun fit_dbg("%s: rem: 0x%02x", __func__, fintek->rem);
287*4882a593Smuzhiyun if (fintek->rem)
288*4882a593Smuzhiyun fintek->parser_state = PARSE_IRDATA;
289*4882a593Smuzhiyun else
290*4882a593Smuzhiyun ir_raw_event_reset(fintek->rdev);
291*4882a593Smuzhiyun break;
292*4882a593Smuzhiyun case SUBCMD:
293*4882a593Smuzhiyun fintek->rem = fintek_cmdsize(fintek->cmd, sample);
294*4882a593Smuzhiyun fintek->parser_state = CMD_DATA;
295*4882a593Smuzhiyun break;
296*4882a593Smuzhiyun case CMD_DATA:
297*4882a593Smuzhiyun fintek->rem--;
298*4882a593Smuzhiyun break;
299*4882a593Smuzhiyun case PARSE_IRDATA:
300*4882a593Smuzhiyun fintek->rem--;
301*4882a593Smuzhiyun rawir.pulse = ((sample & BUF_PULSE_BIT) != 0);
302*4882a593Smuzhiyun rawir.duration = (sample & BUF_SAMPLE_MASK)
303*4882a593Smuzhiyun * CIR_SAMPLE_PERIOD;
304*4882a593Smuzhiyun
305*4882a593Smuzhiyun fit_dbg("Storing %s with duration %d",
306*4882a593Smuzhiyun rawir.pulse ? "pulse" : "space",
307*4882a593Smuzhiyun rawir.duration);
308*4882a593Smuzhiyun if (ir_raw_event_store_with_filter(fintek->rdev,
309*4882a593Smuzhiyun &rawir))
310*4882a593Smuzhiyun event = true;
311*4882a593Smuzhiyun break;
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun if ((fintek->parser_state != CMD_HEADER) && !fintek->rem)
315*4882a593Smuzhiyun fintek->parser_state = CMD_HEADER;
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun
318*4882a593Smuzhiyun fintek->pkts = 0;
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun if (event) {
321*4882a593Smuzhiyun fit_dbg("Calling ir_raw_event_handle");
322*4882a593Smuzhiyun ir_raw_event_handle(fintek->rdev);
323*4882a593Smuzhiyun }
324*4882a593Smuzhiyun }
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun /* copy data from hardware rx register into driver buffer */
fintek_get_rx_ir_data(struct fintek_dev * fintek,u8 rx_irqs)327*4882a593Smuzhiyun static void fintek_get_rx_ir_data(struct fintek_dev *fintek, u8 rx_irqs)
328*4882a593Smuzhiyun {
329*4882a593Smuzhiyun unsigned long flags;
330*4882a593Smuzhiyun u8 sample, status;
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun spin_lock_irqsave(&fintek->fintek_lock, flags);
333*4882a593Smuzhiyun
334*4882a593Smuzhiyun /*
335*4882a593Smuzhiyun * We must read data from CIR_RX_DATA until the hardware IR buffer
336*4882a593Smuzhiyun * is empty and clears the RX_TIMEOUT and/or RX_RECEIVE flags in
337*4882a593Smuzhiyun * the CIR_STATUS register
338*4882a593Smuzhiyun */
339*4882a593Smuzhiyun do {
340*4882a593Smuzhiyun sample = fintek_cir_reg_read(fintek, CIR_RX_DATA);
341*4882a593Smuzhiyun fit_dbg("%s: sample: 0x%02x", __func__, sample);
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun fintek->buf[fintek->pkts] = sample;
344*4882a593Smuzhiyun fintek->pkts++;
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun status = fintek_cir_reg_read(fintek, CIR_STATUS);
347*4882a593Smuzhiyun if (!(status & CIR_STATUS_IRQ_EN))
348*4882a593Smuzhiyun break;
349*4882a593Smuzhiyun } while (status & rx_irqs);
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun fintek_process_rx_ir_data(fintek);
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun spin_unlock_irqrestore(&fintek->fintek_lock, flags);
354*4882a593Smuzhiyun }
355*4882a593Smuzhiyun
fintek_cir_log_irqs(u8 status)356*4882a593Smuzhiyun static void fintek_cir_log_irqs(u8 status)
357*4882a593Smuzhiyun {
358*4882a593Smuzhiyun fit_pr(KERN_INFO, "IRQ 0x%02x:%s%s%s%s%s", status,
359*4882a593Smuzhiyun status & CIR_STATUS_IRQ_EN ? " IRQEN" : "",
360*4882a593Smuzhiyun status & CIR_STATUS_TX_FINISH ? " TXF" : "",
361*4882a593Smuzhiyun status & CIR_STATUS_TX_UNDERRUN ? " TXU" : "",
362*4882a593Smuzhiyun status & CIR_STATUS_RX_TIMEOUT ? " RXTO" : "",
363*4882a593Smuzhiyun status & CIR_STATUS_RX_RECEIVE ? " RXOK" : "");
364*4882a593Smuzhiyun }
365*4882a593Smuzhiyun
366*4882a593Smuzhiyun /* interrupt service routine for incoming and outgoing CIR data */
fintek_cir_isr(int irq,void * data)367*4882a593Smuzhiyun static irqreturn_t fintek_cir_isr(int irq, void *data)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun struct fintek_dev *fintek = data;
370*4882a593Smuzhiyun u8 status, rx_irqs;
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun fit_dbg_verbose("%s firing", __func__);
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun fintek_config_mode_enable(fintek);
375*4882a593Smuzhiyun fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
376*4882a593Smuzhiyun fintek_config_mode_disable(fintek);
377*4882a593Smuzhiyun
378*4882a593Smuzhiyun /*
379*4882a593Smuzhiyun * Get IR Status register contents. Write 1 to ack/clear
380*4882a593Smuzhiyun *
381*4882a593Smuzhiyun * bit: reg name - description
382*4882a593Smuzhiyun * 3: TX_FINISH - TX is finished
383*4882a593Smuzhiyun * 2: TX_UNDERRUN - TX underrun
384*4882a593Smuzhiyun * 1: RX_TIMEOUT - RX data timeout
385*4882a593Smuzhiyun * 0: RX_RECEIVE - RX data received
386*4882a593Smuzhiyun */
387*4882a593Smuzhiyun status = fintek_cir_reg_read(fintek, CIR_STATUS);
388*4882a593Smuzhiyun if (!(status & CIR_STATUS_IRQ_MASK) || status == 0xff) {
389*4882a593Smuzhiyun fit_dbg_verbose("%s exiting, IRSTS 0x%02x", __func__, status);
390*4882a593Smuzhiyun fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
391*4882a593Smuzhiyun return IRQ_RETVAL(IRQ_NONE);
392*4882a593Smuzhiyun }
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun if (debug)
395*4882a593Smuzhiyun fintek_cir_log_irqs(status);
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun rx_irqs = status & (CIR_STATUS_RX_RECEIVE | CIR_STATUS_RX_TIMEOUT);
398*4882a593Smuzhiyun if (rx_irqs)
399*4882a593Smuzhiyun fintek_get_rx_ir_data(fintek, rx_irqs);
400*4882a593Smuzhiyun
401*4882a593Smuzhiyun /* ack/clear all irq flags we've got */
402*4882a593Smuzhiyun fintek_cir_reg_write(fintek, status, CIR_STATUS);
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun fit_dbg_verbose("%s done", __func__);
405*4882a593Smuzhiyun return IRQ_RETVAL(IRQ_HANDLED);
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun
fintek_enable_cir(struct fintek_dev * fintek)408*4882a593Smuzhiyun static void fintek_enable_cir(struct fintek_dev *fintek)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun /* set IRQ enabled */
411*4882a593Smuzhiyun fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_EN, CIR_STATUS);
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun fintek_config_mode_enable(fintek);
414*4882a593Smuzhiyun
415*4882a593Smuzhiyun /* enable the CIR logical device */
416*4882a593Smuzhiyun fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
417*4882a593Smuzhiyun fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun fintek_config_mode_disable(fintek);
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun /* clear all pending interrupts */
422*4882a593Smuzhiyun fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun /* enable interrupts */
425*4882a593Smuzhiyun fintek_enable_cir_irq(fintek);
426*4882a593Smuzhiyun }
427*4882a593Smuzhiyun
fintek_disable_cir(struct fintek_dev * fintek)428*4882a593Smuzhiyun static void fintek_disable_cir(struct fintek_dev *fintek)
429*4882a593Smuzhiyun {
430*4882a593Smuzhiyun fintek_config_mode_enable(fintek);
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun /* disable the CIR logical device */
433*4882a593Smuzhiyun fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
434*4882a593Smuzhiyun fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
435*4882a593Smuzhiyun
436*4882a593Smuzhiyun fintek_config_mode_disable(fintek);
437*4882a593Smuzhiyun }
438*4882a593Smuzhiyun
fintek_open(struct rc_dev * dev)439*4882a593Smuzhiyun static int fintek_open(struct rc_dev *dev)
440*4882a593Smuzhiyun {
441*4882a593Smuzhiyun struct fintek_dev *fintek = dev->priv;
442*4882a593Smuzhiyun unsigned long flags;
443*4882a593Smuzhiyun
444*4882a593Smuzhiyun spin_lock_irqsave(&fintek->fintek_lock, flags);
445*4882a593Smuzhiyun fintek_enable_cir(fintek);
446*4882a593Smuzhiyun spin_unlock_irqrestore(&fintek->fintek_lock, flags);
447*4882a593Smuzhiyun
448*4882a593Smuzhiyun return 0;
449*4882a593Smuzhiyun }
450*4882a593Smuzhiyun
fintek_close(struct rc_dev * dev)451*4882a593Smuzhiyun static void fintek_close(struct rc_dev *dev)
452*4882a593Smuzhiyun {
453*4882a593Smuzhiyun struct fintek_dev *fintek = dev->priv;
454*4882a593Smuzhiyun unsigned long flags;
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun spin_lock_irqsave(&fintek->fintek_lock, flags);
457*4882a593Smuzhiyun fintek_disable_cir(fintek);
458*4882a593Smuzhiyun spin_unlock_irqrestore(&fintek->fintek_lock, flags);
459*4882a593Smuzhiyun }
460*4882a593Smuzhiyun
461*4882a593Smuzhiyun /* Allocate memory, probe hardware, and initialize everything */
fintek_probe(struct pnp_dev * pdev,const struct pnp_device_id * dev_id)462*4882a593Smuzhiyun static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
463*4882a593Smuzhiyun {
464*4882a593Smuzhiyun struct fintek_dev *fintek;
465*4882a593Smuzhiyun struct rc_dev *rdev;
466*4882a593Smuzhiyun int ret = -ENOMEM;
467*4882a593Smuzhiyun
468*4882a593Smuzhiyun fintek = kzalloc(sizeof(struct fintek_dev), GFP_KERNEL);
469*4882a593Smuzhiyun if (!fintek)
470*4882a593Smuzhiyun return ret;
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun /* input device for IR remote (and tx) */
473*4882a593Smuzhiyun rdev = rc_allocate_device(RC_DRIVER_IR_RAW);
474*4882a593Smuzhiyun if (!rdev)
475*4882a593Smuzhiyun goto exit_free_dev_rdev;
476*4882a593Smuzhiyun
477*4882a593Smuzhiyun ret = -ENODEV;
478*4882a593Smuzhiyun /* validate pnp resources */
479*4882a593Smuzhiyun if (!pnp_port_valid(pdev, 0)) {
480*4882a593Smuzhiyun dev_err(&pdev->dev, "IR PNP Port not valid!\n");
481*4882a593Smuzhiyun goto exit_free_dev_rdev;
482*4882a593Smuzhiyun }
483*4882a593Smuzhiyun
484*4882a593Smuzhiyun if (!pnp_irq_valid(pdev, 0)) {
485*4882a593Smuzhiyun dev_err(&pdev->dev, "IR PNP IRQ not valid!\n");
486*4882a593Smuzhiyun goto exit_free_dev_rdev;
487*4882a593Smuzhiyun }
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun fintek->cir_addr = pnp_port_start(pdev, 0);
490*4882a593Smuzhiyun fintek->cir_irq = pnp_irq(pdev, 0);
491*4882a593Smuzhiyun fintek->cir_port_len = pnp_port_len(pdev, 0);
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun fintek->cr_ip = CR_INDEX_PORT;
494*4882a593Smuzhiyun fintek->cr_dp = CR_DATA_PORT;
495*4882a593Smuzhiyun
496*4882a593Smuzhiyun spin_lock_init(&fintek->fintek_lock);
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun pnp_set_drvdata(pdev, fintek);
499*4882a593Smuzhiyun fintek->pdev = pdev;
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun ret = fintek_hw_detect(fintek);
502*4882a593Smuzhiyun if (ret)
503*4882a593Smuzhiyun goto exit_free_dev_rdev;
504*4882a593Smuzhiyun
505*4882a593Smuzhiyun /* Initialize CIR & CIR Wake Logical Devices */
506*4882a593Smuzhiyun fintek_config_mode_enable(fintek);
507*4882a593Smuzhiyun fintek_cir_ldev_init(fintek);
508*4882a593Smuzhiyun fintek_config_mode_disable(fintek);
509*4882a593Smuzhiyun
510*4882a593Smuzhiyun /* Initialize CIR & CIR Wake Config Registers */
511*4882a593Smuzhiyun fintek_cir_regs_init(fintek);
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun /* Set up the rc device */
514*4882a593Smuzhiyun rdev->priv = fintek;
515*4882a593Smuzhiyun rdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
516*4882a593Smuzhiyun rdev->open = fintek_open;
517*4882a593Smuzhiyun rdev->close = fintek_close;
518*4882a593Smuzhiyun rdev->device_name = FINTEK_DESCRIPTION;
519*4882a593Smuzhiyun rdev->input_phys = "fintek/cir0";
520*4882a593Smuzhiyun rdev->input_id.bustype = BUS_HOST;
521*4882a593Smuzhiyun rdev->input_id.vendor = VENDOR_ID_FINTEK;
522*4882a593Smuzhiyun rdev->input_id.product = fintek->chip_major;
523*4882a593Smuzhiyun rdev->input_id.version = fintek->chip_minor;
524*4882a593Smuzhiyun rdev->dev.parent = &pdev->dev;
525*4882a593Smuzhiyun rdev->driver_name = FINTEK_DRIVER_NAME;
526*4882a593Smuzhiyun rdev->map_name = RC_MAP_RC6_MCE;
527*4882a593Smuzhiyun rdev->timeout = 1000;
528*4882a593Smuzhiyun /* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
529*4882a593Smuzhiyun rdev->rx_resolution = CIR_SAMPLE_PERIOD;
530*4882a593Smuzhiyun
531*4882a593Smuzhiyun fintek->rdev = rdev;
532*4882a593Smuzhiyun
533*4882a593Smuzhiyun ret = -EBUSY;
534*4882a593Smuzhiyun /* now claim resources */
535*4882a593Smuzhiyun if (!request_region(fintek->cir_addr,
536*4882a593Smuzhiyun fintek->cir_port_len, FINTEK_DRIVER_NAME))
537*4882a593Smuzhiyun goto exit_free_dev_rdev;
538*4882a593Smuzhiyun
539*4882a593Smuzhiyun if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED,
540*4882a593Smuzhiyun FINTEK_DRIVER_NAME, (void *)fintek))
541*4882a593Smuzhiyun goto exit_free_cir_addr;
542*4882a593Smuzhiyun
543*4882a593Smuzhiyun ret = rc_register_device(rdev);
544*4882a593Smuzhiyun if (ret)
545*4882a593Smuzhiyun goto exit_free_irq;
546*4882a593Smuzhiyun
547*4882a593Smuzhiyun device_init_wakeup(&pdev->dev, true);
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun fit_pr(KERN_NOTICE, "driver has been successfully loaded\n");
550*4882a593Smuzhiyun if (debug)
551*4882a593Smuzhiyun cir_dump_regs(fintek);
552*4882a593Smuzhiyun
553*4882a593Smuzhiyun return 0;
554*4882a593Smuzhiyun
555*4882a593Smuzhiyun exit_free_irq:
556*4882a593Smuzhiyun free_irq(fintek->cir_irq, fintek);
557*4882a593Smuzhiyun exit_free_cir_addr:
558*4882a593Smuzhiyun release_region(fintek->cir_addr, fintek->cir_port_len);
559*4882a593Smuzhiyun exit_free_dev_rdev:
560*4882a593Smuzhiyun rc_free_device(rdev);
561*4882a593Smuzhiyun kfree(fintek);
562*4882a593Smuzhiyun
563*4882a593Smuzhiyun return ret;
564*4882a593Smuzhiyun }
565*4882a593Smuzhiyun
fintek_remove(struct pnp_dev * pdev)566*4882a593Smuzhiyun static void fintek_remove(struct pnp_dev *pdev)
567*4882a593Smuzhiyun {
568*4882a593Smuzhiyun struct fintek_dev *fintek = pnp_get_drvdata(pdev);
569*4882a593Smuzhiyun unsigned long flags;
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun spin_lock_irqsave(&fintek->fintek_lock, flags);
572*4882a593Smuzhiyun /* disable CIR */
573*4882a593Smuzhiyun fintek_disable_cir(fintek);
574*4882a593Smuzhiyun fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
575*4882a593Smuzhiyun /* enable CIR Wake (for IR power-on) */
576*4882a593Smuzhiyun fintek_enable_wake(fintek);
577*4882a593Smuzhiyun spin_unlock_irqrestore(&fintek->fintek_lock, flags);
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun /* free resources */
580*4882a593Smuzhiyun free_irq(fintek->cir_irq, fintek);
581*4882a593Smuzhiyun release_region(fintek->cir_addr, fintek->cir_port_len);
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun rc_unregister_device(fintek->rdev);
584*4882a593Smuzhiyun
585*4882a593Smuzhiyun kfree(fintek);
586*4882a593Smuzhiyun }
587*4882a593Smuzhiyun
fintek_suspend(struct pnp_dev * pdev,pm_message_t state)588*4882a593Smuzhiyun static int fintek_suspend(struct pnp_dev *pdev, pm_message_t state)
589*4882a593Smuzhiyun {
590*4882a593Smuzhiyun struct fintek_dev *fintek = pnp_get_drvdata(pdev);
591*4882a593Smuzhiyun unsigned long flags;
592*4882a593Smuzhiyun
593*4882a593Smuzhiyun fit_dbg("%s called", __func__);
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun spin_lock_irqsave(&fintek->fintek_lock, flags);
596*4882a593Smuzhiyun
597*4882a593Smuzhiyun /* disable all CIR interrupts */
598*4882a593Smuzhiyun fintek_cir_reg_write(fintek, CIR_STATUS_IRQ_MASK, CIR_STATUS);
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun spin_unlock_irqrestore(&fintek->fintek_lock, flags);
601*4882a593Smuzhiyun
602*4882a593Smuzhiyun fintek_config_mode_enable(fintek);
603*4882a593Smuzhiyun
604*4882a593Smuzhiyun /* disable cir logical dev */
605*4882a593Smuzhiyun fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
606*4882a593Smuzhiyun fintek_cr_write(fintek, LOGICAL_DEV_DISABLE, CIR_CR_DEV_EN);
607*4882a593Smuzhiyun
608*4882a593Smuzhiyun fintek_config_mode_disable(fintek);
609*4882a593Smuzhiyun
610*4882a593Smuzhiyun /* make sure wake is enabled */
611*4882a593Smuzhiyun fintek_enable_wake(fintek);
612*4882a593Smuzhiyun
613*4882a593Smuzhiyun return 0;
614*4882a593Smuzhiyun }
615*4882a593Smuzhiyun
fintek_resume(struct pnp_dev * pdev)616*4882a593Smuzhiyun static int fintek_resume(struct pnp_dev *pdev)
617*4882a593Smuzhiyun {
618*4882a593Smuzhiyun struct fintek_dev *fintek = pnp_get_drvdata(pdev);
619*4882a593Smuzhiyun
620*4882a593Smuzhiyun fit_dbg("%s called", __func__);
621*4882a593Smuzhiyun
622*4882a593Smuzhiyun /* open interrupt */
623*4882a593Smuzhiyun fintek_enable_cir_irq(fintek);
624*4882a593Smuzhiyun
625*4882a593Smuzhiyun /* Enable CIR logical device */
626*4882a593Smuzhiyun fintek_config_mode_enable(fintek);
627*4882a593Smuzhiyun fintek_select_logical_dev(fintek, fintek->logical_dev_cir);
628*4882a593Smuzhiyun fintek_cr_write(fintek, LOGICAL_DEV_ENABLE, CIR_CR_DEV_EN);
629*4882a593Smuzhiyun
630*4882a593Smuzhiyun fintek_config_mode_disable(fintek);
631*4882a593Smuzhiyun
632*4882a593Smuzhiyun fintek_cir_regs_init(fintek);
633*4882a593Smuzhiyun
634*4882a593Smuzhiyun return 0;
635*4882a593Smuzhiyun }
636*4882a593Smuzhiyun
fintek_shutdown(struct pnp_dev * pdev)637*4882a593Smuzhiyun static void fintek_shutdown(struct pnp_dev *pdev)
638*4882a593Smuzhiyun {
639*4882a593Smuzhiyun struct fintek_dev *fintek = pnp_get_drvdata(pdev);
640*4882a593Smuzhiyun fintek_enable_wake(fintek);
641*4882a593Smuzhiyun }
642*4882a593Smuzhiyun
643*4882a593Smuzhiyun static const struct pnp_device_id fintek_ids[] = {
644*4882a593Smuzhiyun { "FIT0002", 0 }, /* CIR */
645*4882a593Smuzhiyun { "", 0 },
646*4882a593Smuzhiyun };
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun static struct pnp_driver fintek_driver = {
649*4882a593Smuzhiyun .name = FINTEK_DRIVER_NAME,
650*4882a593Smuzhiyun .id_table = fintek_ids,
651*4882a593Smuzhiyun .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
652*4882a593Smuzhiyun .probe = fintek_probe,
653*4882a593Smuzhiyun .remove = fintek_remove,
654*4882a593Smuzhiyun .suspend = fintek_suspend,
655*4882a593Smuzhiyun .resume = fintek_resume,
656*4882a593Smuzhiyun .shutdown = fintek_shutdown,
657*4882a593Smuzhiyun };
658*4882a593Smuzhiyun
659*4882a593Smuzhiyun module_param(debug, int, S_IRUGO | S_IWUSR);
660*4882a593Smuzhiyun MODULE_PARM_DESC(debug, "Enable debugging output");
661*4882a593Smuzhiyun
662*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pnp, fintek_ids);
663*4882a593Smuzhiyun MODULE_DESCRIPTION(FINTEK_DESCRIPTION " driver");
664*4882a593Smuzhiyun
665*4882a593Smuzhiyun MODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
666*4882a593Smuzhiyun MODULE_LICENSE("GPL");
667*4882a593Smuzhiyun
668*4882a593Smuzhiyun module_pnp_driver(fintek_driver);
669