18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Driver for Nuvoton Technology Corporation w83667hg/w83677hg-i CIR
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2010 Jarod Wilson <jarod@redhat.com>
58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Nuvoton PS Team
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Special thanks to Nuvoton for providing hardware, spec sheets and
88c2ecf20Sopenharmony_ci * sample code upon which portions of this driver are based. Indirect
98c2ecf20Sopenharmony_ci * thanks also to Maxim Levitsky, whose ene_ir driver this driver is
108c2ecf20Sopenharmony_ci * modeled after.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or
138c2ecf20Sopenharmony_ci * modify it under the terms of the GNU General Public License as
148c2ecf20Sopenharmony_ci * published by the Free Software Foundation; either version 2 of the
158c2ecf20Sopenharmony_ci * License, or (at your option) any later version.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but
188c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of
198c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
208c2ecf20Sopenharmony_ci * General Public License for more details.
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#include <linux/kernel.h>
268c2ecf20Sopenharmony_ci#include <linux/module.h>
278c2ecf20Sopenharmony_ci#include <linux/pnp.h>
288c2ecf20Sopenharmony_ci#include <linux/io.h>
298c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
308c2ecf20Sopenharmony_ci#include <linux/sched.h>
318c2ecf20Sopenharmony_ci#include <linux/slab.h>
328c2ecf20Sopenharmony_ci#include <media/rc-core.h>
338c2ecf20Sopenharmony_ci#include <linux/pci_ids.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include "nuvoton-cir.h"
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic void nvt_clear_cir_wake_fifo(struct nvt_dev *nvt);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic const struct nvt_chip nvt_chips[] = {
408c2ecf20Sopenharmony_ci	{ "w83667hg", NVT_W83667HG },
418c2ecf20Sopenharmony_ci	{ "NCT6775F", NVT_6775F },
428c2ecf20Sopenharmony_ci	{ "NCT6776F", NVT_6776F },
438c2ecf20Sopenharmony_ci	{ "NCT6779D", NVT_6779D },
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic inline struct device *nvt_get_dev(const struct nvt_dev *nvt)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	return nvt->rdev->dev.parent;
498c2ecf20Sopenharmony_ci}
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic inline bool is_w83667hg(struct nvt_dev *nvt)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	return nvt->chip_ver == NVT_W83667HG;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/* write val to config reg */
578c2ecf20Sopenharmony_cistatic inline void nvt_cr_write(struct nvt_dev *nvt, u8 val, u8 reg)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	outb(reg, nvt->cr_efir);
608c2ecf20Sopenharmony_ci	outb(val, nvt->cr_efdr);
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/* read val from config reg */
648c2ecf20Sopenharmony_cistatic inline u8 nvt_cr_read(struct nvt_dev *nvt, u8 reg)
658c2ecf20Sopenharmony_ci{
668c2ecf20Sopenharmony_ci	outb(reg, nvt->cr_efir);
678c2ecf20Sopenharmony_ci	return inb(nvt->cr_efdr);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* update config register bit without changing other bits */
718c2ecf20Sopenharmony_cistatic inline void nvt_set_reg_bit(struct nvt_dev *nvt, u8 val, u8 reg)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	u8 tmp = nvt_cr_read(nvt, reg) | val;
748c2ecf20Sopenharmony_ci	nvt_cr_write(nvt, tmp, reg);
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/* enter extended function mode */
788c2ecf20Sopenharmony_cistatic inline int nvt_efm_enable(struct nvt_dev *nvt)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	if (!request_muxed_region(nvt->cr_efir, 2, NVT_DRIVER_NAME))
818c2ecf20Sopenharmony_ci		return -EBUSY;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	/* Enabling Extended Function Mode explicitly requires writing 2x */
848c2ecf20Sopenharmony_ci	outb(EFER_EFM_ENABLE, nvt->cr_efir);
858c2ecf20Sopenharmony_ci	outb(EFER_EFM_ENABLE, nvt->cr_efir);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	return 0;
888c2ecf20Sopenharmony_ci}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci/* exit extended function mode */
918c2ecf20Sopenharmony_cistatic inline void nvt_efm_disable(struct nvt_dev *nvt)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	outb(EFER_EFM_DISABLE, nvt->cr_efir);
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	release_region(nvt->cr_efir, 2);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci/*
998c2ecf20Sopenharmony_ci * When you want to address a specific logical device, write its logical
1008c2ecf20Sopenharmony_ci * device number to CR_LOGICAL_DEV_SEL, then enable/disable by writing
1018c2ecf20Sopenharmony_ci * 0x1/0x0 respectively to CR_LOGICAL_DEV_EN.
1028c2ecf20Sopenharmony_ci */
1038c2ecf20Sopenharmony_cistatic inline void nvt_select_logical_dev(struct nvt_dev *nvt, u8 ldev)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	nvt_cr_write(nvt, ldev, CR_LOGICAL_DEV_SEL);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/* select and enable logical device with setting EFM mode*/
1098c2ecf20Sopenharmony_cistatic inline void nvt_enable_logical_dev(struct nvt_dev *nvt, u8 ldev)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	nvt_efm_enable(nvt);
1128c2ecf20Sopenharmony_ci	nvt_select_logical_dev(nvt, ldev);
1138c2ecf20Sopenharmony_ci	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
1148c2ecf20Sopenharmony_ci	nvt_efm_disable(nvt);
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/* select and disable logical device with setting EFM mode*/
1188c2ecf20Sopenharmony_cistatic inline void nvt_disable_logical_dev(struct nvt_dev *nvt, u8 ldev)
1198c2ecf20Sopenharmony_ci{
1208c2ecf20Sopenharmony_ci	nvt_efm_enable(nvt);
1218c2ecf20Sopenharmony_ci	nvt_select_logical_dev(nvt, ldev);
1228c2ecf20Sopenharmony_ci	nvt_cr_write(nvt, LOGICAL_DEV_DISABLE, CR_LOGICAL_DEV_EN);
1238c2ecf20Sopenharmony_ci	nvt_efm_disable(nvt);
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/* write val to cir config register */
1278c2ecf20Sopenharmony_cistatic inline void nvt_cir_reg_write(struct nvt_dev *nvt, u8 val, u8 offset)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	outb(val, nvt->cir_addr + offset);
1308c2ecf20Sopenharmony_ci}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci/* read val from cir config register */
1338c2ecf20Sopenharmony_cistatic u8 nvt_cir_reg_read(struct nvt_dev *nvt, u8 offset)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	return inb(nvt->cir_addr + offset);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci/* write val to cir wake register */
1398c2ecf20Sopenharmony_cistatic inline void nvt_cir_wake_reg_write(struct nvt_dev *nvt,
1408c2ecf20Sopenharmony_ci					  u8 val, u8 offset)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	outb(val, nvt->cir_wake_addr + offset);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci/* read val from cir wake config register */
1468c2ecf20Sopenharmony_cistatic u8 nvt_cir_wake_reg_read(struct nvt_dev *nvt, u8 offset)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	return inb(nvt->cir_wake_addr + offset);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/* don't override io address if one is set already */
1528c2ecf20Sopenharmony_cistatic void nvt_set_ioaddr(struct nvt_dev *nvt, unsigned long *ioaddr)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	unsigned long old_addr;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	old_addr = nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8;
1578c2ecf20Sopenharmony_ci	old_addr |= nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (old_addr)
1608c2ecf20Sopenharmony_ci		*ioaddr = old_addr;
1618c2ecf20Sopenharmony_ci	else {
1628c2ecf20Sopenharmony_ci		nvt_cr_write(nvt, *ioaddr >> 8, CR_CIR_BASE_ADDR_HI);
1638c2ecf20Sopenharmony_ci		nvt_cr_write(nvt, *ioaddr & 0xff, CR_CIR_BASE_ADDR_LO);
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic void nvt_write_wakeup_codes(struct rc_dev *dev,
1688c2ecf20Sopenharmony_ci				   const u8 *wbuf, int count)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	u8 tolerance, config;
1718c2ecf20Sopenharmony_ci	struct nvt_dev *nvt = dev->priv;
1728c2ecf20Sopenharmony_ci	unsigned long flags;
1738c2ecf20Sopenharmony_ci	int i;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	/* hardcode the tolerance to 10% */
1768c2ecf20Sopenharmony_ci	tolerance = DIV_ROUND_UP(count, 10);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nvt->lock, flags);
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	nvt_clear_cir_wake_fifo(nvt);
1818c2ecf20Sopenharmony_ci	nvt_cir_wake_reg_write(nvt, count, CIR_WAKE_FIFO_CMP_DEEP);
1828c2ecf20Sopenharmony_ci	nvt_cir_wake_reg_write(nvt, tolerance, CIR_WAKE_FIFO_CMP_TOL);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	config = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* enable writes to wake fifo */
1878c2ecf20Sopenharmony_ci	nvt_cir_wake_reg_write(nvt, config | CIR_WAKE_IRCON_MODE1,
1888c2ecf20Sopenharmony_ci			       CIR_WAKE_IRCON);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	if (count)
1918c2ecf20Sopenharmony_ci		pr_info("Wake samples (%d) =", count);
1928c2ecf20Sopenharmony_ci	else
1938c2ecf20Sopenharmony_ci		pr_info("Wake sample fifo cleared");
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++)
1968c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_write(nvt, wbuf[i], CIR_WAKE_WR_FIFO_DATA);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	nvt_cir_wake_reg_write(nvt, config, CIR_WAKE_IRCON);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nvt->lock, flags);
2018c2ecf20Sopenharmony_ci}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_cistatic ssize_t wakeup_data_show(struct device *dev,
2048c2ecf20Sopenharmony_ci				struct device_attribute *attr,
2058c2ecf20Sopenharmony_ci				char *buf)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct rc_dev *rc_dev = to_rc_dev(dev);
2088c2ecf20Sopenharmony_ci	struct nvt_dev *nvt = rc_dev->priv;
2098c2ecf20Sopenharmony_ci	int fifo_len, duration;
2108c2ecf20Sopenharmony_ci	unsigned long flags;
2118c2ecf20Sopenharmony_ci	ssize_t buf_len = 0;
2128c2ecf20Sopenharmony_ci	int i;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nvt->lock, flags);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	fifo_len = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT);
2178c2ecf20Sopenharmony_ci	fifo_len = min(fifo_len, WAKEUP_MAX_SIZE);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/* go to first element to be read */
2208c2ecf20Sopenharmony_ci	while (nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX))
2218c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	for (i = 0; i < fifo_len; i++) {
2248c2ecf20Sopenharmony_ci		duration = nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY);
2258c2ecf20Sopenharmony_ci		duration = (duration & BUF_LEN_MASK) * SAMPLE_PERIOD;
2268c2ecf20Sopenharmony_ci		buf_len += scnprintf(buf + buf_len, PAGE_SIZE - buf_len,
2278c2ecf20Sopenharmony_ci				    "%d ", duration);
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci	buf_len += scnprintf(buf + buf_len, PAGE_SIZE - buf_len, "\n");
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nvt->lock, flags);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	return buf_len;
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic ssize_t wakeup_data_store(struct device *dev,
2378c2ecf20Sopenharmony_ci				 struct device_attribute *attr,
2388c2ecf20Sopenharmony_ci				 const char *buf, size_t len)
2398c2ecf20Sopenharmony_ci{
2408c2ecf20Sopenharmony_ci	struct rc_dev *rc_dev = to_rc_dev(dev);
2418c2ecf20Sopenharmony_ci	u8 wake_buf[WAKEUP_MAX_SIZE];
2428c2ecf20Sopenharmony_ci	char **argv;
2438c2ecf20Sopenharmony_ci	int i, count;
2448c2ecf20Sopenharmony_ci	unsigned int val;
2458c2ecf20Sopenharmony_ci	ssize_t ret;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	argv = argv_split(GFP_KERNEL, buf, &count);
2488c2ecf20Sopenharmony_ci	if (!argv)
2498c2ecf20Sopenharmony_ci		return -ENOMEM;
2508c2ecf20Sopenharmony_ci	if (!count || count > WAKEUP_MAX_SIZE) {
2518c2ecf20Sopenharmony_ci		ret = -EINVAL;
2528c2ecf20Sopenharmony_ci		goto out;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
2568c2ecf20Sopenharmony_ci		ret = kstrtouint(argv[i], 10, &val);
2578c2ecf20Sopenharmony_ci		if (ret)
2588c2ecf20Sopenharmony_ci			goto out;
2598c2ecf20Sopenharmony_ci		val = DIV_ROUND_CLOSEST(val, SAMPLE_PERIOD);
2608c2ecf20Sopenharmony_ci		if (!val || val > 0x7f) {
2618c2ecf20Sopenharmony_ci			ret = -EINVAL;
2628c2ecf20Sopenharmony_ci			goto out;
2638c2ecf20Sopenharmony_ci		}
2648c2ecf20Sopenharmony_ci		wake_buf[i] = val;
2658c2ecf20Sopenharmony_ci		/* sequence must start with a pulse */
2668c2ecf20Sopenharmony_ci		if (i % 2 == 0)
2678c2ecf20Sopenharmony_ci			wake_buf[i] |= BUF_PULSE_BIT;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	nvt_write_wakeup_codes(rc_dev, wake_buf, count);
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	ret = len;
2738c2ecf20Sopenharmony_ciout:
2748c2ecf20Sopenharmony_ci	argv_free(argv);
2758c2ecf20Sopenharmony_ci	return ret;
2768c2ecf20Sopenharmony_ci}
2778c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(wakeup_data);
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci/* dump current cir register contents */
2808c2ecf20Sopenharmony_cistatic void cir_dump_regs(struct nvt_dev *nvt)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	nvt_efm_enable(nvt);
2838c2ecf20Sopenharmony_ci	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	pr_info("%s: Dump CIR logical device registers:\n", NVT_DRIVER_NAME);
2868c2ecf20Sopenharmony_ci	pr_info(" * CR CIR ACTIVE :   0x%x\n",
2878c2ecf20Sopenharmony_ci		nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
2888c2ecf20Sopenharmony_ci	pr_info(" * CR CIR BASE ADDR: 0x%x\n",
2898c2ecf20Sopenharmony_ci		(nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
2908c2ecf20Sopenharmony_ci		nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO));
2918c2ecf20Sopenharmony_ci	pr_info(" * CR CIR IRQ NUM:   0x%x\n",
2928c2ecf20Sopenharmony_ci		nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	nvt_efm_disable(nvt);
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	pr_info("%s: Dump CIR registers:\n", NVT_DRIVER_NAME);
2978c2ecf20Sopenharmony_ci	pr_info(" * IRCON:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRCON));
2988c2ecf20Sopenharmony_ci	pr_info(" * IRSTS:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRSTS));
2998c2ecf20Sopenharmony_ci	pr_info(" * IREN:      0x%x\n", nvt_cir_reg_read(nvt, CIR_IREN));
3008c2ecf20Sopenharmony_ci	pr_info(" * RXFCONT:   0x%x\n", nvt_cir_reg_read(nvt, CIR_RXFCONT));
3018c2ecf20Sopenharmony_ci	pr_info(" * CP:        0x%x\n", nvt_cir_reg_read(nvt, CIR_CP));
3028c2ecf20Sopenharmony_ci	pr_info(" * CC:        0x%x\n", nvt_cir_reg_read(nvt, CIR_CC));
3038c2ecf20Sopenharmony_ci	pr_info(" * SLCH:      0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCH));
3048c2ecf20Sopenharmony_ci	pr_info(" * SLCL:      0x%x\n", nvt_cir_reg_read(nvt, CIR_SLCL));
3058c2ecf20Sopenharmony_ci	pr_info(" * FIFOCON:   0x%x\n", nvt_cir_reg_read(nvt, CIR_FIFOCON));
3068c2ecf20Sopenharmony_ci	pr_info(" * IRFIFOSTS: 0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFIFOSTS));
3078c2ecf20Sopenharmony_ci	pr_info(" * SRXFIFO:   0x%x\n", nvt_cir_reg_read(nvt, CIR_SRXFIFO));
3088c2ecf20Sopenharmony_ci	pr_info(" * TXFCONT:   0x%x\n", nvt_cir_reg_read(nvt, CIR_TXFCONT));
3098c2ecf20Sopenharmony_ci	pr_info(" * STXFIFO:   0x%x\n", nvt_cir_reg_read(nvt, CIR_STXFIFO));
3108c2ecf20Sopenharmony_ci	pr_info(" * FCCH:      0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCH));
3118c2ecf20Sopenharmony_ci	pr_info(" * FCCL:      0x%x\n", nvt_cir_reg_read(nvt, CIR_FCCL));
3128c2ecf20Sopenharmony_ci	pr_info(" * IRFSM:     0x%x\n", nvt_cir_reg_read(nvt, CIR_IRFSM));
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci/* dump current cir wake register contents */
3168c2ecf20Sopenharmony_cistatic void cir_wake_dump_regs(struct nvt_dev *nvt)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	u8 i, fifo_len;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	nvt_efm_enable(nvt);
3218c2ecf20Sopenharmony_ci	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	pr_info("%s: Dump CIR WAKE logical device registers:\n",
3248c2ecf20Sopenharmony_ci		NVT_DRIVER_NAME);
3258c2ecf20Sopenharmony_ci	pr_info(" * CR CIR WAKE ACTIVE :   0x%x\n",
3268c2ecf20Sopenharmony_ci		nvt_cr_read(nvt, CR_LOGICAL_DEV_EN));
3278c2ecf20Sopenharmony_ci	pr_info(" * CR CIR WAKE BASE ADDR: 0x%x\n",
3288c2ecf20Sopenharmony_ci		(nvt_cr_read(nvt, CR_CIR_BASE_ADDR_HI) << 8) |
3298c2ecf20Sopenharmony_ci		nvt_cr_read(nvt, CR_CIR_BASE_ADDR_LO));
3308c2ecf20Sopenharmony_ci	pr_info(" * CR CIR WAKE IRQ NUM:   0x%x\n",
3318c2ecf20Sopenharmony_ci		nvt_cr_read(nvt, CR_CIR_IRQ_RSRC));
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	nvt_efm_disable(nvt);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	pr_info("%s: Dump CIR WAKE registers\n", NVT_DRIVER_NAME);
3368c2ecf20Sopenharmony_ci	pr_info(" * IRCON:          0x%x\n",
3378c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON));
3388c2ecf20Sopenharmony_ci	pr_info(" * IRSTS:          0x%x\n",
3398c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRSTS));
3408c2ecf20Sopenharmony_ci	pr_info(" * IREN:           0x%x\n",
3418c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_IREN));
3428c2ecf20Sopenharmony_ci	pr_info(" * FIFO CMP DEEP:  0x%x\n",
3438c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_DEEP));
3448c2ecf20Sopenharmony_ci	pr_info(" * FIFO CMP TOL:   0x%x\n",
3458c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_CMP_TOL));
3468c2ecf20Sopenharmony_ci	pr_info(" * FIFO COUNT:     0x%x\n",
3478c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT));
3488c2ecf20Sopenharmony_ci	pr_info(" * SLCH:           0x%x\n",
3498c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCH));
3508c2ecf20Sopenharmony_ci	pr_info(" * SLCL:           0x%x\n",
3518c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_SLCL));
3528c2ecf20Sopenharmony_ci	pr_info(" * FIFOCON:        0x%x\n",
3538c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON));
3548c2ecf20Sopenharmony_ci	pr_info(" * SRXFSTS:        0x%x\n",
3558c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_SRXFSTS));
3568c2ecf20Sopenharmony_ci	pr_info(" * SAMPLE RX FIFO: 0x%x\n",
3578c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_SAMPLE_RX_FIFO));
3588c2ecf20Sopenharmony_ci	pr_info(" * WR FIFO DATA:   0x%x\n",
3598c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_WR_FIFO_DATA));
3608c2ecf20Sopenharmony_ci	pr_info(" * RD FIFO ONLY:   0x%x\n",
3618c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
3628c2ecf20Sopenharmony_ci	pr_info(" * RD FIFO ONLY IDX: 0x%x\n",
3638c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY_IDX));
3648c2ecf20Sopenharmony_ci	pr_info(" * FIFO IGNORE:    0x%x\n",
3658c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_IGNORE));
3668c2ecf20Sopenharmony_ci	pr_info(" * IRFSM:          0x%x\n",
3678c2ecf20Sopenharmony_ci		nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRFSM));
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	fifo_len = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFO_COUNT);
3708c2ecf20Sopenharmony_ci	pr_info("%s: Dump CIR WAKE FIFO (len %d)\n", NVT_DRIVER_NAME, fifo_len);
3718c2ecf20Sopenharmony_ci	pr_info("* Contents =");
3728c2ecf20Sopenharmony_ci	for (i = 0; i < fifo_len; i++)
3738c2ecf20Sopenharmony_ci		pr_cont(" %02x",
3748c2ecf20Sopenharmony_ci			nvt_cir_wake_reg_read(nvt, CIR_WAKE_RD_FIFO_ONLY));
3758c2ecf20Sopenharmony_ci	pr_cont("\n");
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic inline const char *nvt_find_chip(struct nvt_dev *nvt, int id)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	int i;
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(nvt_chips); i++)
3838c2ecf20Sopenharmony_ci		if ((id & SIO_ID_MASK) == nvt_chips[i].chip_ver) {
3848c2ecf20Sopenharmony_ci			nvt->chip_ver = nvt_chips[i].chip_ver;
3858c2ecf20Sopenharmony_ci			return nvt_chips[i].name;
3868c2ecf20Sopenharmony_ci		}
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	return NULL;
3898c2ecf20Sopenharmony_ci}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci/* detect hardware features */
3938c2ecf20Sopenharmony_cistatic int nvt_hw_detect(struct nvt_dev *nvt)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct device *dev = nvt_get_dev(nvt);
3968c2ecf20Sopenharmony_ci	const char *chip_name;
3978c2ecf20Sopenharmony_ci	int chip_id;
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	nvt_efm_enable(nvt);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	/* Check if we're wired for the alternate EFER setup */
4028c2ecf20Sopenharmony_ci	nvt->chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI);
4038c2ecf20Sopenharmony_ci	if (nvt->chip_major == 0xff) {
4048c2ecf20Sopenharmony_ci		nvt_efm_disable(nvt);
4058c2ecf20Sopenharmony_ci		nvt->cr_efir = CR_EFIR2;
4068c2ecf20Sopenharmony_ci		nvt->cr_efdr = CR_EFDR2;
4078c2ecf20Sopenharmony_ci		nvt_efm_enable(nvt);
4088c2ecf20Sopenharmony_ci		nvt->chip_major = nvt_cr_read(nvt, CR_CHIP_ID_HI);
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci	nvt->chip_minor = nvt_cr_read(nvt, CR_CHIP_ID_LO);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	nvt_efm_disable(nvt);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	chip_id = nvt->chip_major << 8 | nvt->chip_minor;
4158c2ecf20Sopenharmony_ci	if (chip_id == NVT_INVALID) {
4168c2ecf20Sopenharmony_ci		dev_err(dev, "No device found on either EFM port\n");
4178c2ecf20Sopenharmony_ci		return -ENODEV;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	chip_name = nvt_find_chip(nvt, chip_id);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	/* warn, but still let the driver load, if we don't know this chip */
4238c2ecf20Sopenharmony_ci	if (!chip_name)
4248c2ecf20Sopenharmony_ci		dev_warn(dev,
4258c2ecf20Sopenharmony_ci			 "unknown chip, id: 0x%02x 0x%02x, it may not work...",
4268c2ecf20Sopenharmony_ci			 nvt->chip_major, nvt->chip_minor);
4278c2ecf20Sopenharmony_ci	else
4288c2ecf20Sopenharmony_ci		dev_info(dev, "found %s or compatible: chip id: 0x%02x 0x%02x",
4298c2ecf20Sopenharmony_ci			 chip_name, nvt->chip_major, nvt->chip_minor);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return 0;
4328c2ecf20Sopenharmony_ci}
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic void nvt_cir_ldev_init(struct nvt_dev *nvt)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	u8 val, psreg, psmask, psval;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	if (is_w83667hg(nvt)) {
4398c2ecf20Sopenharmony_ci		psreg = CR_MULTIFUNC_PIN_SEL;
4408c2ecf20Sopenharmony_ci		psmask = MULTIFUNC_PIN_SEL_MASK;
4418c2ecf20Sopenharmony_ci		psval = MULTIFUNC_ENABLE_CIR | MULTIFUNC_ENABLE_CIRWB;
4428c2ecf20Sopenharmony_ci	} else {
4438c2ecf20Sopenharmony_ci		psreg = CR_OUTPUT_PIN_SEL;
4448c2ecf20Sopenharmony_ci		psmask = OUTPUT_PIN_SEL_MASK;
4458c2ecf20Sopenharmony_ci		psval = OUTPUT_ENABLE_CIR | OUTPUT_ENABLE_CIRWB;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	/* output pin selection: enable CIR, with WB sensor enabled */
4498c2ecf20Sopenharmony_ci	val = nvt_cr_read(nvt, psreg);
4508c2ecf20Sopenharmony_ci	val &= psmask;
4518c2ecf20Sopenharmony_ci	val |= psval;
4528c2ecf20Sopenharmony_ci	nvt_cr_write(nvt, val, psreg);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	/* Select CIR logical device */
4558c2ecf20Sopenharmony_ci	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	nvt_set_ioaddr(nvt, &nvt->cir_addr);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	nvt_cr_write(nvt, nvt->cir_irq, CR_CIR_IRQ_RSRC);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	nvt_dbg("CIR initialized, base io port address: 0x%lx, irq: %d",
4628c2ecf20Sopenharmony_ci		nvt->cir_addr, nvt->cir_irq);
4638c2ecf20Sopenharmony_ci}
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic void nvt_cir_wake_ldev_init(struct nvt_dev *nvt)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	/* Select ACPI logical device and anable it */
4688c2ecf20Sopenharmony_ci	nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI);
4698c2ecf20Sopenharmony_ci	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	/* Enable CIR Wake via PSOUT# (Pin60) */
4728c2ecf20Sopenharmony_ci	nvt_set_reg_bit(nvt, CIR_WAKE_ENABLE_BIT, CR_ACPI_CIR_WAKE);
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/* enable pme interrupt of cir wakeup event */
4758c2ecf20Sopenharmony_ci	nvt_set_reg_bit(nvt, PME_INTR_CIR_PASS_BIT, CR_ACPI_IRQ_EVENTS2);
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	/* Select CIR Wake logical device */
4788c2ecf20Sopenharmony_ci	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	nvt_set_ioaddr(nvt, &nvt->cir_wake_addr);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	nvt_dbg("CIR Wake initialized, base io port address: 0x%lx",
4838c2ecf20Sopenharmony_ci		nvt->cir_wake_addr);
4848c2ecf20Sopenharmony_ci}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci/* clear out the hardware's cir rx fifo */
4878c2ecf20Sopenharmony_cistatic void nvt_clear_cir_fifo(struct nvt_dev *nvt)
4888c2ecf20Sopenharmony_ci{
4898c2ecf20Sopenharmony_ci	u8 val = nvt_cir_reg_read(nvt, CIR_FIFOCON);
4908c2ecf20Sopenharmony_ci	nvt_cir_reg_write(nvt, val | CIR_FIFOCON_RXFIFOCLR, CIR_FIFOCON);
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci/* clear out the hardware's cir wake rx fifo */
4948c2ecf20Sopenharmony_cistatic void nvt_clear_cir_wake_fifo(struct nvt_dev *nvt)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	u8 val, config;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	config = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	/* clearing wake fifo works in learning mode only */
5018c2ecf20Sopenharmony_ci	nvt_cir_wake_reg_write(nvt, config & ~CIR_WAKE_IRCON_MODE0,
5028c2ecf20Sopenharmony_ci			       CIR_WAKE_IRCON);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	val = nvt_cir_wake_reg_read(nvt, CIR_WAKE_FIFOCON);
5058c2ecf20Sopenharmony_ci	nvt_cir_wake_reg_write(nvt, val | CIR_WAKE_FIFOCON_RXFIFOCLR,
5068c2ecf20Sopenharmony_ci			       CIR_WAKE_FIFOCON);
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci	nvt_cir_wake_reg_write(nvt, config, CIR_WAKE_IRCON);
5098c2ecf20Sopenharmony_ci}
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci/* clear out the hardware's cir tx fifo */
5128c2ecf20Sopenharmony_cistatic void nvt_clear_tx_fifo(struct nvt_dev *nvt)
5138c2ecf20Sopenharmony_ci{
5148c2ecf20Sopenharmony_ci	u8 val;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	val = nvt_cir_reg_read(nvt, CIR_FIFOCON);
5178c2ecf20Sopenharmony_ci	nvt_cir_reg_write(nvt, val | CIR_FIFOCON_TXFIFOCLR, CIR_FIFOCON);
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci/* enable RX Trigger Level Reach and Packet End interrupts */
5218c2ecf20Sopenharmony_cistatic void nvt_set_cir_iren(struct nvt_dev *nvt)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	u8 iren;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	iren = CIR_IREN_RTR | CIR_IREN_PE | CIR_IREN_RFO;
5268c2ecf20Sopenharmony_ci	nvt_cir_reg_write(nvt, iren, CIR_IREN);
5278c2ecf20Sopenharmony_ci}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_cistatic void nvt_cir_regs_init(struct nvt_dev *nvt)
5308c2ecf20Sopenharmony_ci{
5318c2ecf20Sopenharmony_ci	nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/* set sample limit count (PE interrupt raised when reached) */
5348c2ecf20Sopenharmony_ci	nvt_cir_reg_write(nvt, CIR_RX_LIMIT_COUNT >> 8, CIR_SLCH);
5358c2ecf20Sopenharmony_ci	nvt_cir_reg_write(nvt, CIR_RX_LIMIT_COUNT & 0xff, CIR_SLCL);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	/* set fifo irq trigger levels */
5388c2ecf20Sopenharmony_ci	nvt_cir_reg_write(nvt, CIR_FIFOCON_TX_TRIGGER_LEV |
5398c2ecf20Sopenharmony_ci			  CIR_FIFOCON_RX_TRIGGER_LEV, CIR_FIFOCON);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	/* clear hardware rx and tx fifos */
5428c2ecf20Sopenharmony_ci	nvt_clear_cir_fifo(nvt);
5438c2ecf20Sopenharmony_ci	nvt_clear_tx_fifo(nvt);
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	nvt_disable_logical_dev(nvt, LOGICAL_DEV_CIR);
5468c2ecf20Sopenharmony_ci}
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_cistatic void nvt_cir_wake_regs_init(struct nvt_dev *nvt)
5498c2ecf20Sopenharmony_ci{
5508c2ecf20Sopenharmony_ci	nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	/*
5538c2ecf20Sopenharmony_ci	 * Disable RX, set specific carrier on = low, off = high,
5548c2ecf20Sopenharmony_ci	 * and sample period (currently 50us)
5558c2ecf20Sopenharmony_ci	 */
5568c2ecf20Sopenharmony_ci	nvt_cir_wake_reg_write(nvt, CIR_WAKE_IRCON_MODE0 |
5578c2ecf20Sopenharmony_ci			       CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV |
5588c2ecf20Sopenharmony_ci			       CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL,
5598c2ecf20Sopenharmony_ci			       CIR_WAKE_IRCON);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	/* clear any and all stray interrupts */
5628c2ecf20Sopenharmony_ci	nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS);
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_cistatic void nvt_enable_wake(struct nvt_dev *nvt)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	unsigned long flags;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci	nvt_efm_enable(nvt);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	nvt_select_logical_dev(nvt, LOGICAL_DEV_ACPI);
5728c2ecf20Sopenharmony_ci	nvt_set_reg_bit(nvt, CIR_WAKE_ENABLE_BIT, CR_ACPI_CIR_WAKE);
5738c2ecf20Sopenharmony_ci	nvt_set_reg_bit(nvt, PME_INTR_CIR_PASS_BIT, CR_ACPI_IRQ_EVENTS2);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	nvt_select_logical_dev(nvt, LOGICAL_DEV_CIR_WAKE);
5768c2ecf20Sopenharmony_ci	nvt_cr_write(nvt, LOGICAL_DEV_ENABLE, CR_LOGICAL_DEV_EN);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	nvt_efm_disable(nvt);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nvt->lock, flags);
5818c2ecf20Sopenharmony_ci
5828c2ecf20Sopenharmony_ci	nvt_cir_wake_reg_write(nvt, CIR_WAKE_IRCON_MODE0 | CIR_WAKE_IRCON_RXEN |
5838c2ecf20Sopenharmony_ci			       CIR_WAKE_IRCON_R | CIR_WAKE_IRCON_RXINV |
5848c2ecf20Sopenharmony_ci			       CIR_WAKE_IRCON_SAMPLE_PERIOD_SEL,
5858c2ecf20Sopenharmony_ci			       CIR_WAKE_IRCON);
5868c2ecf20Sopenharmony_ci	nvt_cir_wake_reg_write(nvt, 0xff, CIR_WAKE_IRSTS);
5878c2ecf20Sopenharmony_ci	nvt_cir_wake_reg_write(nvt, 0, CIR_WAKE_IREN);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nvt->lock, flags);
5908c2ecf20Sopenharmony_ci}
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci#if 0 /* Currently unused */
5938c2ecf20Sopenharmony_ci/* rx carrier detect only works in learning mode, must be called w/lock */
5948c2ecf20Sopenharmony_cistatic u32 nvt_rx_carrier_detect(struct nvt_dev *nvt)
5958c2ecf20Sopenharmony_ci{
5968c2ecf20Sopenharmony_ci	u32 count, carrier, duration = 0;
5978c2ecf20Sopenharmony_ci	int i;
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	count = nvt_cir_reg_read(nvt, CIR_FCCL) |
6008c2ecf20Sopenharmony_ci		nvt_cir_reg_read(nvt, CIR_FCCH) << 8;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	for (i = 0; i < nvt->pkts; i++) {
6038c2ecf20Sopenharmony_ci		if (nvt->buf[i] & BUF_PULSE_BIT)
6048c2ecf20Sopenharmony_ci			duration += nvt->buf[i] & BUF_LEN_MASK;
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	duration *= SAMPLE_PERIOD;
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci	if (!count || !duration) {
6108c2ecf20Sopenharmony_ci		dev_notice(nvt_get_dev(nvt),
6118c2ecf20Sopenharmony_ci			   "Unable to determine carrier! (c:%u, d:%u)",
6128c2ecf20Sopenharmony_ci			   count, duration);
6138c2ecf20Sopenharmony_ci		return 0;
6148c2ecf20Sopenharmony_ci	}
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci	carrier = MS_TO_NS(count) / duration;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	if ((carrier > MAX_CARRIER) || (carrier < MIN_CARRIER))
6198c2ecf20Sopenharmony_ci		nvt_dbg("WTF? Carrier frequency out of range!");
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci	nvt_dbg("Carrier frequency: %u (count %u, duration %u)",
6228c2ecf20Sopenharmony_ci		carrier, count, duration);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	return carrier;
6258c2ecf20Sopenharmony_ci}
6268c2ecf20Sopenharmony_ci#endif
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic int nvt_ir_raw_set_wakeup_filter(struct rc_dev *dev,
6298c2ecf20Sopenharmony_ci					struct rc_scancode_filter *sc_filter)
6308c2ecf20Sopenharmony_ci{
6318c2ecf20Sopenharmony_ci	u8 buf_val;
6328c2ecf20Sopenharmony_ci	int i, ret, count;
6338c2ecf20Sopenharmony_ci	unsigned int val;
6348c2ecf20Sopenharmony_ci	struct ir_raw_event *raw;
6358c2ecf20Sopenharmony_ci	u8 wake_buf[WAKEUP_MAX_SIZE];
6368c2ecf20Sopenharmony_ci	bool complete;
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci	/* Require mask to be set */
6398c2ecf20Sopenharmony_ci	if (!sc_filter->mask)
6408c2ecf20Sopenharmony_ci		return 0;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	raw = kmalloc_array(WAKEUP_MAX_SIZE, sizeof(*raw), GFP_KERNEL);
6438c2ecf20Sopenharmony_ci	if (!raw)
6448c2ecf20Sopenharmony_ci		return -ENOMEM;
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci	ret = ir_raw_encode_scancode(dev->wakeup_protocol, sc_filter->data,
6478c2ecf20Sopenharmony_ci				     raw, WAKEUP_MAX_SIZE);
6488c2ecf20Sopenharmony_ci	complete = (ret != -ENOBUFS);
6498c2ecf20Sopenharmony_ci	if (!complete)
6508c2ecf20Sopenharmony_ci		ret = WAKEUP_MAX_SIZE;
6518c2ecf20Sopenharmony_ci	else if (ret < 0)
6528c2ecf20Sopenharmony_ci		goto out_raw;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	/* Inspect the ir samples */
6558c2ecf20Sopenharmony_ci	for (i = 0, count = 0; i < ret && count < WAKEUP_MAX_SIZE; ++i) {
6568c2ecf20Sopenharmony_ci		val = raw[i].duration / SAMPLE_PERIOD;
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci		/* Split too large values into several smaller ones */
6598c2ecf20Sopenharmony_ci		while (val > 0 && count < WAKEUP_MAX_SIZE) {
6608c2ecf20Sopenharmony_ci			/* Skip last value for better comparison tolerance */
6618c2ecf20Sopenharmony_ci			if (complete && i == ret - 1 && val < BUF_LEN_MASK)
6628c2ecf20Sopenharmony_ci				break;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci			/* Clamp values to BUF_LEN_MASK at most */
6658c2ecf20Sopenharmony_ci			buf_val = (val > BUF_LEN_MASK) ? BUF_LEN_MASK : val;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci			wake_buf[count] = buf_val;
6688c2ecf20Sopenharmony_ci			val -= buf_val;
6698c2ecf20Sopenharmony_ci			if ((raw[i]).pulse)
6708c2ecf20Sopenharmony_ci				wake_buf[count] |= BUF_PULSE_BIT;
6718c2ecf20Sopenharmony_ci			count++;
6728c2ecf20Sopenharmony_ci		}
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	nvt_write_wakeup_codes(dev, wake_buf, count);
6768c2ecf20Sopenharmony_ci	ret = 0;
6778c2ecf20Sopenharmony_ciout_raw:
6788c2ecf20Sopenharmony_ci	kfree(raw);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci	return ret;
6818c2ecf20Sopenharmony_ci}
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci/* dump contents of the last rx buffer we got from the hw rx fifo */
6848c2ecf20Sopenharmony_cistatic void nvt_dump_rx_buf(struct nvt_dev *nvt)
6858c2ecf20Sopenharmony_ci{
6868c2ecf20Sopenharmony_ci	int i;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	printk(KERN_DEBUG "%s (len %d): ", __func__, nvt->pkts);
6898c2ecf20Sopenharmony_ci	for (i = 0; (i < nvt->pkts) && (i < RX_BUF_LEN); i++)
6908c2ecf20Sopenharmony_ci		printk(KERN_CONT "0x%02x ", nvt->buf[i]);
6918c2ecf20Sopenharmony_ci	printk(KERN_CONT "\n");
6928c2ecf20Sopenharmony_ci}
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci/*
6958c2ecf20Sopenharmony_ci * Process raw data in rx driver buffer, store it in raw IR event kfifo,
6968c2ecf20Sopenharmony_ci * trigger decode when appropriate.
6978c2ecf20Sopenharmony_ci *
6988c2ecf20Sopenharmony_ci * We get IR data samples one byte at a time. If the msb is set, its a pulse,
6998c2ecf20Sopenharmony_ci * otherwise its a space. The lower 7 bits are the count of SAMPLE_PERIOD
7008c2ecf20Sopenharmony_ci * (default 50us) intervals for that pulse/space. A discrete signal is
7018c2ecf20Sopenharmony_ci * followed by a series of 0x7f packets, then either 0x7<something> or 0x80
7028c2ecf20Sopenharmony_ci * to signal more IR coming (repeats) or end of IR, respectively. We store
7038c2ecf20Sopenharmony_ci * sample data in the raw event kfifo until we see 0x7<something> (except f)
7048c2ecf20Sopenharmony_ci * or 0x80, at which time, we trigger a decode operation.
7058c2ecf20Sopenharmony_ci */
7068c2ecf20Sopenharmony_cistatic void nvt_process_rx_ir_data(struct nvt_dev *nvt)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	struct ir_raw_event rawir = {};
7098c2ecf20Sopenharmony_ci	u8 sample;
7108c2ecf20Sopenharmony_ci	int i;
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	nvt_dbg_verbose("%s firing", __func__);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	if (debug)
7158c2ecf20Sopenharmony_ci		nvt_dump_rx_buf(nvt);
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci	nvt_dbg_verbose("Processing buffer of len %d", nvt->pkts);
7188c2ecf20Sopenharmony_ci
7198c2ecf20Sopenharmony_ci	for (i = 0; i < nvt->pkts; i++) {
7208c2ecf20Sopenharmony_ci		sample = nvt->buf[i];
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci		rawir.pulse = ((sample & BUF_PULSE_BIT) != 0);
7238c2ecf20Sopenharmony_ci		rawir.duration = (sample & BUF_LEN_MASK) * SAMPLE_PERIOD;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci		nvt_dbg("Storing %s with duration %d",
7268c2ecf20Sopenharmony_ci			rawir.pulse ? "pulse" : "space", rawir.duration);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci		ir_raw_event_store_with_filter(nvt->rdev, &rawir);
7298c2ecf20Sopenharmony_ci	}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	nvt->pkts = 0;
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci	nvt_dbg("Calling ir_raw_event_handle\n");
7348c2ecf20Sopenharmony_ci	ir_raw_event_handle(nvt->rdev);
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	nvt_dbg_verbose("%s done", __func__);
7378c2ecf20Sopenharmony_ci}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_cistatic void nvt_handle_rx_fifo_overrun(struct nvt_dev *nvt)
7408c2ecf20Sopenharmony_ci{
7418c2ecf20Sopenharmony_ci	dev_warn(nvt_get_dev(nvt), "RX FIFO overrun detected, flushing data!");
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	nvt->pkts = 0;
7448c2ecf20Sopenharmony_ci	nvt_clear_cir_fifo(nvt);
7458c2ecf20Sopenharmony_ci	ir_raw_event_reset(nvt->rdev);
7468c2ecf20Sopenharmony_ci}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci/* copy data from hardware rx fifo into driver buffer */
7498c2ecf20Sopenharmony_cistatic void nvt_get_rx_ir_data(struct nvt_dev *nvt)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	u8 fifocount;
7528c2ecf20Sopenharmony_ci	int i;
7538c2ecf20Sopenharmony_ci
7548c2ecf20Sopenharmony_ci	/* Get count of how many bytes to read from RX FIFO */
7558c2ecf20Sopenharmony_ci	fifocount = nvt_cir_reg_read(nvt, CIR_RXFCONT);
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	nvt_dbg("attempting to fetch %u bytes from hw rx fifo", fifocount);
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	/* Read fifocount bytes from CIR Sample RX FIFO register */
7608c2ecf20Sopenharmony_ci	for (i = 0; i < fifocount; i++)
7618c2ecf20Sopenharmony_ci		nvt->buf[i] = nvt_cir_reg_read(nvt, CIR_SRXFIFO);
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	nvt->pkts = fifocount;
7648c2ecf20Sopenharmony_ci	nvt_dbg("%s: pkts now %d", __func__, nvt->pkts);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	nvt_process_rx_ir_data(nvt);
7678c2ecf20Sopenharmony_ci}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cistatic void nvt_cir_log_irqs(u8 status, u8 iren)
7708c2ecf20Sopenharmony_ci{
7718c2ecf20Sopenharmony_ci	nvt_dbg("IRQ 0x%02x (IREN 0x%02x) :%s%s%s%s%s%s%s%s%s",
7728c2ecf20Sopenharmony_ci		status, iren,
7738c2ecf20Sopenharmony_ci		status & CIR_IRSTS_RDR	? " RDR"	: "",
7748c2ecf20Sopenharmony_ci		status & CIR_IRSTS_RTR	? " RTR"	: "",
7758c2ecf20Sopenharmony_ci		status & CIR_IRSTS_PE	? " PE"		: "",
7768c2ecf20Sopenharmony_ci		status & CIR_IRSTS_RFO	? " RFO"	: "",
7778c2ecf20Sopenharmony_ci		status & CIR_IRSTS_TE	? " TE"		: "",
7788c2ecf20Sopenharmony_ci		status & CIR_IRSTS_TTR	? " TTR"	: "",
7798c2ecf20Sopenharmony_ci		status & CIR_IRSTS_TFU	? " TFU"	: "",
7808c2ecf20Sopenharmony_ci		status & CIR_IRSTS_GH	? " GH"		: "",
7818c2ecf20Sopenharmony_ci		status & ~(CIR_IRSTS_RDR | CIR_IRSTS_RTR | CIR_IRSTS_PE |
7828c2ecf20Sopenharmony_ci			   CIR_IRSTS_RFO | CIR_IRSTS_TE | CIR_IRSTS_TTR |
7838c2ecf20Sopenharmony_ci			   CIR_IRSTS_TFU | CIR_IRSTS_GH) ? " ?" : "");
7848c2ecf20Sopenharmony_ci}
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci/* interrupt service routine for incoming and outgoing CIR data */
7878c2ecf20Sopenharmony_cistatic irqreturn_t nvt_cir_isr(int irq, void *data)
7888c2ecf20Sopenharmony_ci{
7898c2ecf20Sopenharmony_ci	struct nvt_dev *nvt = data;
7908c2ecf20Sopenharmony_ci	u8 status, iren;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	nvt_dbg_verbose("%s firing", __func__);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	spin_lock(&nvt->lock);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	/*
7978c2ecf20Sopenharmony_ci	 * Get IR Status register contents. Write 1 to ack/clear
7988c2ecf20Sopenharmony_ci	 *
7998c2ecf20Sopenharmony_ci	 * bit: reg name      - description
8008c2ecf20Sopenharmony_ci	 *   7: CIR_IRSTS_RDR - RX Data Ready
8018c2ecf20Sopenharmony_ci	 *   6: CIR_IRSTS_RTR - RX FIFO Trigger Level Reach
8028c2ecf20Sopenharmony_ci	 *   5: CIR_IRSTS_PE  - Packet End
8038c2ecf20Sopenharmony_ci	 *   4: CIR_IRSTS_RFO - RX FIFO Overrun (RDR will also be set)
8048c2ecf20Sopenharmony_ci	 *   3: CIR_IRSTS_TE  - TX FIFO Empty
8058c2ecf20Sopenharmony_ci	 *   2: CIR_IRSTS_TTR - TX FIFO Trigger Level Reach
8068c2ecf20Sopenharmony_ci	 *   1: CIR_IRSTS_TFU - TX FIFO Underrun
8078c2ecf20Sopenharmony_ci	 *   0: CIR_IRSTS_GH  - Min Length Detected
8088c2ecf20Sopenharmony_ci	 */
8098c2ecf20Sopenharmony_ci	status = nvt_cir_reg_read(nvt, CIR_IRSTS);
8108c2ecf20Sopenharmony_ci	iren = nvt_cir_reg_read(nvt, CIR_IREN);
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	/* At least NCT6779D creates a spurious interrupt when the
8138c2ecf20Sopenharmony_ci	 * logical device is being disabled.
8148c2ecf20Sopenharmony_ci	 */
8158c2ecf20Sopenharmony_ci	if (status == 0xff && iren == 0xff) {
8168c2ecf20Sopenharmony_ci		spin_unlock(&nvt->lock);
8178c2ecf20Sopenharmony_ci		nvt_dbg_verbose("Spurious interrupt detected");
8188c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
8198c2ecf20Sopenharmony_ci	}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	/* IRQ may be shared with CIR WAKE, therefore check for each
8228c2ecf20Sopenharmony_ci	 * status bit whether the related interrupt source is enabled
8238c2ecf20Sopenharmony_ci	 */
8248c2ecf20Sopenharmony_ci	if (!(status & iren)) {
8258c2ecf20Sopenharmony_ci		spin_unlock(&nvt->lock);
8268c2ecf20Sopenharmony_ci		nvt_dbg_verbose("%s exiting, IRSTS 0x0", __func__);
8278c2ecf20Sopenharmony_ci		return IRQ_NONE;
8288c2ecf20Sopenharmony_ci	}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	/* ack/clear all irq flags we've got */
8318c2ecf20Sopenharmony_ci	nvt_cir_reg_write(nvt, status, CIR_IRSTS);
8328c2ecf20Sopenharmony_ci	nvt_cir_reg_write(nvt, 0, CIR_IRSTS);
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	nvt_cir_log_irqs(status, iren);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	if (status & CIR_IRSTS_RFO)
8378c2ecf20Sopenharmony_ci		nvt_handle_rx_fifo_overrun(nvt);
8388c2ecf20Sopenharmony_ci	else if (status & (CIR_IRSTS_RTR | CIR_IRSTS_PE))
8398c2ecf20Sopenharmony_ci		nvt_get_rx_ir_data(nvt);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	spin_unlock(&nvt->lock);
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	nvt_dbg_verbose("%s done", __func__);
8448c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
8458c2ecf20Sopenharmony_ci}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_cistatic void nvt_enable_cir(struct nvt_dev *nvt)
8488c2ecf20Sopenharmony_ci{
8498c2ecf20Sopenharmony_ci	unsigned long flags;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	/* enable the CIR logical device */
8528c2ecf20Sopenharmony_ci	nvt_enable_logical_dev(nvt, LOGICAL_DEV_CIR);
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nvt->lock, flags);
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	/*
8578c2ecf20Sopenharmony_ci	 * Enable TX and RX, specify carrier on = low, off = high, and set
8588c2ecf20Sopenharmony_ci	 * sample period (currently 50us)
8598c2ecf20Sopenharmony_ci	 */
8608c2ecf20Sopenharmony_ci	nvt_cir_reg_write(nvt, CIR_IRCON_TXEN | CIR_IRCON_RXEN |
8618c2ecf20Sopenharmony_ci			  CIR_IRCON_RXINV | CIR_IRCON_SAMPLE_PERIOD_SEL,
8628c2ecf20Sopenharmony_ci			  CIR_IRCON);
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	/* clear all pending interrupts */
8658c2ecf20Sopenharmony_ci	nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	/* enable interrupts */
8688c2ecf20Sopenharmony_ci	nvt_set_cir_iren(nvt);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nvt->lock, flags);
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_cistatic void nvt_disable_cir(struct nvt_dev *nvt)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	unsigned long flags;
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	spin_lock_irqsave(&nvt->lock, flags);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	/* disable CIR interrupts */
8808c2ecf20Sopenharmony_ci	nvt_cir_reg_write(nvt, 0, CIR_IREN);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	/* clear any and all pending interrupts */
8838c2ecf20Sopenharmony_ci	nvt_cir_reg_write(nvt, 0xff, CIR_IRSTS);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	/* clear all function enable flags */
8868c2ecf20Sopenharmony_ci	nvt_cir_reg_write(nvt, 0, CIR_IRCON);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	/* clear hardware rx and tx fifos */
8898c2ecf20Sopenharmony_ci	nvt_clear_cir_fifo(nvt);
8908c2ecf20Sopenharmony_ci	nvt_clear_tx_fifo(nvt);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&nvt->lock, flags);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	/* disable the CIR logical device */
8958c2ecf20Sopenharmony_ci	nvt_disable_logical_dev(nvt, LOGICAL_DEV_CIR);
8968c2ecf20Sopenharmony_ci}
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_cistatic int nvt_open(struct rc_dev *dev)
8998c2ecf20Sopenharmony_ci{
9008c2ecf20Sopenharmony_ci	struct nvt_dev *nvt = dev->priv;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	nvt_enable_cir(nvt);
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	return 0;
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_cistatic void nvt_close(struct rc_dev *dev)
9088c2ecf20Sopenharmony_ci{
9098c2ecf20Sopenharmony_ci	struct nvt_dev *nvt = dev->priv;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	nvt_disable_cir(nvt);
9128c2ecf20Sopenharmony_ci}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci/* Allocate memory, probe hardware, and initialize everything */
9158c2ecf20Sopenharmony_cistatic int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
9168c2ecf20Sopenharmony_ci{
9178c2ecf20Sopenharmony_ci	struct nvt_dev *nvt;
9188c2ecf20Sopenharmony_ci	struct rc_dev *rdev;
9198c2ecf20Sopenharmony_ci	int ret;
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	nvt = devm_kzalloc(&pdev->dev, sizeof(struct nvt_dev), GFP_KERNEL);
9228c2ecf20Sopenharmony_ci	if (!nvt)
9238c2ecf20Sopenharmony_ci		return -ENOMEM;
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	/* input device for IR remote */
9268c2ecf20Sopenharmony_ci	nvt->rdev = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW);
9278c2ecf20Sopenharmony_ci	if (!nvt->rdev)
9288c2ecf20Sopenharmony_ci		return -ENOMEM;
9298c2ecf20Sopenharmony_ci	rdev = nvt->rdev;
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_ci	/* activate pnp device */
9328c2ecf20Sopenharmony_ci	ret = pnp_activate_dev(pdev);
9338c2ecf20Sopenharmony_ci	if (ret) {
9348c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Could not activate PNP device!\n");
9358c2ecf20Sopenharmony_ci		return ret;
9368c2ecf20Sopenharmony_ci	}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	/* validate pnp resources */
9398c2ecf20Sopenharmony_ci	if (!pnp_port_valid(pdev, 0) ||
9408c2ecf20Sopenharmony_ci	    pnp_port_len(pdev, 0) < CIR_IOREG_LENGTH) {
9418c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "IR PNP Port not valid!\n");
9428c2ecf20Sopenharmony_ci		return -EINVAL;
9438c2ecf20Sopenharmony_ci	}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	if (!pnp_irq_valid(pdev, 0)) {
9468c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "PNP IRQ not valid!\n");
9478c2ecf20Sopenharmony_ci		return -EINVAL;
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	if (!pnp_port_valid(pdev, 1) ||
9518c2ecf20Sopenharmony_ci	    pnp_port_len(pdev, 1) < CIR_IOREG_LENGTH) {
9528c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Wake PNP Port not valid!\n");
9538c2ecf20Sopenharmony_ci		return -EINVAL;
9548c2ecf20Sopenharmony_ci	}
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	nvt->cir_addr = pnp_port_start(pdev, 0);
9578c2ecf20Sopenharmony_ci	nvt->cir_irq  = pnp_irq(pdev, 0);
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	nvt->cir_wake_addr = pnp_port_start(pdev, 1);
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	nvt->cr_efir = CR_EFIR;
9628c2ecf20Sopenharmony_ci	nvt->cr_efdr = CR_EFDR;
9638c2ecf20Sopenharmony_ci
9648c2ecf20Sopenharmony_ci	spin_lock_init(&nvt->lock);
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	pnp_set_drvdata(pdev, nvt);
9678c2ecf20Sopenharmony_ci
9688c2ecf20Sopenharmony_ci	ret = nvt_hw_detect(nvt);
9698c2ecf20Sopenharmony_ci	if (ret)
9708c2ecf20Sopenharmony_ci		return ret;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	/* Initialize CIR & CIR Wake Logical Devices */
9738c2ecf20Sopenharmony_ci	nvt_efm_enable(nvt);
9748c2ecf20Sopenharmony_ci	nvt_cir_ldev_init(nvt);
9758c2ecf20Sopenharmony_ci	nvt_cir_wake_ldev_init(nvt);
9768c2ecf20Sopenharmony_ci	nvt_efm_disable(nvt);
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci	/*
9798c2ecf20Sopenharmony_ci	 * Initialize CIR & CIR Wake Config Registers
9808c2ecf20Sopenharmony_ci	 * and enable logical devices
9818c2ecf20Sopenharmony_ci	 */
9828c2ecf20Sopenharmony_ci	nvt_cir_regs_init(nvt);
9838c2ecf20Sopenharmony_ci	nvt_cir_wake_regs_init(nvt);
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	/* Set up the rc device */
9868c2ecf20Sopenharmony_ci	rdev->priv = nvt;
9878c2ecf20Sopenharmony_ci	rdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
9888c2ecf20Sopenharmony_ci	rdev->allowed_wakeup_protocols = RC_PROTO_BIT_ALL_IR_ENCODER;
9898c2ecf20Sopenharmony_ci	rdev->encode_wakeup = true;
9908c2ecf20Sopenharmony_ci	rdev->open = nvt_open;
9918c2ecf20Sopenharmony_ci	rdev->close = nvt_close;
9928c2ecf20Sopenharmony_ci	rdev->s_wakeup_filter = nvt_ir_raw_set_wakeup_filter;
9938c2ecf20Sopenharmony_ci	rdev->device_name = "Nuvoton w836x7hg Infrared Remote Transceiver";
9948c2ecf20Sopenharmony_ci	rdev->input_phys = "nuvoton/cir0";
9958c2ecf20Sopenharmony_ci	rdev->input_id.bustype = BUS_HOST;
9968c2ecf20Sopenharmony_ci	rdev->input_id.vendor = PCI_VENDOR_ID_WINBOND2;
9978c2ecf20Sopenharmony_ci	rdev->input_id.product = nvt->chip_major;
9988c2ecf20Sopenharmony_ci	rdev->input_id.version = nvt->chip_minor;
9998c2ecf20Sopenharmony_ci	rdev->driver_name = NVT_DRIVER_NAME;
10008c2ecf20Sopenharmony_ci	rdev->map_name = RC_MAP_RC6_MCE;
10018c2ecf20Sopenharmony_ci	rdev->timeout = MS_TO_US(100);
10028c2ecf20Sopenharmony_ci	/* rx resolution is hardwired to 50us atm, 1, 25, 100 also possible */
10038c2ecf20Sopenharmony_ci	rdev->rx_resolution = CIR_SAMPLE_PERIOD;
10048c2ecf20Sopenharmony_ci#if 0
10058c2ecf20Sopenharmony_ci	rdev->min_timeout = XYZ;
10068c2ecf20Sopenharmony_ci	rdev->max_timeout = XYZ;
10078c2ecf20Sopenharmony_ci#endif
10088c2ecf20Sopenharmony_ci	ret = devm_rc_register_device(&pdev->dev, rdev);
10098c2ecf20Sopenharmony_ci	if (ret)
10108c2ecf20Sopenharmony_ci		return ret;
10118c2ecf20Sopenharmony_ci
10128c2ecf20Sopenharmony_ci	/* now claim resources */
10138c2ecf20Sopenharmony_ci	if (!devm_request_region(&pdev->dev, nvt->cir_addr,
10148c2ecf20Sopenharmony_ci			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME))
10158c2ecf20Sopenharmony_ci		return -EBUSY;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	ret = devm_request_irq(&pdev->dev, nvt->cir_irq, nvt_cir_isr,
10188c2ecf20Sopenharmony_ci			       IRQF_SHARED, NVT_DRIVER_NAME, nvt);
10198c2ecf20Sopenharmony_ci	if (ret)
10208c2ecf20Sopenharmony_ci		return ret;
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	if (!devm_request_region(&pdev->dev, nvt->cir_wake_addr,
10238c2ecf20Sopenharmony_ci			    CIR_IOREG_LENGTH, NVT_DRIVER_NAME "-wake"))
10248c2ecf20Sopenharmony_ci		return -EBUSY;
10258c2ecf20Sopenharmony_ci
10268c2ecf20Sopenharmony_ci	ret = device_create_file(&rdev->dev, &dev_attr_wakeup_data);
10278c2ecf20Sopenharmony_ci	if (ret)
10288c2ecf20Sopenharmony_ci		return ret;
10298c2ecf20Sopenharmony_ci
10308c2ecf20Sopenharmony_ci	device_init_wakeup(&pdev->dev, true);
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci	dev_notice(&pdev->dev, "driver has been successfully loaded\n");
10338c2ecf20Sopenharmony_ci	if (debug) {
10348c2ecf20Sopenharmony_ci		cir_dump_regs(nvt);
10358c2ecf20Sopenharmony_ci		cir_wake_dump_regs(nvt);
10368c2ecf20Sopenharmony_ci	}
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	return 0;
10398c2ecf20Sopenharmony_ci}
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_cistatic void nvt_remove(struct pnp_dev *pdev)
10428c2ecf20Sopenharmony_ci{
10438c2ecf20Sopenharmony_ci	struct nvt_dev *nvt = pnp_get_drvdata(pdev);
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	device_remove_file(&nvt->rdev->dev, &dev_attr_wakeup_data);
10468c2ecf20Sopenharmony_ci
10478c2ecf20Sopenharmony_ci	nvt_disable_cir(nvt);
10488c2ecf20Sopenharmony_ci
10498c2ecf20Sopenharmony_ci	/* enable CIR Wake (for IR power-on) */
10508c2ecf20Sopenharmony_ci	nvt_enable_wake(nvt);
10518c2ecf20Sopenharmony_ci}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_cistatic int nvt_suspend(struct pnp_dev *pdev, pm_message_t state)
10548c2ecf20Sopenharmony_ci{
10558c2ecf20Sopenharmony_ci	struct nvt_dev *nvt = pnp_get_drvdata(pdev);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	nvt_dbg("%s called", __func__);
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	mutex_lock(&nvt->rdev->lock);
10608c2ecf20Sopenharmony_ci	if (nvt->rdev->users)
10618c2ecf20Sopenharmony_ci		nvt_disable_cir(nvt);
10628c2ecf20Sopenharmony_ci	mutex_unlock(&nvt->rdev->lock);
10638c2ecf20Sopenharmony_ci
10648c2ecf20Sopenharmony_ci	/* make sure wake is enabled */
10658c2ecf20Sopenharmony_ci	nvt_enable_wake(nvt);
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	return 0;
10688c2ecf20Sopenharmony_ci}
10698c2ecf20Sopenharmony_ci
10708c2ecf20Sopenharmony_cistatic int nvt_resume(struct pnp_dev *pdev)
10718c2ecf20Sopenharmony_ci{
10728c2ecf20Sopenharmony_ci	struct nvt_dev *nvt = pnp_get_drvdata(pdev);
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci	nvt_dbg("%s called", __func__);
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	nvt_cir_regs_init(nvt);
10778c2ecf20Sopenharmony_ci	nvt_cir_wake_regs_init(nvt);
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_ci	mutex_lock(&nvt->rdev->lock);
10808c2ecf20Sopenharmony_ci	if (nvt->rdev->users)
10818c2ecf20Sopenharmony_ci		nvt_enable_cir(nvt);
10828c2ecf20Sopenharmony_ci	mutex_unlock(&nvt->rdev->lock);
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	return 0;
10858c2ecf20Sopenharmony_ci}
10868c2ecf20Sopenharmony_ci
10878c2ecf20Sopenharmony_cistatic void nvt_shutdown(struct pnp_dev *pdev)
10888c2ecf20Sopenharmony_ci{
10898c2ecf20Sopenharmony_ci	struct nvt_dev *nvt = pnp_get_drvdata(pdev);
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci	nvt_enable_wake(nvt);
10928c2ecf20Sopenharmony_ci}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_cistatic const struct pnp_device_id nvt_ids[] = {
10958c2ecf20Sopenharmony_ci	{ "WEC0530", 0 },   /* CIR */
10968c2ecf20Sopenharmony_ci	{ "NTN0530", 0 },   /* CIR for new chip's pnp id*/
10978c2ecf20Sopenharmony_ci	{ "", 0 },
10988c2ecf20Sopenharmony_ci};
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_cistatic struct pnp_driver nvt_driver = {
11018c2ecf20Sopenharmony_ci	.name		= NVT_DRIVER_NAME,
11028c2ecf20Sopenharmony_ci	.id_table	= nvt_ids,
11038c2ecf20Sopenharmony_ci	.flags		= PNP_DRIVER_RES_DO_NOT_CHANGE,
11048c2ecf20Sopenharmony_ci	.probe		= nvt_probe,
11058c2ecf20Sopenharmony_ci	.remove		= nvt_remove,
11068c2ecf20Sopenharmony_ci	.suspend	= nvt_suspend,
11078c2ecf20Sopenharmony_ci	.resume		= nvt_resume,
11088c2ecf20Sopenharmony_ci	.shutdown	= nvt_shutdown,
11098c2ecf20Sopenharmony_ci};
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_cimodule_param(debug, int, S_IRUGO | S_IWUSR);
11128c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "Enable debugging output");
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pnp, nvt_ids);
11158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Nuvoton W83667HG-A & W83677HG-I CIR driver");
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jarod Wilson <jarod@redhat.com>");
11188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_cimodule_pnp_driver(nvt_driver);
1121