18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * serial_ir.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * serial_ir - Device driver that records pulse- and pause-lengths
68c2ecf20Sopenharmony_ci *	       (space-lengths) between DDCD event on a serial port.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de>
98c2ecf20Sopenharmony_ci * Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu>
108c2ecf20Sopenharmony_ci * Copyright (C) 1998 Ben Pfaff <blp@gnu.org>
118c2ecf20Sopenharmony_ci * Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
128c2ecf20Sopenharmony_ci * Copyright (C) 2007 Andrei Tanas <andrei@tanas.ca> (suspend/resume support)
138c2ecf20Sopenharmony_ci * Copyright (C) 2016 Sean Young <sean@mess.org> (port to rc-core)
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <linux/errno.h>
208c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
218c2ecf20Sopenharmony_ci#include <linux/kernel.h>
228c2ecf20Sopenharmony_ci#include <linux/serial_reg.h>
238c2ecf20Sopenharmony_ci#include <linux/types.h>
248c2ecf20Sopenharmony_ci#include <linux/delay.h>
258c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
268c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
278c2ecf20Sopenharmony_ci#include <media/rc-core.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistruct serial_ir_hw {
308c2ecf20Sopenharmony_ci	int signal_pin;
318c2ecf20Sopenharmony_ci	int signal_pin_change;
328c2ecf20Sopenharmony_ci	u8 on;
338c2ecf20Sopenharmony_ci	u8 off;
348c2ecf20Sopenharmony_ci	unsigned set_send_carrier:1;
358c2ecf20Sopenharmony_ci	unsigned set_duty_cycle:1;
368c2ecf20Sopenharmony_ci	void (*send_pulse)(unsigned int length, ktime_t edge);
378c2ecf20Sopenharmony_ci	void (*send_space)(void);
388c2ecf20Sopenharmony_ci	spinlock_t lock;
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define IR_HOMEBREW	0
428c2ecf20Sopenharmony_ci#define IR_IRDEO	1
438c2ecf20Sopenharmony_ci#define IR_IRDEO_REMOTE	2
448c2ecf20Sopenharmony_ci#define IR_ANIMAX	3
458c2ecf20Sopenharmony_ci#define IR_IGOR		4
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/* module parameters */
488c2ecf20Sopenharmony_cistatic int type;
498c2ecf20Sopenharmony_cistatic int io;
508c2ecf20Sopenharmony_cistatic int irq;
518c2ecf20Sopenharmony_cistatic ulong iommap;
528c2ecf20Sopenharmony_cistatic int ioshift;
538c2ecf20Sopenharmony_cistatic bool softcarrier = true;
548c2ecf20Sopenharmony_cistatic bool share_irq;
558c2ecf20Sopenharmony_cistatic int sense = -1;	/* -1 = auto, 0 = active high, 1 = active low */
568c2ecf20Sopenharmony_cistatic bool txsense;	/* 0 = active high, 1 = active low */
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* forward declarations */
598c2ecf20Sopenharmony_cistatic void send_pulse_irdeo(unsigned int length, ktime_t edge);
608c2ecf20Sopenharmony_cistatic void send_space_irdeo(void);
618c2ecf20Sopenharmony_ci#ifdef CONFIG_IR_SERIAL_TRANSMITTER
628c2ecf20Sopenharmony_cistatic void send_pulse_homebrew(unsigned int length, ktime_t edge);
638c2ecf20Sopenharmony_cistatic void send_space_homebrew(void);
648c2ecf20Sopenharmony_ci#endif
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_cistatic struct serial_ir_hw hardware[] = {
678c2ecf20Sopenharmony_ci	[IR_HOMEBREW] = {
688c2ecf20Sopenharmony_ci		.lock = __SPIN_LOCK_UNLOCKED(hardware[IR_HOMEBREW].lock),
698c2ecf20Sopenharmony_ci		.signal_pin	   = UART_MSR_DCD,
708c2ecf20Sopenharmony_ci		.signal_pin_change = UART_MSR_DDCD,
718c2ecf20Sopenharmony_ci		.on  = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
728c2ecf20Sopenharmony_ci		.off = (UART_MCR_RTS | UART_MCR_OUT2),
738c2ecf20Sopenharmony_ci#ifdef CONFIG_IR_SERIAL_TRANSMITTER
748c2ecf20Sopenharmony_ci		.send_pulse = send_pulse_homebrew,
758c2ecf20Sopenharmony_ci		.send_space = send_space_homebrew,
768c2ecf20Sopenharmony_ci		.set_send_carrier = true,
778c2ecf20Sopenharmony_ci		.set_duty_cycle = true,
788c2ecf20Sopenharmony_ci#endif
798c2ecf20Sopenharmony_ci	},
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	[IR_IRDEO] = {
828c2ecf20Sopenharmony_ci		.lock = __SPIN_LOCK_UNLOCKED(hardware[IR_IRDEO].lock),
838c2ecf20Sopenharmony_ci		.signal_pin	   = UART_MSR_DSR,
848c2ecf20Sopenharmony_ci		.signal_pin_change = UART_MSR_DDSR,
858c2ecf20Sopenharmony_ci		.on  = UART_MCR_OUT2,
868c2ecf20Sopenharmony_ci		.off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
878c2ecf20Sopenharmony_ci		.send_pulse = send_pulse_irdeo,
888c2ecf20Sopenharmony_ci		.send_space = send_space_irdeo,
898c2ecf20Sopenharmony_ci		.set_duty_cycle = true,
908c2ecf20Sopenharmony_ci	},
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	[IR_IRDEO_REMOTE] = {
938c2ecf20Sopenharmony_ci		.lock = __SPIN_LOCK_UNLOCKED(hardware[IR_IRDEO_REMOTE].lock),
948c2ecf20Sopenharmony_ci		.signal_pin	   = UART_MSR_DSR,
958c2ecf20Sopenharmony_ci		.signal_pin_change = UART_MSR_DDSR,
968c2ecf20Sopenharmony_ci		.on  = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
978c2ecf20Sopenharmony_ci		.off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
988c2ecf20Sopenharmony_ci		.send_pulse = send_pulse_irdeo,
998c2ecf20Sopenharmony_ci		.send_space = send_space_irdeo,
1008c2ecf20Sopenharmony_ci		.set_duty_cycle = true,
1018c2ecf20Sopenharmony_ci	},
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	[IR_ANIMAX] = {
1048c2ecf20Sopenharmony_ci		.lock = __SPIN_LOCK_UNLOCKED(hardware[IR_ANIMAX].lock),
1058c2ecf20Sopenharmony_ci		.signal_pin	   = UART_MSR_DCD,
1068c2ecf20Sopenharmony_ci		.signal_pin_change = UART_MSR_DDCD,
1078c2ecf20Sopenharmony_ci		.on  = 0,
1088c2ecf20Sopenharmony_ci		.off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
1098c2ecf20Sopenharmony_ci	},
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	[IR_IGOR] = {
1128c2ecf20Sopenharmony_ci		.lock = __SPIN_LOCK_UNLOCKED(hardware[IR_IGOR].lock),
1138c2ecf20Sopenharmony_ci		.signal_pin	   = UART_MSR_DSR,
1148c2ecf20Sopenharmony_ci		.signal_pin_change = UART_MSR_DDSR,
1158c2ecf20Sopenharmony_ci		.on  = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
1168c2ecf20Sopenharmony_ci		.off = (UART_MCR_RTS | UART_MCR_OUT2),
1178c2ecf20Sopenharmony_ci#ifdef CONFIG_IR_SERIAL_TRANSMITTER
1188c2ecf20Sopenharmony_ci		.send_pulse = send_pulse_homebrew,
1198c2ecf20Sopenharmony_ci		.send_space = send_space_homebrew,
1208c2ecf20Sopenharmony_ci		.set_send_carrier = true,
1218c2ecf20Sopenharmony_ci		.set_duty_cycle = true,
1228c2ecf20Sopenharmony_ci#endif
1238c2ecf20Sopenharmony_ci	},
1248c2ecf20Sopenharmony_ci};
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci#define RS_ISR_PASS_LIMIT 256
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistruct serial_ir {
1298c2ecf20Sopenharmony_ci	ktime_t lastkt;
1308c2ecf20Sopenharmony_ci	struct rc_dev *rcdev;
1318c2ecf20Sopenharmony_ci	struct platform_device *pdev;
1328c2ecf20Sopenharmony_ci	struct timer_list timeout_timer;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	unsigned int carrier;
1358c2ecf20Sopenharmony_ci	unsigned int duty_cycle;
1368c2ecf20Sopenharmony_ci};
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic struct serial_ir serial_ir;
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/* fetch serial input packet (1 byte) from register offset */
1418c2ecf20Sopenharmony_cistatic u8 sinp(int offset)
1428c2ecf20Sopenharmony_ci{
1438c2ecf20Sopenharmony_ci	if (iommap)
1448c2ecf20Sopenharmony_ci		/* the register is memory-mapped */
1458c2ecf20Sopenharmony_ci		offset <<= ioshift;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return inb(io + offset);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci/* write serial output packet (1 byte) of value to register offset */
1518c2ecf20Sopenharmony_cistatic void soutp(int offset, u8 value)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	if (iommap)
1548c2ecf20Sopenharmony_ci		/* the register is memory-mapped */
1558c2ecf20Sopenharmony_ci		offset <<= ioshift;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	outb(value, io + offset);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic void on(void)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	if (txsense)
1638c2ecf20Sopenharmony_ci		soutp(UART_MCR, hardware[type].off);
1648c2ecf20Sopenharmony_ci	else
1658c2ecf20Sopenharmony_ci		soutp(UART_MCR, hardware[type].on);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic void off(void)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	if (txsense)
1718c2ecf20Sopenharmony_ci		soutp(UART_MCR, hardware[type].on);
1728c2ecf20Sopenharmony_ci	else
1738c2ecf20Sopenharmony_ci		soutp(UART_MCR, hardware[type].off);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void send_pulse_irdeo(unsigned int length, ktime_t target)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	long rawbits;
1798c2ecf20Sopenharmony_ci	int i;
1808c2ecf20Sopenharmony_ci	unsigned char output;
1818c2ecf20Sopenharmony_ci	unsigned char chunk, shifted;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* how many bits have to be sent ? */
1848c2ecf20Sopenharmony_ci	rawbits = length * 1152 / 10000;
1858c2ecf20Sopenharmony_ci	if (serial_ir.duty_cycle > 50)
1868c2ecf20Sopenharmony_ci		chunk = 3;
1878c2ecf20Sopenharmony_ci	else
1888c2ecf20Sopenharmony_ci		chunk = 1;
1898c2ecf20Sopenharmony_ci	for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) {
1908c2ecf20Sopenharmony_ci		shifted = chunk << (i * 3);
1918c2ecf20Sopenharmony_ci		shifted >>= 1;
1928c2ecf20Sopenharmony_ci		output &= (~shifted);
1938c2ecf20Sopenharmony_ci		i++;
1948c2ecf20Sopenharmony_ci		if (i == 3) {
1958c2ecf20Sopenharmony_ci			soutp(UART_TX, output);
1968c2ecf20Sopenharmony_ci			while (!(sinp(UART_LSR) & UART_LSR_THRE))
1978c2ecf20Sopenharmony_ci				;
1988c2ecf20Sopenharmony_ci			output = 0x7f;
1998c2ecf20Sopenharmony_ci			i = 0;
2008c2ecf20Sopenharmony_ci		}
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	if (i != 0) {
2038c2ecf20Sopenharmony_ci		soutp(UART_TX, output);
2048c2ecf20Sopenharmony_ci		while (!(sinp(UART_LSR) & UART_LSR_TEMT))
2058c2ecf20Sopenharmony_ci			;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_cistatic void send_space_irdeo(void)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci#ifdef CONFIG_IR_SERIAL_TRANSMITTER
2148c2ecf20Sopenharmony_cistatic void send_pulse_homebrew_softcarrier(unsigned int length, ktime_t edge)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	ktime_t now, target = ktime_add_us(edge, length);
2178c2ecf20Sopenharmony_ci	/*
2188c2ecf20Sopenharmony_ci	 * delta should never exceed 4 seconds and on m68k
2198c2ecf20Sopenharmony_ci	 * ndelay(s64) does not compile; so use s32 rather than s64.
2208c2ecf20Sopenharmony_ci	 */
2218c2ecf20Sopenharmony_ci	s32 delta;
2228c2ecf20Sopenharmony_ci	unsigned int pulse, space;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	/* Ensure the dividend fits into 32 bit */
2258c2ecf20Sopenharmony_ci	pulse = DIV_ROUND_CLOSEST(serial_ir.duty_cycle * (NSEC_PER_SEC / 100),
2268c2ecf20Sopenharmony_ci				  serial_ir.carrier);
2278c2ecf20Sopenharmony_ci	space = DIV_ROUND_CLOSEST((100 - serial_ir.duty_cycle) *
2288c2ecf20Sopenharmony_ci				  (NSEC_PER_SEC / 100), serial_ir.carrier);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	for (;;) {
2318c2ecf20Sopenharmony_ci		now = ktime_get();
2328c2ecf20Sopenharmony_ci		if (ktime_compare(now, target) >= 0)
2338c2ecf20Sopenharmony_ci			break;
2348c2ecf20Sopenharmony_ci		on();
2358c2ecf20Sopenharmony_ci		edge = ktime_add_ns(edge, pulse);
2368c2ecf20Sopenharmony_ci		delta = ktime_to_ns(ktime_sub(edge, now));
2378c2ecf20Sopenharmony_ci		if (delta > 0)
2388c2ecf20Sopenharmony_ci			ndelay(delta);
2398c2ecf20Sopenharmony_ci		now = ktime_get();
2408c2ecf20Sopenharmony_ci		off();
2418c2ecf20Sopenharmony_ci		if (ktime_compare(now, target) >= 0)
2428c2ecf20Sopenharmony_ci			break;
2438c2ecf20Sopenharmony_ci		edge = ktime_add_ns(edge, space);
2448c2ecf20Sopenharmony_ci		delta = ktime_to_ns(ktime_sub(edge, now));
2458c2ecf20Sopenharmony_ci		if (delta > 0)
2468c2ecf20Sopenharmony_ci			ndelay(delta);
2478c2ecf20Sopenharmony_ci	}
2488c2ecf20Sopenharmony_ci}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_cistatic void send_pulse_homebrew(unsigned int length, ktime_t edge)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	if (softcarrier)
2538c2ecf20Sopenharmony_ci		send_pulse_homebrew_softcarrier(length, edge);
2548c2ecf20Sopenharmony_ci	else
2558c2ecf20Sopenharmony_ci		on();
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic void send_space_homebrew(void)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	off();
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci#endif
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic void frbwrite(unsigned int l, bool is_pulse)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	/* simple noise filter */
2678c2ecf20Sopenharmony_ci	static unsigned int ptr, pulse, space;
2688c2ecf20Sopenharmony_ci	struct ir_raw_event ev = {};
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (ptr > 0 && is_pulse) {
2718c2ecf20Sopenharmony_ci		pulse += l;
2728c2ecf20Sopenharmony_ci		if (pulse > 250) {
2738c2ecf20Sopenharmony_ci			ev.duration = space;
2748c2ecf20Sopenharmony_ci			ev.pulse = false;
2758c2ecf20Sopenharmony_ci			ir_raw_event_store_with_filter(serial_ir.rcdev, &ev);
2768c2ecf20Sopenharmony_ci			ev.duration = pulse;
2778c2ecf20Sopenharmony_ci			ev.pulse = true;
2788c2ecf20Sopenharmony_ci			ir_raw_event_store_with_filter(serial_ir.rcdev, &ev);
2798c2ecf20Sopenharmony_ci			ptr = 0;
2808c2ecf20Sopenharmony_ci			pulse = 0;
2818c2ecf20Sopenharmony_ci		}
2828c2ecf20Sopenharmony_ci		return;
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci	if (!is_pulse) {
2858c2ecf20Sopenharmony_ci		if (ptr == 0) {
2868c2ecf20Sopenharmony_ci			if (l > 20000) {
2878c2ecf20Sopenharmony_ci				space = l;
2888c2ecf20Sopenharmony_ci				ptr++;
2898c2ecf20Sopenharmony_ci				return;
2908c2ecf20Sopenharmony_ci			}
2918c2ecf20Sopenharmony_ci		} else {
2928c2ecf20Sopenharmony_ci			if (l > 20000) {
2938c2ecf20Sopenharmony_ci				space += pulse;
2948c2ecf20Sopenharmony_ci				if (space > IR_MAX_DURATION)
2958c2ecf20Sopenharmony_ci					space = IR_MAX_DURATION;
2968c2ecf20Sopenharmony_ci				space += l;
2978c2ecf20Sopenharmony_ci				if (space > IR_MAX_DURATION)
2988c2ecf20Sopenharmony_ci					space = IR_MAX_DURATION;
2998c2ecf20Sopenharmony_ci				pulse = 0;
3008c2ecf20Sopenharmony_ci				return;
3018c2ecf20Sopenharmony_ci			}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci			ev.duration = space;
3048c2ecf20Sopenharmony_ci			ev.pulse = false;
3058c2ecf20Sopenharmony_ci			ir_raw_event_store_with_filter(serial_ir.rcdev, &ev);
3068c2ecf20Sopenharmony_ci			ev.duration = pulse;
3078c2ecf20Sopenharmony_ci			ev.pulse = true;
3088c2ecf20Sopenharmony_ci			ir_raw_event_store_with_filter(serial_ir.rcdev, &ev);
3098c2ecf20Sopenharmony_ci			ptr = 0;
3108c2ecf20Sopenharmony_ci			pulse = 0;
3118c2ecf20Sopenharmony_ci		}
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	ev.duration = l;
3158c2ecf20Sopenharmony_ci	ev.pulse = is_pulse;
3168c2ecf20Sopenharmony_ci	ir_raw_event_store_with_filter(serial_ir.rcdev, &ev);
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic irqreturn_t serial_ir_irq_handler(int i, void *blah)
3208c2ecf20Sopenharmony_ci{
3218c2ecf20Sopenharmony_ci	ktime_t kt;
3228c2ecf20Sopenharmony_ci	int counter, dcd;
3238c2ecf20Sopenharmony_ci	u8 status;
3248c2ecf20Sopenharmony_ci	ktime_t delkt;
3258c2ecf20Sopenharmony_ci	unsigned int data;
3268c2ecf20Sopenharmony_ci	static int last_dcd = -1;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
3298c2ecf20Sopenharmony_ci		/* not our interrupt */
3308c2ecf20Sopenharmony_ci		return IRQ_NONE;
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	counter = 0;
3348c2ecf20Sopenharmony_ci	do {
3358c2ecf20Sopenharmony_ci		counter++;
3368c2ecf20Sopenharmony_ci		status = sinp(UART_MSR);
3378c2ecf20Sopenharmony_ci		if (counter > RS_ISR_PASS_LIMIT) {
3388c2ecf20Sopenharmony_ci			dev_err(&serial_ir.pdev->dev, "Trapped in interrupt");
3398c2ecf20Sopenharmony_ci			break;
3408c2ecf20Sopenharmony_ci		}
3418c2ecf20Sopenharmony_ci		if ((status & hardware[type].signal_pin_change) &&
3428c2ecf20Sopenharmony_ci		    sense != -1) {
3438c2ecf20Sopenharmony_ci			/* get current time */
3448c2ecf20Sopenharmony_ci			kt = ktime_get();
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci			/*
3478c2ecf20Sopenharmony_ci			 * The driver needs to know if your receiver is
3488c2ecf20Sopenharmony_ci			 * active high or active low, or the space/pulse
3498c2ecf20Sopenharmony_ci			 * sense could be inverted.
3508c2ecf20Sopenharmony_ci			 */
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci			/* calc time since last interrupt in nanoseconds */
3538c2ecf20Sopenharmony_ci			dcd = (status & hardware[type].signal_pin) ? 1 : 0;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci			if (dcd == last_dcd) {
3568c2ecf20Sopenharmony_ci				dev_dbg(&serial_ir.pdev->dev,
3578c2ecf20Sopenharmony_ci					"ignoring spike: %d %d %lldns %lldns\n",
3588c2ecf20Sopenharmony_ci					dcd, sense, ktime_to_ns(kt),
3598c2ecf20Sopenharmony_ci					ktime_to_ns(serial_ir.lastkt));
3608c2ecf20Sopenharmony_ci				continue;
3618c2ecf20Sopenharmony_ci			}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci			delkt = ktime_sub(kt, serial_ir.lastkt);
3648c2ecf20Sopenharmony_ci			if (ktime_compare(delkt, ktime_set(15, 0)) > 0) {
3658c2ecf20Sopenharmony_ci				data = IR_MAX_DURATION; /* really long time */
3668c2ecf20Sopenharmony_ci				if (!(dcd ^ sense)) {
3678c2ecf20Sopenharmony_ci					/* sanity check */
3688c2ecf20Sopenharmony_ci					dev_err(&serial_ir.pdev->dev,
3698c2ecf20Sopenharmony_ci						"dcd unexpected: %d %d %lldns %lldns\n",
3708c2ecf20Sopenharmony_ci						dcd, sense, ktime_to_ns(kt),
3718c2ecf20Sopenharmony_ci						ktime_to_ns(serial_ir.lastkt));
3728c2ecf20Sopenharmony_ci					/*
3738c2ecf20Sopenharmony_ci					 * detecting pulse while this
3748c2ecf20Sopenharmony_ci					 * MUST be a space!
3758c2ecf20Sopenharmony_ci					 */
3768c2ecf20Sopenharmony_ci					sense = sense ? 0 : 1;
3778c2ecf20Sopenharmony_ci				}
3788c2ecf20Sopenharmony_ci			} else {
3798c2ecf20Sopenharmony_ci				data = ktime_to_us(delkt);
3808c2ecf20Sopenharmony_ci			}
3818c2ecf20Sopenharmony_ci			frbwrite(data, !(dcd ^ sense));
3828c2ecf20Sopenharmony_ci			serial_ir.lastkt = kt;
3838c2ecf20Sopenharmony_ci			last_dcd = dcd;
3848c2ecf20Sopenharmony_ci		}
3858c2ecf20Sopenharmony_ci	} while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	mod_timer(&serial_ir.timeout_timer,
3888c2ecf20Sopenharmony_ci		  jiffies + usecs_to_jiffies(serial_ir.rcdev->timeout));
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	ir_raw_event_handle(serial_ir.rcdev);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int hardware_init_port(void)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	u8 scratch, scratch2, scratch3;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	/*
4008c2ecf20Sopenharmony_ci	 * This is a simple port existence test, borrowed from the autoconfig
4018c2ecf20Sopenharmony_ci	 * function in drivers/tty/serial/8250/8250_port.c
4028c2ecf20Sopenharmony_ci	 */
4038c2ecf20Sopenharmony_ci	scratch = sinp(UART_IER);
4048c2ecf20Sopenharmony_ci	soutp(UART_IER, 0);
4058c2ecf20Sopenharmony_ci#ifdef __i386__
4068c2ecf20Sopenharmony_ci	outb(0xff, 0x080);
4078c2ecf20Sopenharmony_ci#endif
4088c2ecf20Sopenharmony_ci	scratch2 = sinp(UART_IER) & 0x0f;
4098c2ecf20Sopenharmony_ci	soutp(UART_IER, 0x0f);
4108c2ecf20Sopenharmony_ci#ifdef __i386__
4118c2ecf20Sopenharmony_ci	outb(0x00, 0x080);
4128c2ecf20Sopenharmony_ci#endif
4138c2ecf20Sopenharmony_ci	scratch3 = sinp(UART_IER) & 0x0f;
4148c2ecf20Sopenharmony_ci	soutp(UART_IER, scratch);
4158c2ecf20Sopenharmony_ci	if (scratch2 != 0 || scratch3 != 0x0f) {
4168c2ecf20Sopenharmony_ci		/* we fail, there's nothing here */
4178c2ecf20Sopenharmony_ci		pr_err("port existence test failed, cannot continue\n");
4188c2ecf20Sopenharmony_ci		return -ENODEV;
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	/* Set DLAB 0. */
4228c2ecf20Sopenharmony_ci	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	/* First of all, disable all interrupts */
4258c2ecf20Sopenharmony_ci	soutp(UART_IER, sinp(UART_IER) &
4268c2ecf20Sopenharmony_ci	      (~(UART_IER_MSI | UART_IER_RLSI | UART_IER_THRI | UART_IER_RDI)));
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	/* Clear registers. */
4298c2ecf20Sopenharmony_ci	sinp(UART_LSR);
4308c2ecf20Sopenharmony_ci	sinp(UART_RX);
4318c2ecf20Sopenharmony_ci	sinp(UART_IIR);
4328c2ecf20Sopenharmony_ci	sinp(UART_MSR);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	/* Set line for power source */
4358c2ecf20Sopenharmony_ci	off();
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	/* Clear registers again to be sure. */
4388c2ecf20Sopenharmony_ci	sinp(UART_LSR);
4398c2ecf20Sopenharmony_ci	sinp(UART_RX);
4408c2ecf20Sopenharmony_ci	sinp(UART_IIR);
4418c2ecf20Sopenharmony_ci	sinp(UART_MSR);
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	switch (type) {
4448c2ecf20Sopenharmony_ci	case IR_IRDEO:
4458c2ecf20Sopenharmony_ci	case IR_IRDEO_REMOTE:
4468c2ecf20Sopenharmony_ci		/* setup port to 7N1 @ 115200 Baud */
4478c2ecf20Sopenharmony_ci		/* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci		/* Set DLAB 1. */
4508c2ecf20Sopenharmony_ci		soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
4518c2ecf20Sopenharmony_ci		/* Set divisor to 1 => 115200 Baud */
4528c2ecf20Sopenharmony_ci		soutp(UART_DLM, 0);
4538c2ecf20Sopenharmony_ci		soutp(UART_DLL, 1);
4548c2ecf20Sopenharmony_ci		/* Set DLAB 0 +  7N1 */
4558c2ecf20Sopenharmony_ci		soutp(UART_LCR, UART_LCR_WLEN7);
4568c2ecf20Sopenharmony_ci		/* THR interrupt already disabled at this point */
4578c2ecf20Sopenharmony_ci		break;
4588c2ecf20Sopenharmony_ci	default:
4598c2ecf20Sopenharmony_ci		break;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	return 0;
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic void serial_ir_timeout(struct timer_list *unused)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	struct ir_raw_event ev = {
4688c2ecf20Sopenharmony_ci		.timeout = true,
4698c2ecf20Sopenharmony_ci		.duration = serial_ir.rcdev->timeout
4708c2ecf20Sopenharmony_ci	};
4718c2ecf20Sopenharmony_ci	ir_raw_event_store_with_filter(serial_ir.rcdev, &ev);
4728c2ecf20Sopenharmony_ci	ir_raw_event_handle(serial_ir.rcdev);
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci/* Needed by serial_ir_probe() */
4768c2ecf20Sopenharmony_cistatic int serial_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
4778c2ecf20Sopenharmony_ci			unsigned int count);
4788c2ecf20Sopenharmony_cistatic int serial_ir_tx_duty_cycle(struct rc_dev *dev, u32 cycle);
4798c2ecf20Sopenharmony_cistatic int serial_ir_tx_carrier(struct rc_dev *dev, u32 carrier);
4808c2ecf20Sopenharmony_cistatic int serial_ir_open(struct rc_dev *rcdev);
4818c2ecf20Sopenharmony_cistatic void serial_ir_close(struct rc_dev *rcdev);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic int serial_ir_probe(struct platform_device *dev)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	struct rc_dev *rcdev;
4868c2ecf20Sopenharmony_ci	int i, nlow, nhigh, result;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	rcdev = devm_rc_allocate_device(&dev->dev, RC_DRIVER_IR_RAW);
4898c2ecf20Sopenharmony_ci	if (!rcdev)
4908c2ecf20Sopenharmony_ci		return -ENOMEM;
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (hardware[type].send_pulse && hardware[type].send_space)
4938c2ecf20Sopenharmony_ci		rcdev->tx_ir = serial_ir_tx;
4948c2ecf20Sopenharmony_ci	if (hardware[type].set_send_carrier)
4958c2ecf20Sopenharmony_ci		rcdev->s_tx_carrier = serial_ir_tx_carrier;
4968c2ecf20Sopenharmony_ci	if (hardware[type].set_duty_cycle)
4978c2ecf20Sopenharmony_ci		rcdev->s_tx_duty_cycle = serial_ir_tx_duty_cycle;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	switch (type) {
5008c2ecf20Sopenharmony_ci	case IR_HOMEBREW:
5018c2ecf20Sopenharmony_ci		rcdev->device_name = "Serial IR type home-brew";
5028c2ecf20Sopenharmony_ci		break;
5038c2ecf20Sopenharmony_ci	case IR_IRDEO:
5048c2ecf20Sopenharmony_ci		rcdev->device_name = "Serial IR type IRdeo";
5058c2ecf20Sopenharmony_ci		break;
5068c2ecf20Sopenharmony_ci	case IR_IRDEO_REMOTE:
5078c2ecf20Sopenharmony_ci		rcdev->device_name = "Serial IR type IRdeo remote";
5088c2ecf20Sopenharmony_ci		break;
5098c2ecf20Sopenharmony_ci	case IR_ANIMAX:
5108c2ecf20Sopenharmony_ci		rcdev->device_name = "Serial IR type AnimaX";
5118c2ecf20Sopenharmony_ci		break;
5128c2ecf20Sopenharmony_ci	case IR_IGOR:
5138c2ecf20Sopenharmony_ci		rcdev->device_name = "Serial IR type IgorPlug";
5148c2ecf20Sopenharmony_ci		break;
5158c2ecf20Sopenharmony_ci	}
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	rcdev->input_phys = KBUILD_MODNAME "/input0";
5188c2ecf20Sopenharmony_ci	rcdev->input_id.bustype = BUS_HOST;
5198c2ecf20Sopenharmony_ci	rcdev->input_id.vendor = 0x0001;
5208c2ecf20Sopenharmony_ci	rcdev->input_id.product = 0x0001;
5218c2ecf20Sopenharmony_ci	rcdev->input_id.version = 0x0100;
5228c2ecf20Sopenharmony_ci	rcdev->open = serial_ir_open;
5238c2ecf20Sopenharmony_ci	rcdev->close = serial_ir_close;
5248c2ecf20Sopenharmony_ci	rcdev->dev.parent = &serial_ir.pdev->dev;
5258c2ecf20Sopenharmony_ci	rcdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
5268c2ecf20Sopenharmony_ci	rcdev->driver_name = KBUILD_MODNAME;
5278c2ecf20Sopenharmony_ci	rcdev->map_name = RC_MAP_RC6_MCE;
5288c2ecf20Sopenharmony_ci	rcdev->min_timeout = 1;
5298c2ecf20Sopenharmony_ci	rcdev->timeout = IR_DEFAULT_TIMEOUT;
5308c2ecf20Sopenharmony_ci	rcdev->max_timeout = 10 * IR_DEFAULT_TIMEOUT;
5318c2ecf20Sopenharmony_ci	rcdev->rx_resolution = 250;
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	serial_ir.rcdev = rcdev;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	timer_setup(&serial_ir.timeout_timer, serial_ir_timeout, 0);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	result = devm_request_irq(&dev->dev, irq, serial_ir_irq_handler,
5388c2ecf20Sopenharmony_ci				  share_irq ? IRQF_SHARED : 0,
5398c2ecf20Sopenharmony_ci				  KBUILD_MODNAME, &hardware);
5408c2ecf20Sopenharmony_ci	if (result < 0) {
5418c2ecf20Sopenharmony_ci		if (result == -EBUSY)
5428c2ecf20Sopenharmony_ci			dev_err(&dev->dev, "IRQ %d busy\n", irq);
5438c2ecf20Sopenharmony_ci		else if (result == -EINVAL)
5448c2ecf20Sopenharmony_ci			dev_err(&dev->dev, "Bad irq number or handler\n");
5458c2ecf20Sopenharmony_ci		return result;
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	/* Reserve io region. */
5498c2ecf20Sopenharmony_ci	if ((iommap &&
5508c2ecf20Sopenharmony_ci	     (devm_request_mem_region(&dev->dev, iommap, 8UL << ioshift,
5518c2ecf20Sopenharmony_ci				      KBUILD_MODNAME) == NULL)) ||
5528c2ecf20Sopenharmony_ci	     (!iommap && (devm_request_region(&dev->dev, io, 8,
5538c2ecf20Sopenharmony_ci			  KBUILD_MODNAME) == NULL))) {
5548c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "port %04x already in use\n", io);
5558c2ecf20Sopenharmony_ci		dev_warn(&dev->dev, "use 'setserial /dev/ttySX uart none'\n");
5568c2ecf20Sopenharmony_ci		dev_warn(&dev->dev,
5578c2ecf20Sopenharmony_ci			 "or compile the serial port driver as module and\n");
5588c2ecf20Sopenharmony_ci		dev_warn(&dev->dev, "make sure this module is loaded first\n");
5598c2ecf20Sopenharmony_ci		return -EBUSY;
5608c2ecf20Sopenharmony_ci	}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	result = hardware_init_port();
5638c2ecf20Sopenharmony_ci	if (result < 0)
5648c2ecf20Sopenharmony_ci		return result;
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci	/* Initialize pulse/space widths */
5678c2ecf20Sopenharmony_ci	serial_ir.duty_cycle = 50;
5688c2ecf20Sopenharmony_ci	serial_ir.carrier = 38000;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	/* If pin is high, then this must be an active low receiver. */
5718c2ecf20Sopenharmony_ci	if (sense == -1) {
5728c2ecf20Sopenharmony_ci		/* wait 1/2 sec for the power supply */
5738c2ecf20Sopenharmony_ci		msleep(500);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci		/*
5768c2ecf20Sopenharmony_ci		 * probe 9 times every 0.04s, collect "votes" for
5778c2ecf20Sopenharmony_ci		 * active high/low
5788c2ecf20Sopenharmony_ci		 */
5798c2ecf20Sopenharmony_ci		nlow = 0;
5808c2ecf20Sopenharmony_ci		nhigh = 0;
5818c2ecf20Sopenharmony_ci		for (i = 0; i < 9; i++) {
5828c2ecf20Sopenharmony_ci			if (sinp(UART_MSR) & hardware[type].signal_pin)
5838c2ecf20Sopenharmony_ci				nlow++;
5848c2ecf20Sopenharmony_ci			else
5858c2ecf20Sopenharmony_ci				nhigh++;
5868c2ecf20Sopenharmony_ci			msleep(40);
5878c2ecf20Sopenharmony_ci		}
5888c2ecf20Sopenharmony_ci		sense = nlow >= nhigh ? 1 : 0;
5898c2ecf20Sopenharmony_ci		dev_info(&dev->dev, "auto-detected active %s receiver\n",
5908c2ecf20Sopenharmony_ci			 sense ? "low" : "high");
5918c2ecf20Sopenharmony_ci	} else
5928c2ecf20Sopenharmony_ci		dev_info(&dev->dev, "Manually using active %s receiver\n",
5938c2ecf20Sopenharmony_ci			 sense ? "low" : "high");
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	dev_dbg(&dev->dev, "Interrupt %d, port %04x obtained\n", irq, io);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	return devm_rc_register_device(&dev->dev, rcdev);
5988c2ecf20Sopenharmony_ci}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_cistatic int serial_ir_open(struct rc_dev *rcdev)
6018c2ecf20Sopenharmony_ci{
6028c2ecf20Sopenharmony_ci	unsigned long flags;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci	/* initialize timestamp */
6058c2ecf20Sopenharmony_ci	serial_ir.lastkt = ktime_get();
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hardware[type].lock, flags);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	/* Set DLAB 0. */
6108c2ecf20Sopenharmony_ci	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	soutp(UART_IER, sinp(UART_IER) | UART_IER_MSI);
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hardware[type].lock, flags);
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	return 0;
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic void serial_ir_close(struct rc_dev *rcdev)
6208c2ecf20Sopenharmony_ci{
6218c2ecf20Sopenharmony_ci	unsigned long flags;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hardware[type].lock, flags);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	/* Set DLAB 0. */
6268c2ecf20Sopenharmony_ci	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci	/* First of all, disable all interrupts */
6298c2ecf20Sopenharmony_ci	soutp(UART_IER, sinp(UART_IER) &
6308c2ecf20Sopenharmony_ci	      (~(UART_IER_MSI | UART_IER_RLSI | UART_IER_THRI | UART_IER_RDI)));
6318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hardware[type].lock, flags);
6328c2ecf20Sopenharmony_ci}
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_cistatic int serial_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
6358c2ecf20Sopenharmony_ci			unsigned int count)
6368c2ecf20Sopenharmony_ci{
6378c2ecf20Sopenharmony_ci	unsigned long flags;
6388c2ecf20Sopenharmony_ci	ktime_t edge;
6398c2ecf20Sopenharmony_ci	s64 delta;
6408c2ecf20Sopenharmony_ci	int i;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hardware[type].lock, flags);
6438c2ecf20Sopenharmony_ci	if (type == IR_IRDEO) {
6448c2ecf20Sopenharmony_ci		/* DTR, RTS down */
6458c2ecf20Sopenharmony_ci		on();
6468c2ecf20Sopenharmony_ci	}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	edge = ktime_get();
6498c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
6508c2ecf20Sopenharmony_ci		if (i % 2)
6518c2ecf20Sopenharmony_ci			hardware[type].send_space();
6528c2ecf20Sopenharmony_ci		else
6538c2ecf20Sopenharmony_ci			hardware[type].send_pulse(txbuf[i], edge);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci		edge = ktime_add_us(edge, txbuf[i]);
6568c2ecf20Sopenharmony_ci		delta = ktime_us_delta(edge, ktime_get());
6578c2ecf20Sopenharmony_ci		if (delta > 25) {
6588c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&hardware[type].lock, flags);
6598c2ecf20Sopenharmony_ci			usleep_range(delta - 25, delta + 25);
6608c2ecf20Sopenharmony_ci			spin_lock_irqsave(&hardware[type].lock, flags);
6618c2ecf20Sopenharmony_ci		} else if (delta > 0) {
6628c2ecf20Sopenharmony_ci			udelay(delta);
6638c2ecf20Sopenharmony_ci		}
6648c2ecf20Sopenharmony_ci	}
6658c2ecf20Sopenharmony_ci	off();
6668c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hardware[type].lock, flags);
6678c2ecf20Sopenharmony_ci	return count;
6688c2ecf20Sopenharmony_ci}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_cistatic int serial_ir_tx_duty_cycle(struct rc_dev *dev, u32 cycle)
6718c2ecf20Sopenharmony_ci{
6728c2ecf20Sopenharmony_ci	serial_ir.duty_cycle = cycle;
6738c2ecf20Sopenharmony_ci	return 0;
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_cistatic int serial_ir_tx_carrier(struct rc_dev *dev, u32 carrier)
6778c2ecf20Sopenharmony_ci{
6788c2ecf20Sopenharmony_ci	if (carrier > 500000 || carrier < 20000)
6798c2ecf20Sopenharmony_ci		return -EINVAL;
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	serial_ir.carrier = carrier;
6828c2ecf20Sopenharmony_ci	return 0;
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cistatic int serial_ir_suspend(struct platform_device *dev,
6868c2ecf20Sopenharmony_ci			     pm_message_t state)
6878c2ecf20Sopenharmony_ci{
6888c2ecf20Sopenharmony_ci	/* Set DLAB 0. */
6898c2ecf20Sopenharmony_ci	soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	/* Disable all interrupts */
6928c2ecf20Sopenharmony_ci	soutp(UART_IER, sinp(UART_IER) &
6938c2ecf20Sopenharmony_ci	      (~(UART_IER_MSI | UART_IER_RLSI | UART_IER_THRI | UART_IER_RDI)));
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	/* Clear registers. */
6968c2ecf20Sopenharmony_ci	sinp(UART_LSR);
6978c2ecf20Sopenharmony_ci	sinp(UART_RX);
6988c2ecf20Sopenharmony_ci	sinp(UART_IIR);
6998c2ecf20Sopenharmony_ci	sinp(UART_MSR);
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci	return 0;
7028c2ecf20Sopenharmony_ci}
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_cistatic int serial_ir_resume(struct platform_device *dev)
7058c2ecf20Sopenharmony_ci{
7068c2ecf20Sopenharmony_ci	unsigned long flags;
7078c2ecf20Sopenharmony_ci	int result;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	result = hardware_init_port();
7108c2ecf20Sopenharmony_ci	if (result < 0)
7118c2ecf20Sopenharmony_ci		return result;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&hardware[type].lock, flags);
7148c2ecf20Sopenharmony_ci	/* Enable Interrupt */
7158c2ecf20Sopenharmony_ci	serial_ir.lastkt = ktime_get();
7168c2ecf20Sopenharmony_ci	soutp(UART_IER, sinp(UART_IER) | UART_IER_MSI);
7178c2ecf20Sopenharmony_ci	off();
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&hardware[type].lock, flags);
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	return 0;
7228c2ecf20Sopenharmony_ci}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_cistatic struct platform_driver serial_ir_driver = {
7258c2ecf20Sopenharmony_ci	.probe		= serial_ir_probe,
7268c2ecf20Sopenharmony_ci	.suspend	= serial_ir_suspend,
7278c2ecf20Sopenharmony_ci	.resume		= serial_ir_resume,
7288c2ecf20Sopenharmony_ci	.driver		= {
7298c2ecf20Sopenharmony_ci		.name	= "serial_ir",
7308c2ecf20Sopenharmony_ci	},
7318c2ecf20Sopenharmony_ci};
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_cistatic int __init serial_ir_init(void)
7348c2ecf20Sopenharmony_ci{
7358c2ecf20Sopenharmony_ci	int result;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	result = platform_driver_register(&serial_ir_driver);
7388c2ecf20Sopenharmony_ci	if (result)
7398c2ecf20Sopenharmony_ci		return result;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	serial_ir.pdev = platform_device_alloc("serial_ir", 0);
7428c2ecf20Sopenharmony_ci	if (!serial_ir.pdev) {
7438c2ecf20Sopenharmony_ci		result = -ENOMEM;
7448c2ecf20Sopenharmony_ci		goto exit_driver_unregister;
7458c2ecf20Sopenharmony_ci	}
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci	result = platform_device_add(serial_ir.pdev);
7488c2ecf20Sopenharmony_ci	if (result)
7498c2ecf20Sopenharmony_ci		goto exit_device_put;
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ci	return 0;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ciexit_device_put:
7548c2ecf20Sopenharmony_ci	platform_device_put(serial_ir.pdev);
7558c2ecf20Sopenharmony_ciexit_driver_unregister:
7568c2ecf20Sopenharmony_ci	platform_driver_unregister(&serial_ir_driver);
7578c2ecf20Sopenharmony_ci	return result;
7588c2ecf20Sopenharmony_ci}
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_cistatic void serial_ir_exit(void)
7618c2ecf20Sopenharmony_ci{
7628c2ecf20Sopenharmony_ci	platform_device_unregister(serial_ir.pdev);
7638c2ecf20Sopenharmony_ci	platform_driver_unregister(&serial_ir_driver);
7648c2ecf20Sopenharmony_ci}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_cistatic int __init serial_ir_init_module(void)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci	switch (type) {
7698c2ecf20Sopenharmony_ci	case IR_HOMEBREW:
7708c2ecf20Sopenharmony_ci	case IR_IRDEO:
7718c2ecf20Sopenharmony_ci	case IR_IRDEO_REMOTE:
7728c2ecf20Sopenharmony_ci	case IR_ANIMAX:
7738c2ecf20Sopenharmony_ci	case IR_IGOR:
7748c2ecf20Sopenharmony_ci		/* if nothing specified, use ttyS0/com1 and irq 4 */
7758c2ecf20Sopenharmony_ci		io = io ? io : 0x3f8;
7768c2ecf20Sopenharmony_ci		irq = irq ? irq : 4;
7778c2ecf20Sopenharmony_ci		break;
7788c2ecf20Sopenharmony_ci	default:
7798c2ecf20Sopenharmony_ci		return -EINVAL;
7808c2ecf20Sopenharmony_ci	}
7818c2ecf20Sopenharmony_ci	if (!softcarrier) {
7828c2ecf20Sopenharmony_ci		switch (type) {
7838c2ecf20Sopenharmony_ci		case IR_HOMEBREW:
7848c2ecf20Sopenharmony_ci		case IR_IGOR:
7858c2ecf20Sopenharmony_ci			hardware[type].set_send_carrier = false;
7868c2ecf20Sopenharmony_ci			hardware[type].set_duty_cycle = false;
7878c2ecf20Sopenharmony_ci			break;
7888c2ecf20Sopenharmony_ci		}
7898c2ecf20Sopenharmony_ci	}
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	/* make sure sense is either -1, 0, or 1 */
7928c2ecf20Sopenharmony_ci	if (sense != -1)
7938c2ecf20Sopenharmony_ci		sense = !!sense;
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	return serial_ir_init();
7968c2ecf20Sopenharmony_ci}
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_cistatic void __exit serial_ir_exit_module(void)
7998c2ecf20Sopenharmony_ci{
8008c2ecf20Sopenharmony_ci	del_timer_sync(&serial_ir.timeout_timer);
8018c2ecf20Sopenharmony_ci	serial_ir_exit();
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_cimodule_init(serial_ir_init_module);
8058c2ecf20Sopenharmony_cimodule_exit(serial_ir_exit_module);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Infra-red receiver driver for serial ports.");
8088c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, Christoph Bartelmus, Andrei Tanas");
8098c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_cimodule_param(type, int, 0444);
8128c2ecf20Sopenharmony_ciMODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo, 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug");
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_cimodule_param_hw(io, int, ioport, 0444);
8158c2ecf20Sopenharmony_ciMODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci/* some architectures (e.g. intel xscale) have memory mapped registers */
8188c2ecf20Sopenharmony_cimodule_param_hw(iommap, ulong, other, 0444);
8198c2ecf20Sopenharmony_ciMODULE_PARM_DESC(iommap, "physical base for memory mapped I/O (0 = no memory mapped io)");
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci/*
8228c2ecf20Sopenharmony_ci * some architectures (e.g. intel xscale) align the 8bit serial registers
8238c2ecf20Sopenharmony_ci * on 32bit word boundaries.
8248c2ecf20Sopenharmony_ci * See linux-kernel/drivers/tty/serial/8250/8250.c serial_in()/out()
8258c2ecf20Sopenharmony_ci */
8268c2ecf20Sopenharmony_cimodule_param_hw(ioshift, int, other, 0444);
8278c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)");
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cimodule_param_hw(irq, int, irq, 0444);
8308c2ecf20Sopenharmony_ciMODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_cimodule_param_hw(share_irq, bool, other, 0444);
8338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)");
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cimodule_param(sense, int, 0444);
8368c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit (0 = active high, 1 = active low )");
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci#ifdef CONFIG_IR_SERIAL_TRANSMITTER
8398c2ecf20Sopenharmony_cimodule_param(txsense, bool, 0444);
8408c2ecf20Sopenharmony_ciMODULE_PARM_DESC(txsense, "Sense of transmitter circuit (0 = active high, 1 = active low )");
8418c2ecf20Sopenharmony_ci#endif
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_cimodule_param(softcarrier, bool, 0444);
8448c2ecf20Sopenharmony_ciMODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)");
845