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