18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2014 Linaro Ltd.
48c2ecf20Sopenharmony_ci * Copyright (c) 2014 Hisilicon Limited.
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/clk.h>
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
108c2ecf20Sopenharmony_ci#include <linux/mfd/syscon.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/of_device.h>
138c2ecf20Sopenharmony_ci#include <linux/regmap.h>
148c2ecf20Sopenharmony_ci#include <media/rc-core.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define IR_ENABLE		0x00
178c2ecf20Sopenharmony_ci#define IR_CONFIG		0x04
188c2ecf20Sopenharmony_ci#define CNT_LEADS		0x08
198c2ecf20Sopenharmony_ci#define CNT_LEADE		0x0c
208c2ecf20Sopenharmony_ci#define CNT_SLEADE		0x10
218c2ecf20Sopenharmony_ci#define CNT0_B			0x14
228c2ecf20Sopenharmony_ci#define CNT1_B			0x18
238c2ecf20Sopenharmony_ci#define IR_BUSY			0x1c
248c2ecf20Sopenharmony_ci#define IR_DATAH		0x20
258c2ecf20Sopenharmony_ci#define IR_DATAL		0x24
268c2ecf20Sopenharmony_ci#define IR_INTM			0x28
278c2ecf20Sopenharmony_ci#define IR_INTS			0x2c
288c2ecf20Sopenharmony_ci#define IR_INTC			0x30
298c2ecf20Sopenharmony_ci#define IR_START		0x34
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/* interrupt mask */
328c2ecf20Sopenharmony_ci#define INTMS_SYMBRCV		(BIT(24) | BIT(8))
338c2ecf20Sopenharmony_ci#define INTMS_TIMEOUT		(BIT(25) | BIT(9))
348c2ecf20Sopenharmony_ci#define INTMS_OVERFLOW		(BIT(26) | BIT(10))
358c2ecf20Sopenharmony_ci#define INT_CLR_OVERFLOW	BIT(18)
368c2ecf20Sopenharmony_ci#define INT_CLR_TIMEOUT		BIT(17)
378c2ecf20Sopenharmony_ci#define INT_CLR_RCV		BIT(16)
388c2ecf20Sopenharmony_ci#define INT_CLR_RCVTIMEOUT	(BIT(16) | BIT(17))
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define IR_CLK_ENABLE		BIT(4)
418c2ecf20Sopenharmony_ci#define IR_CLK_RESET		BIT(5)
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* IR_ENABLE register bits */
448c2ecf20Sopenharmony_ci#define IR_ENABLE_EN		BIT(0)
458c2ecf20Sopenharmony_ci#define IR_ENABLE_EN_EXTRA	BIT(8)
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define IR_CFG_WIDTH_MASK	0xffff
488c2ecf20Sopenharmony_ci#define IR_CFG_WIDTH_SHIFT	16
498c2ecf20Sopenharmony_ci#define IR_CFG_FORMAT_MASK	0x3
508c2ecf20Sopenharmony_ci#define IR_CFG_FORMAT_SHIFT	14
518c2ecf20Sopenharmony_ci#define IR_CFG_INT_LEVEL_MASK	0x3f
528c2ecf20Sopenharmony_ci#define IR_CFG_INT_LEVEL_SHIFT	8
538c2ecf20Sopenharmony_ci/* only support raw mode */
548c2ecf20Sopenharmony_ci#define IR_CFG_MODE_RAW		BIT(7)
558c2ecf20Sopenharmony_ci#define IR_CFG_FREQ_MASK	0x7f
568c2ecf20Sopenharmony_ci#define IR_CFG_FREQ_SHIFT	0
578c2ecf20Sopenharmony_ci#define IR_CFG_INT_THRESHOLD	1
588c2ecf20Sopenharmony_ci/* symbol start from low to high, symbol stream end at high*/
598c2ecf20Sopenharmony_ci#define IR_CFG_SYMBOL_FMT	0
608c2ecf20Sopenharmony_ci#define IR_CFG_SYMBOL_MAXWIDTH	0x3e80
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define IR_HIX5HD2_NAME		"hix5hd2-ir"
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/* Need to set extra bit for enabling IR */
658c2ecf20Sopenharmony_ci#define HIX5HD2_FLAG_EXTRA_ENABLE	BIT(0)
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistruct hix5hd2_soc_data {
688c2ecf20Sopenharmony_ci	u32 clk_reg;
698c2ecf20Sopenharmony_ci	u32 flags;
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic const struct hix5hd2_soc_data hix5hd2_data = {
738c2ecf20Sopenharmony_ci	.clk_reg = 0x48,
748c2ecf20Sopenharmony_ci};
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic const struct hix5hd2_soc_data hi3796cv300_data = {
778c2ecf20Sopenharmony_ci	.clk_reg = 0x60,
788c2ecf20Sopenharmony_ci	.flags = HIX5HD2_FLAG_EXTRA_ENABLE,
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistruct hix5hd2_ir_priv {
828c2ecf20Sopenharmony_ci	int			irq;
838c2ecf20Sopenharmony_ci	void __iomem		*base;
848c2ecf20Sopenharmony_ci	struct device		*dev;
858c2ecf20Sopenharmony_ci	struct rc_dev		*rdev;
868c2ecf20Sopenharmony_ci	struct regmap		*regmap;
878c2ecf20Sopenharmony_ci	struct clk		*clock;
888c2ecf20Sopenharmony_ci	unsigned long		rate;
898c2ecf20Sopenharmony_ci	const struct hix5hd2_soc_data *socdata;
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic int hix5hd2_ir_clk_enable(struct hix5hd2_ir_priv *dev, bool on)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	u32 clk_reg = dev->socdata->clk_reg;
958c2ecf20Sopenharmony_ci	u32 val;
968c2ecf20Sopenharmony_ci	int ret = 0;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (dev->regmap) {
998c2ecf20Sopenharmony_ci		regmap_read(dev->regmap, clk_reg, &val);
1008c2ecf20Sopenharmony_ci		if (on) {
1018c2ecf20Sopenharmony_ci			val &= ~IR_CLK_RESET;
1028c2ecf20Sopenharmony_ci			val |= IR_CLK_ENABLE;
1038c2ecf20Sopenharmony_ci		} else {
1048c2ecf20Sopenharmony_ci			val &= ~IR_CLK_ENABLE;
1058c2ecf20Sopenharmony_ci			val |= IR_CLK_RESET;
1068c2ecf20Sopenharmony_ci		}
1078c2ecf20Sopenharmony_ci		regmap_write(dev->regmap, clk_reg, val);
1088c2ecf20Sopenharmony_ci	} else {
1098c2ecf20Sopenharmony_ci		if (on)
1108c2ecf20Sopenharmony_ci			ret = clk_prepare_enable(dev->clock);
1118c2ecf20Sopenharmony_ci		else
1128c2ecf20Sopenharmony_ci			clk_disable_unprepare(dev->clock);
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci	return ret;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic inline void hix5hd2_ir_enable(struct hix5hd2_ir_priv *priv)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	u32 val = IR_ENABLE_EN;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (priv->socdata->flags & HIX5HD2_FLAG_EXTRA_ENABLE)
1228c2ecf20Sopenharmony_ci		val |= IR_ENABLE_EN_EXTRA;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	writel_relaxed(val, priv->base + IR_ENABLE);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cistatic int hix5hd2_ir_config(struct hix5hd2_ir_priv *priv)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	int timeout = 10000;
1308c2ecf20Sopenharmony_ci	u32 val, rate;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	hix5hd2_ir_enable(priv);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	while (readl_relaxed(priv->base + IR_BUSY)) {
1358c2ecf20Sopenharmony_ci		if (timeout--) {
1368c2ecf20Sopenharmony_ci			udelay(1);
1378c2ecf20Sopenharmony_ci		} else {
1388c2ecf20Sopenharmony_ci			dev_err(priv->dev, "IR_BUSY timeout\n");
1398c2ecf20Sopenharmony_ci			return -ETIMEDOUT;
1408c2ecf20Sopenharmony_ci		}
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* Now only support raw mode, with symbol start from low to high */
1448c2ecf20Sopenharmony_ci	rate = DIV_ROUND_CLOSEST(priv->rate, 1000000);
1458c2ecf20Sopenharmony_ci	val = IR_CFG_SYMBOL_MAXWIDTH & IR_CFG_WIDTH_MASK << IR_CFG_WIDTH_SHIFT;
1468c2ecf20Sopenharmony_ci	val |= IR_CFG_SYMBOL_FMT & IR_CFG_FORMAT_MASK << IR_CFG_FORMAT_SHIFT;
1478c2ecf20Sopenharmony_ci	val |= (IR_CFG_INT_THRESHOLD - 1) & IR_CFG_INT_LEVEL_MASK
1488c2ecf20Sopenharmony_ci	       << IR_CFG_INT_LEVEL_SHIFT;
1498c2ecf20Sopenharmony_ci	val |= IR_CFG_MODE_RAW;
1508c2ecf20Sopenharmony_ci	val |= (rate - 1) & IR_CFG_FREQ_MASK << IR_CFG_FREQ_SHIFT;
1518c2ecf20Sopenharmony_ci	writel_relaxed(val, priv->base + IR_CONFIG);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	writel_relaxed(0x00, priv->base + IR_INTM);
1548c2ecf20Sopenharmony_ci	/* write arbitrary value to start  */
1558c2ecf20Sopenharmony_ci	writel_relaxed(0x01, priv->base + IR_START);
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int hix5hd2_ir_open(struct rc_dev *rdev)
1608c2ecf20Sopenharmony_ci{
1618c2ecf20Sopenharmony_ci	struct hix5hd2_ir_priv *priv = rdev->priv;
1628c2ecf20Sopenharmony_ci	int ret;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	ret = hix5hd2_ir_clk_enable(priv, true);
1658c2ecf20Sopenharmony_ci	if (ret)
1668c2ecf20Sopenharmony_ci		return ret;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	ret = hix5hd2_ir_config(priv);
1698c2ecf20Sopenharmony_ci	if (ret) {
1708c2ecf20Sopenharmony_ci		hix5hd2_ir_clk_enable(priv, false);
1718c2ecf20Sopenharmony_ci		return ret;
1728c2ecf20Sopenharmony_ci	}
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void hix5hd2_ir_close(struct rc_dev *rdev)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct hix5hd2_ir_priv *priv = rdev->priv;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	hix5hd2_ir_clk_enable(priv, false);
1818c2ecf20Sopenharmony_ci}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic irqreturn_t hix5hd2_ir_rx_interrupt(int irq, void *data)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	u32 symb_num, symb_val, symb_time;
1868c2ecf20Sopenharmony_ci	u32 data_l, data_h;
1878c2ecf20Sopenharmony_ci	u32 irq_sr, i;
1888c2ecf20Sopenharmony_ci	struct hix5hd2_ir_priv *priv = data;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	irq_sr = readl_relaxed(priv->base + IR_INTS);
1918c2ecf20Sopenharmony_ci	if (irq_sr & INTMS_OVERFLOW) {
1928c2ecf20Sopenharmony_ci		/*
1938c2ecf20Sopenharmony_ci		 * we must read IR_DATAL first, then we can clean up
1948c2ecf20Sopenharmony_ci		 * IR_INTS availably since logic would not clear
1958c2ecf20Sopenharmony_ci		 * fifo when overflow, drv do the job
1968c2ecf20Sopenharmony_ci		 */
1978c2ecf20Sopenharmony_ci		ir_raw_event_reset(priv->rdev);
1988c2ecf20Sopenharmony_ci		symb_num = readl_relaxed(priv->base + IR_DATAH);
1998c2ecf20Sopenharmony_ci		for (i = 0; i < symb_num; i++)
2008c2ecf20Sopenharmony_ci			readl_relaxed(priv->base + IR_DATAL);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci		writel_relaxed(INT_CLR_OVERFLOW, priv->base + IR_INTC);
2038c2ecf20Sopenharmony_ci		dev_info(priv->dev, "overflow, level=%d\n",
2048c2ecf20Sopenharmony_ci			 IR_CFG_INT_THRESHOLD);
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if ((irq_sr & INTMS_SYMBRCV) || (irq_sr & INTMS_TIMEOUT)) {
2088c2ecf20Sopenharmony_ci		struct ir_raw_event ev = {};
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		symb_num = readl_relaxed(priv->base + IR_DATAH);
2118c2ecf20Sopenharmony_ci		for (i = 0; i < symb_num; i++) {
2128c2ecf20Sopenharmony_ci			symb_val = readl_relaxed(priv->base + IR_DATAL);
2138c2ecf20Sopenharmony_ci			data_l = ((symb_val & 0xffff) * 10);
2148c2ecf20Sopenharmony_ci			data_h =  ((symb_val >> 16) & 0xffff) * 10;
2158c2ecf20Sopenharmony_ci			symb_time = (data_l + data_h) / 10;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci			ev.duration = data_l;
2188c2ecf20Sopenharmony_ci			ev.pulse = true;
2198c2ecf20Sopenharmony_ci			ir_raw_event_store(priv->rdev, &ev);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci			if (symb_time < IR_CFG_SYMBOL_MAXWIDTH) {
2228c2ecf20Sopenharmony_ci				ev.duration = data_h;
2238c2ecf20Sopenharmony_ci				ev.pulse = false;
2248c2ecf20Sopenharmony_ci				ir_raw_event_store(priv->rdev, &ev);
2258c2ecf20Sopenharmony_ci			} else {
2268c2ecf20Sopenharmony_ci				ir_raw_event_set_idle(priv->rdev, true);
2278c2ecf20Sopenharmony_ci			}
2288c2ecf20Sopenharmony_ci		}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci		if (irq_sr & INTMS_SYMBRCV)
2318c2ecf20Sopenharmony_ci			writel_relaxed(INT_CLR_RCV, priv->base + IR_INTC);
2328c2ecf20Sopenharmony_ci		if (irq_sr & INTMS_TIMEOUT)
2338c2ecf20Sopenharmony_ci			writel_relaxed(INT_CLR_TIMEOUT, priv->base + IR_INTC);
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/* Empty software fifo */
2378c2ecf20Sopenharmony_ci	ir_raw_event_handle(priv->rdev);
2388c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic const struct of_device_id hix5hd2_ir_table[] = {
2428c2ecf20Sopenharmony_ci	{ .compatible = "hisilicon,hix5hd2-ir", &hix5hd2_data, },
2438c2ecf20Sopenharmony_ci	{ .compatible = "hisilicon,hi3796cv300-ir", &hi3796cv300_data, },
2448c2ecf20Sopenharmony_ci	{},
2458c2ecf20Sopenharmony_ci};
2468c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, hix5hd2_ir_table);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_cistatic int hix5hd2_ir_probe(struct platform_device *pdev)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct rc_dev *rdev;
2518c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
2528c2ecf20Sopenharmony_ci	struct resource *res;
2538c2ecf20Sopenharmony_ci	struct hix5hd2_ir_priv *priv;
2548c2ecf20Sopenharmony_ci	struct device_node *node = pdev->dev.of_node;
2558c2ecf20Sopenharmony_ci	const struct of_device_id *of_id;
2568c2ecf20Sopenharmony_ci	const char *map_name;
2578c2ecf20Sopenharmony_ci	int ret;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
2608c2ecf20Sopenharmony_ci	if (!priv)
2618c2ecf20Sopenharmony_ci		return -ENOMEM;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	of_id = of_match_device(hix5hd2_ir_table, dev);
2648c2ecf20Sopenharmony_ci	if (!of_id) {
2658c2ecf20Sopenharmony_ci		dev_err(dev, "Unable to initialize IR data\n");
2668c2ecf20Sopenharmony_ci		return -ENODEV;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci	priv->socdata = of_id->data;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	priv->regmap = syscon_regmap_lookup_by_phandle(node,
2718c2ecf20Sopenharmony_ci						       "hisilicon,power-syscon");
2728c2ecf20Sopenharmony_ci	if (IS_ERR(priv->regmap)) {
2738c2ecf20Sopenharmony_ci		dev_info(dev, "no power-reg\n");
2748c2ecf20Sopenharmony_ci		priv->regmap = NULL;
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2788c2ecf20Sopenharmony_ci	priv->base = devm_ioremap_resource(dev, res);
2798c2ecf20Sopenharmony_ci	if (IS_ERR(priv->base))
2808c2ecf20Sopenharmony_ci		return PTR_ERR(priv->base);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	priv->irq = platform_get_irq(pdev, 0);
2838c2ecf20Sopenharmony_ci	if (priv->irq < 0)
2848c2ecf20Sopenharmony_ci		return priv->irq;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	rdev = rc_allocate_device(RC_DRIVER_IR_RAW);
2878c2ecf20Sopenharmony_ci	if (!rdev)
2888c2ecf20Sopenharmony_ci		return -ENOMEM;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	priv->clock = devm_clk_get(dev, NULL);
2918c2ecf20Sopenharmony_ci	if (IS_ERR(priv->clock)) {
2928c2ecf20Sopenharmony_ci		dev_err(dev, "clock not found\n");
2938c2ecf20Sopenharmony_ci		ret = PTR_ERR(priv->clock);
2948c2ecf20Sopenharmony_ci		goto err;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(priv->clock);
2978c2ecf20Sopenharmony_ci	if (ret)
2988c2ecf20Sopenharmony_ci		goto err;
2998c2ecf20Sopenharmony_ci	priv->rate = clk_get_rate(priv->clock);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	rdev->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
3028c2ecf20Sopenharmony_ci	rdev->priv = priv;
3038c2ecf20Sopenharmony_ci	rdev->open = hix5hd2_ir_open;
3048c2ecf20Sopenharmony_ci	rdev->close = hix5hd2_ir_close;
3058c2ecf20Sopenharmony_ci	rdev->driver_name = IR_HIX5HD2_NAME;
3068c2ecf20Sopenharmony_ci	map_name = of_get_property(node, "linux,rc-map-name", NULL);
3078c2ecf20Sopenharmony_ci	rdev->map_name = map_name ?: RC_MAP_EMPTY;
3088c2ecf20Sopenharmony_ci	rdev->device_name = IR_HIX5HD2_NAME;
3098c2ecf20Sopenharmony_ci	rdev->input_phys = IR_HIX5HD2_NAME "/input0";
3108c2ecf20Sopenharmony_ci	rdev->input_id.bustype = BUS_HOST;
3118c2ecf20Sopenharmony_ci	rdev->input_id.vendor = 0x0001;
3128c2ecf20Sopenharmony_ci	rdev->input_id.product = 0x0001;
3138c2ecf20Sopenharmony_ci	rdev->input_id.version = 0x0100;
3148c2ecf20Sopenharmony_ci	rdev->rx_resolution = 10;
3158c2ecf20Sopenharmony_ci	rdev->timeout = IR_CFG_SYMBOL_MAXWIDTH * 10;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	ret = rc_register_device(rdev);
3188c2ecf20Sopenharmony_ci	if (ret < 0)
3198c2ecf20Sopenharmony_ci		goto clkerr;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	if (devm_request_irq(dev, priv->irq, hix5hd2_ir_rx_interrupt,
3228c2ecf20Sopenharmony_ci			     0, pdev->name, priv) < 0) {
3238c2ecf20Sopenharmony_ci		dev_err(dev, "IRQ %d register failed\n", priv->irq);
3248c2ecf20Sopenharmony_ci		ret = -EINVAL;
3258c2ecf20Sopenharmony_ci		goto regerr;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	priv->rdev = rdev;
3298c2ecf20Sopenharmony_ci	priv->dev = dev;
3308c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, priv);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	return ret;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ciregerr:
3358c2ecf20Sopenharmony_ci	rc_unregister_device(rdev);
3368c2ecf20Sopenharmony_ci	rdev = NULL;
3378c2ecf20Sopenharmony_ciclkerr:
3388c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->clock);
3398c2ecf20Sopenharmony_cierr:
3408c2ecf20Sopenharmony_ci	rc_free_device(rdev);
3418c2ecf20Sopenharmony_ci	dev_err(dev, "Unable to register device (%d)\n", ret);
3428c2ecf20Sopenharmony_ci	return ret;
3438c2ecf20Sopenharmony_ci}
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_cistatic int hix5hd2_ir_remove(struct platform_device *pdev)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	struct hix5hd2_ir_priv *priv = platform_get_drvdata(pdev);
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->clock);
3508c2ecf20Sopenharmony_ci	rc_unregister_device(priv->rdev);
3518c2ecf20Sopenharmony_ci	return 0;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
3558c2ecf20Sopenharmony_cistatic int hix5hd2_ir_suspend(struct device *dev)
3568c2ecf20Sopenharmony_ci{
3578c2ecf20Sopenharmony_ci	struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	clk_disable_unprepare(priv->clock);
3608c2ecf20Sopenharmony_ci	hix5hd2_ir_clk_enable(priv, false);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	return 0;
3638c2ecf20Sopenharmony_ci}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_cistatic int hix5hd2_ir_resume(struct device *dev)
3668c2ecf20Sopenharmony_ci{
3678c2ecf20Sopenharmony_ci	struct hix5hd2_ir_priv *priv = dev_get_drvdata(dev);
3688c2ecf20Sopenharmony_ci	int ret;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	ret = hix5hd2_ir_clk_enable(priv, true);
3718c2ecf20Sopenharmony_ci	if (ret)
3728c2ecf20Sopenharmony_ci		return ret;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(priv->clock);
3758c2ecf20Sopenharmony_ci	if (ret) {
3768c2ecf20Sopenharmony_ci		hix5hd2_ir_clk_enable(priv, false);
3778c2ecf20Sopenharmony_ci		return ret;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	hix5hd2_ir_enable(priv);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	writel_relaxed(0x00, priv->base + IR_INTM);
3838c2ecf20Sopenharmony_ci	writel_relaxed(0xff, priv->base + IR_INTC);
3848c2ecf20Sopenharmony_ci	writel_relaxed(0x01, priv->base + IR_START);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return 0;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci#endif
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(hix5hd2_ir_pm_ops, hix5hd2_ir_suspend,
3918c2ecf20Sopenharmony_ci			 hix5hd2_ir_resume);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_cistatic struct platform_driver hix5hd2_ir_driver = {
3948c2ecf20Sopenharmony_ci	.driver = {
3958c2ecf20Sopenharmony_ci		.name = IR_HIX5HD2_NAME,
3968c2ecf20Sopenharmony_ci		.of_match_table = hix5hd2_ir_table,
3978c2ecf20Sopenharmony_ci		.pm     = &hix5hd2_ir_pm_ops,
3988c2ecf20Sopenharmony_ci	},
3998c2ecf20Sopenharmony_ci	.probe = hix5hd2_ir_probe,
4008c2ecf20Sopenharmony_ci	.remove = hix5hd2_ir_remove,
4018c2ecf20Sopenharmony_ci};
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_cimodule_platform_driver(hix5hd2_ir_driver);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IR controller driver for hix5hd2 platforms");
4068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Guoxiong Yan <yanguoxiong@huawei.com>");
4078c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
4088c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:hix5hd2-ir");
409