18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * IR SIR driver, (C) 2000 Milan Pikula <www@fornax.sk> 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * sir_ir - Device driver for use with SIR (serial infra red) 68c2ecf20Sopenharmony_ci * mode of IrDA on many notebooks. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/serial_reg.h> 158c2ecf20Sopenharmony_ci#include <linux/ktime.h> 168c2ecf20Sopenharmony_ci#include <linux/delay.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <media/rc-core.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci/* SECTION: Definitions */ 228c2ecf20Sopenharmony_ci#define PULSE '[' 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/ 258c2ecf20Sopenharmony_ci#define TIME_CONST (9000000ul / 115200ul) 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci/* timeout for sequences in jiffies (=5/100s), must be longer than TIME_CONST */ 288c2ecf20Sopenharmony_ci#define SIR_TIMEOUT (HZ * 5 / 100) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* onboard sir ports are typically com3 */ 318c2ecf20Sopenharmony_cistatic int io = 0x3e8; 328c2ecf20Sopenharmony_cistatic int irq = 4; 338c2ecf20Sopenharmony_cistatic int threshold = 3; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(timer_lock); 368c2ecf20Sopenharmony_cistatic struct timer_list timerlist; 378c2ecf20Sopenharmony_ci/* time of last signal change detected */ 388c2ecf20Sopenharmony_cistatic ktime_t last; 398c2ecf20Sopenharmony_ci/* time of last UART data ready interrupt */ 408c2ecf20Sopenharmony_cistatic ktime_t last_intr_time; 418c2ecf20Sopenharmony_cistatic int last_value; 428c2ecf20Sopenharmony_cistatic struct rc_dev *rcdev; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic struct platform_device *sir_ir_dev; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(hardware_lock); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/* SECTION: Prototypes */ 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Communication with user-space */ 518c2ecf20Sopenharmony_cistatic void add_read_queue(int flag, unsigned long val); 528c2ecf20Sopenharmony_ci/* Hardware */ 538c2ecf20Sopenharmony_cistatic irqreturn_t sir_interrupt(int irq, void *dev_id); 548c2ecf20Sopenharmony_cistatic void send_space(unsigned long len); 558c2ecf20Sopenharmony_cistatic void send_pulse(unsigned long len); 568c2ecf20Sopenharmony_cistatic int init_hardware(void); 578c2ecf20Sopenharmony_cistatic void drop_hardware(void); 588c2ecf20Sopenharmony_ci/* Initialisation */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic inline unsigned int sinp(int offset) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci return inb(io + offset); 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic inline void soutp(int offset, int value) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci outb(value, io + offset); 688c2ecf20Sopenharmony_ci} 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci/* SECTION: Communication with user-space */ 718c2ecf20Sopenharmony_cistatic int sir_tx_ir(struct rc_dev *dev, unsigned int *tx_buf, 728c2ecf20Sopenharmony_ci unsigned int count) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci unsigned long flags; 758c2ecf20Sopenharmony_ci int i; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci local_irq_save(flags); 788c2ecf20Sopenharmony_ci for (i = 0; i < count;) { 798c2ecf20Sopenharmony_ci if (tx_buf[i]) 808c2ecf20Sopenharmony_ci send_pulse(tx_buf[i]); 818c2ecf20Sopenharmony_ci i++; 828c2ecf20Sopenharmony_ci if (i >= count) 838c2ecf20Sopenharmony_ci break; 848c2ecf20Sopenharmony_ci if (tx_buf[i]) 858c2ecf20Sopenharmony_ci send_space(tx_buf[i]); 868c2ecf20Sopenharmony_ci i++; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci local_irq_restore(flags); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci return count; 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic void add_read_queue(int flag, unsigned long val) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct ir_raw_event ev = {}; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci pr_debug("add flag %d with val %lu\n", flag, val); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * statistically, pulses are ~TIME_CONST/2 too long. we could 1018c2ecf20Sopenharmony_ci * maybe make this more exact, but this is good enough 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci if (flag) { 1048c2ecf20Sopenharmony_ci /* pulse */ 1058c2ecf20Sopenharmony_ci if (val > TIME_CONST / 2) 1068c2ecf20Sopenharmony_ci val -= TIME_CONST / 2; 1078c2ecf20Sopenharmony_ci else /* should not ever happen */ 1088c2ecf20Sopenharmony_ci val = 1; 1098c2ecf20Sopenharmony_ci ev.pulse = true; 1108c2ecf20Sopenharmony_ci } else { 1118c2ecf20Sopenharmony_ci val += TIME_CONST / 2; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci ev.duration = val; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci ir_raw_event_store_with_filter(rcdev, &ev); 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci/* SECTION: Hardware */ 1198c2ecf20Sopenharmony_cistatic void sir_timeout(struct timer_list *unused) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci /* 1228c2ecf20Sopenharmony_ci * if last received signal was a pulse, but receiving stopped 1238c2ecf20Sopenharmony_ci * within the 9 bit frame, we need to finish this pulse and 1248c2ecf20Sopenharmony_ci * simulate a signal change to from pulse to space. Otherwise 1258c2ecf20Sopenharmony_ci * upper layers will receive two sequences next time. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci unsigned long flags; 1298c2ecf20Sopenharmony_ci unsigned long pulse_end; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* avoid interference with interrupt */ 1328c2ecf20Sopenharmony_ci spin_lock_irqsave(&timer_lock, flags); 1338c2ecf20Sopenharmony_ci if (last_value) { 1348c2ecf20Sopenharmony_ci /* clear unread bits in UART and restart */ 1358c2ecf20Sopenharmony_ci outb(UART_FCR_CLEAR_RCVR, io + UART_FCR); 1368c2ecf20Sopenharmony_ci /* determine 'virtual' pulse end: */ 1378c2ecf20Sopenharmony_ci pulse_end = min_t(unsigned long, 1388c2ecf20Sopenharmony_ci ktime_us_delta(last, last_intr_time), 1398c2ecf20Sopenharmony_ci IR_MAX_DURATION); 1408c2ecf20Sopenharmony_ci dev_dbg(&sir_ir_dev->dev, "timeout add %d for %lu usec\n", 1418c2ecf20Sopenharmony_ci last_value, pulse_end); 1428c2ecf20Sopenharmony_ci add_read_queue(last_value, pulse_end); 1438c2ecf20Sopenharmony_ci last_value = 0; 1448c2ecf20Sopenharmony_ci last = last_intr_time; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&timer_lock, flags); 1478c2ecf20Sopenharmony_ci ir_raw_event_handle(rcdev); 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic irqreturn_t sir_interrupt(int irq, void *dev_id) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci unsigned char data; 1538c2ecf20Sopenharmony_ci ktime_t curr_time; 1548c2ecf20Sopenharmony_ci unsigned long delt; 1558c2ecf20Sopenharmony_ci unsigned long deltintr; 1568c2ecf20Sopenharmony_ci unsigned long flags; 1578c2ecf20Sopenharmony_ci int counter = 0; 1588c2ecf20Sopenharmony_ci int iir, lsr; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) { 1618c2ecf20Sopenharmony_ci if (++counter > 256) { 1628c2ecf20Sopenharmony_ci dev_err(&sir_ir_dev->dev, "Trapped in interrupt"); 1638c2ecf20Sopenharmony_ci break; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci switch (iir & UART_IIR_ID) { /* FIXME toto treba preriedit */ 1678c2ecf20Sopenharmony_ci case UART_IIR_MSI: 1688c2ecf20Sopenharmony_ci (void)inb(io + UART_MSR); 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci case UART_IIR_RLSI: 1718c2ecf20Sopenharmony_ci case UART_IIR_THRI: 1728c2ecf20Sopenharmony_ci (void)inb(io + UART_LSR); 1738c2ecf20Sopenharmony_ci break; 1748c2ecf20Sopenharmony_ci case UART_IIR_RDI: 1758c2ecf20Sopenharmony_ci /* avoid interference with timer */ 1768c2ecf20Sopenharmony_ci spin_lock_irqsave(&timer_lock, flags); 1778c2ecf20Sopenharmony_ci do { 1788c2ecf20Sopenharmony_ci del_timer(&timerlist); 1798c2ecf20Sopenharmony_ci data = inb(io + UART_RX); 1808c2ecf20Sopenharmony_ci curr_time = ktime_get(); 1818c2ecf20Sopenharmony_ci delt = min_t(unsigned long, 1828c2ecf20Sopenharmony_ci ktime_us_delta(last, curr_time), 1838c2ecf20Sopenharmony_ci IR_MAX_DURATION); 1848c2ecf20Sopenharmony_ci deltintr = min_t(unsigned long, 1858c2ecf20Sopenharmony_ci ktime_us_delta(last_intr_time, 1868c2ecf20Sopenharmony_ci curr_time), 1878c2ecf20Sopenharmony_ci IR_MAX_DURATION); 1888c2ecf20Sopenharmony_ci dev_dbg(&sir_ir_dev->dev, "t %lu, d %d\n", 1898c2ecf20Sopenharmony_ci deltintr, (int)data); 1908c2ecf20Sopenharmony_ci /* 1918c2ecf20Sopenharmony_ci * if nothing came in last X cycles, 1928c2ecf20Sopenharmony_ci * it was gap 1938c2ecf20Sopenharmony_ci */ 1948c2ecf20Sopenharmony_ci if (deltintr > TIME_CONST * threshold) { 1958c2ecf20Sopenharmony_ci if (last_value) { 1968c2ecf20Sopenharmony_ci dev_dbg(&sir_ir_dev->dev, "GAP\n"); 1978c2ecf20Sopenharmony_ci /* simulate signal change */ 1988c2ecf20Sopenharmony_ci add_read_queue(last_value, 1998c2ecf20Sopenharmony_ci delt - 2008c2ecf20Sopenharmony_ci deltintr); 2018c2ecf20Sopenharmony_ci last_value = 0; 2028c2ecf20Sopenharmony_ci last = last_intr_time; 2038c2ecf20Sopenharmony_ci delt = deltintr; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci data = 1; 2078c2ecf20Sopenharmony_ci if (data ^ last_value) { 2088c2ecf20Sopenharmony_ci /* 2098c2ecf20Sopenharmony_ci * deltintr > 2*TIME_CONST, remember? 2108c2ecf20Sopenharmony_ci * the other case is timeout 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_ci add_read_queue(last_value, 2138c2ecf20Sopenharmony_ci delt - TIME_CONST); 2148c2ecf20Sopenharmony_ci last_value = data; 2158c2ecf20Sopenharmony_ci last = curr_time; 2168c2ecf20Sopenharmony_ci last = ktime_sub_us(last, 2178c2ecf20Sopenharmony_ci TIME_CONST); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci last_intr_time = curr_time; 2208c2ecf20Sopenharmony_ci if (data) { 2218c2ecf20Sopenharmony_ci /* 2228c2ecf20Sopenharmony_ci * start timer for end of 2238c2ecf20Sopenharmony_ci * sequence detection 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci timerlist.expires = jiffies + 2268c2ecf20Sopenharmony_ci SIR_TIMEOUT; 2278c2ecf20Sopenharmony_ci add_timer(&timerlist); 2288c2ecf20Sopenharmony_ci } 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci lsr = inb(io + UART_LSR); 2318c2ecf20Sopenharmony_ci } while (lsr & UART_LSR_DR); /* data ready */ 2328c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&timer_lock, flags); 2338c2ecf20Sopenharmony_ci break; 2348c2ecf20Sopenharmony_ci default: 2358c2ecf20Sopenharmony_ci break; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci ir_raw_event_handle(rcdev); 2398c2ecf20Sopenharmony_ci return IRQ_RETVAL(IRQ_HANDLED); 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic void send_space(unsigned long len) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci usleep_range(len, len + 25); 2458c2ecf20Sopenharmony_ci} 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_cistatic void send_pulse(unsigned long len) 2488c2ecf20Sopenharmony_ci{ 2498c2ecf20Sopenharmony_ci long bytes_out = len / TIME_CONST; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (bytes_out == 0) 2528c2ecf20Sopenharmony_ci bytes_out++; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci while (bytes_out--) { 2558c2ecf20Sopenharmony_ci outb(PULSE, io + UART_TX); 2568c2ecf20Sopenharmony_ci /* FIXME treba seriozne cakanie z char/serial.c */ 2578c2ecf20Sopenharmony_ci while (!(inb(io + UART_LSR) & UART_LSR_THRE)) 2588c2ecf20Sopenharmony_ci ; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic int init_hardware(void) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci u8 scratch, scratch2, scratch3; 2658c2ecf20Sopenharmony_ci unsigned long flags; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci spin_lock_irqsave(&hardware_lock, flags); 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* 2708c2ecf20Sopenharmony_ci * This is a simple port existence test, borrowed from the autoconfig 2718c2ecf20Sopenharmony_ci * function in drivers/tty/serial/8250/8250_port.c 2728c2ecf20Sopenharmony_ci */ 2738c2ecf20Sopenharmony_ci scratch = sinp(UART_IER); 2748c2ecf20Sopenharmony_ci soutp(UART_IER, 0); 2758c2ecf20Sopenharmony_ci#ifdef __i386__ 2768c2ecf20Sopenharmony_ci outb(0xff, 0x080); 2778c2ecf20Sopenharmony_ci#endif 2788c2ecf20Sopenharmony_ci scratch2 = sinp(UART_IER) & 0x0f; 2798c2ecf20Sopenharmony_ci soutp(UART_IER, 0x0f); 2808c2ecf20Sopenharmony_ci#ifdef __i386__ 2818c2ecf20Sopenharmony_ci outb(0x00, 0x080); 2828c2ecf20Sopenharmony_ci#endif 2838c2ecf20Sopenharmony_ci scratch3 = sinp(UART_IER) & 0x0f; 2848c2ecf20Sopenharmony_ci soutp(UART_IER, scratch); 2858c2ecf20Sopenharmony_ci if (scratch2 != 0 || scratch3 != 0x0f) { 2868c2ecf20Sopenharmony_ci /* we fail, there's nothing here */ 2878c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hardware_lock, flags); 2888c2ecf20Sopenharmony_ci pr_err("port existence test failed, cannot continue\n"); 2898c2ecf20Sopenharmony_ci return -ENODEV; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* reset UART */ 2938c2ecf20Sopenharmony_ci outb(0, io + UART_MCR); 2948c2ecf20Sopenharmony_ci outb(0, io + UART_IER); 2958c2ecf20Sopenharmony_ci /* init UART */ 2968c2ecf20Sopenharmony_ci /* set DLAB, speed = 115200 */ 2978c2ecf20Sopenharmony_ci outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR); 2988c2ecf20Sopenharmony_ci outb(1, io + UART_DLL); outb(0, io + UART_DLM); 2998c2ecf20Sopenharmony_ci /* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */ 3008c2ecf20Sopenharmony_ci outb(UART_LCR_WLEN7, io + UART_LCR); 3018c2ecf20Sopenharmony_ci /* FIFO operation */ 3028c2ecf20Sopenharmony_ci outb(UART_FCR_ENABLE_FIFO, io + UART_FCR); 3038c2ecf20Sopenharmony_ci /* interrupts */ 3048c2ecf20Sopenharmony_ci /* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */ 3058c2ecf20Sopenharmony_ci outb(UART_IER_RDI, io + UART_IER); 3068c2ecf20Sopenharmony_ci /* turn on UART */ 3078c2ecf20Sopenharmony_ci outb(UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2, io + UART_MCR); 3088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hardware_lock, flags); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic void drop_hardware(void) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci unsigned long flags; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci spin_lock_irqsave(&hardware_lock, flags); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci /* turn off interrupts */ 3208c2ecf20Sopenharmony_ci outb(0, io + UART_IER); 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&hardware_lock, flags); 3238c2ecf20Sopenharmony_ci} 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci/* SECTION: Initialisation */ 3268c2ecf20Sopenharmony_cistatic int sir_ir_probe(struct platform_device *dev) 3278c2ecf20Sopenharmony_ci{ 3288c2ecf20Sopenharmony_ci int retval; 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci rcdev = devm_rc_allocate_device(&sir_ir_dev->dev, RC_DRIVER_IR_RAW); 3318c2ecf20Sopenharmony_ci if (!rcdev) 3328c2ecf20Sopenharmony_ci return -ENOMEM; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci rcdev->device_name = "SIR IrDA port"; 3358c2ecf20Sopenharmony_ci rcdev->input_phys = KBUILD_MODNAME "/input0"; 3368c2ecf20Sopenharmony_ci rcdev->input_id.bustype = BUS_HOST; 3378c2ecf20Sopenharmony_ci rcdev->input_id.vendor = 0x0001; 3388c2ecf20Sopenharmony_ci rcdev->input_id.product = 0x0001; 3398c2ecf20Sopenharmony_ci rcdev->input_id.version = 0x0100; 3408c2ecf20Sopenharmony_ci rcdev->tx_ir = sir_tx_ir; 3418c2ecf20Sopenharmony_ci rcdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER; 3428c2ecf20Sopenharmony_ci rcdev->driver_name = KBUILD_MODNAME; 3438c2ecf20Sopenharmony_ci rcdev->map_name = RC_MAP_RC6_MCE; 3448c2ecf20Sopenharmony_ci rcdev->timeout = IR_DEFAULT_TIMEOUT; 3458c2ecf20Sopenharmony_ci rcdev->dev.parent = &sir_ir_dev->dev; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci timer_setup(&timerlist, sir_timeout, 0); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci /* get I/O port access and IRQ line */ 3508c2ecf20Sopenharmony_ci if (!devm_request_region(&sir_ir_dev->dev, io, 8, KBUILD_MODNAME)) { 3518c2ecf20Sopenharmony_ci pr_err("i/o port 0x%.4x already in use.\n", io); 3528c2ecf20Sopenharmony_ci return -EBUSY; 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci retval = devm_request_irq(&sir_ir_dev->dev, irq, sir_interrupt, 0, 3558c2ecf20Sopenharmony_ci KBUILD_MODNAME, NULL); 3568c2ecf20Sopenharmony_ci if (retval < 0) { 3578c2ecf20Sopenharmony_ci pr_err("IRQ %d already in use.\n", irq); 3588c2ecf20Sopenharmony_ci return retval; 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci retval = init_hardware(); 3628c2ecf20Sopenharmony_ci if (retval) { 3638c2ecf20Sopenharmony_ci del_timer_sync(&timerlist); 3648c2ecf20Sopenharmony_ci return retval; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci pr_info("I/O port 0x%.4x, IRQ %d.\n", io, irq); 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci retval = devm_rc_register_device(&sir_ir_dev->dev, rcdev); 3708c2ecf20Sopenharmony_ci if (retval < 0) 3718c2ecf20Sopenharmony_ci return retval; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci return 0; 3748c2ecf20Sopenharmony_ci} 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_cistatic int sir_ir_remove(struct platform_device *dev) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci drop_hardware(); 3798c2ecf20Sopenharmony_ci del_timer_sync(&timerlist); 3808c2ecf20Sopenharmony_ci return 0; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic struct platform_driver sir_ir_driver = { 3848c2ecf20Sopenharmony_ci .probe = sir_ir_probe, 3858c2ecf20Sopenharmony_ci .remove = sir_ir_remove, 3868c2ecf20Sopenharmony_ci .driver = { 3878c2ecf20Sopenharmony_ci .name = "sir_ir", 3888c2ecf20Sopenharmony_ci }, 3898c2ecf20Sopenharmony_ci}; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_cistatic int __init sir_ir_init(void) 3928c2ecf20Sopenharmony_ci{ 3938c2ecf20Sopenharmony_ci int retval; 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci retval = platform_driver_register(&sir_ir_driver); 3968c2ecf20Sopenharmony_ci if (retval) 3978c2ecf20Sopenharmony_ci return retval; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci sir_ir_dev = platform_device_alloc("sir_ir", 0); 4008c2ecf20Sopenharmony_ci if (!sir_ir_dev) { 4018c2ecf20Sopenharmony_ci retval = -ENOMEM; 4028c2ecf20Sopenharmony_ci goto pdev_alloc_fail; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci retval = platform_device_add(sir_ir_dev); 4068c2ecf20Sopenharmony_ci if (retval) 4078c2ecf20Sopenharmony_ci goto pdev_add_fail; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return 0; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cipdev_add_fail: 4128c2ecf20Sopenharmony_ci platform_device_put(sir_ir_dev); 4138c2ecf20Sopenharmony_cipdev_alloc_fail: 4148c2ecf20Sopenharmony_ci platform_driver_unregister(&sir_ir_driver); 4158c2ecf20Sopenharmony_ci return retval; 4168c2ecf20Sopenharmony_ci} 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_cistatic void __exit sir_ir_exit(void) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci platform_device_unregister(sir_ir_dev); 4218c2ecf20Sopenharmony_ci platform_driver_unregister(&sir_ir_driver); 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cimodule_init(sir_ir_init); 4258c2ecf20Sopenharmony_cimodule_exit(sir_ir_exit); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports"); 4288c2ecf20Sopenharmony_ciMODULE_AUTHOR("Milan Pikula"); 4298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cimodule_param_hw(io, int, ioport, 0444); 4328c2ecf20Sopenharmony_ciMODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)"); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cimodule_param_hw(irq, int, irq, 0444); 4358c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "Interrupt (4 or 3)"); 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cimodule_param(threshold, int, 0444); 4388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(threshold, "space detection threshold (3)"); 439