xref: /linux/drivers/media/rc/img-ir/img-ir-core.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2160a8f8aSJames Hogan /*
3160a8f8aSJames Hogan  * ImgTec IR Decoder found in PowerDown Controller.
4160a8f8aSJames Hogan  *
5160a8f8aSJames Hogan  * Copyright 2010-2014 Imagination Technologies Ltd.
6160a8f8aSJames Hogan  *
7160a8f8aSJames Hogan  * This contains core img-ir code for setting up the driver. The two interfaces
8160a8f8aSJames Hogan  * (raw and hardware decode) are handled separately.
9160a8f8aSJames Hogan  */
10160a8f8aSJames Hogan 
11160a8f8aSJames Hogan #include <linux/clk.h>
12160a8f8aSJames Hogan #include <linux/init.h>
13160a8f8aSJames Hogan #include <linux/interrupt.h>
14160a8f8aSJames Hogan #include <linux/io.h>
15160a8f8aSJames Hogan #include <linux/module.h>
16160a8f8aSJames Hogan #include <linux/platform_device.h>
17160a8f8aSJames Hogan #include <linux/slab.h>
18160a8f8aSJames Hogan #include <linux/spinlock.h>
19160a8f8aSJames Hogan #include "img-ir.h"
20160a8f8aSJames Hogan 
img_ir_isr(int irq,void * dev_id)21160a8f8aSJames Hogan static irqreturn_t img_ir_isr(int irq, void *dev_id)
22160a8f8aSJames Hogan {
23160a8f8aSJames Hogan 	struct img_ir_priv *priv = dev_id;
24160a8f8aSJames Hogan 	u32 irq_status;
25160a8f8aSJames Hogan 
26160a8f8aSJames Hogan 	spin_lock(&priv->lock);
27160a8f8aSJames Hogan 	/* we have to clear irqs before reading */
28160a8f8aSJames Hogan 	irq_status = img_ir_read(priv, IMG_IR_IRQ_STATUS);
29160a8f8aSJames Hogan 	img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_status);
30160a8f8aSJames Hogan 
31160a8f8aSJames Hogan 	/* don't handle valid data irqs if we're only interested in matches */
32160a8f8aSJames Hogan 	irq_status &= img_ir_read(priv, IMG_IR_IRQ_ENABLE);
33160a8f8aSJames Hogan 
34160a8f8aSJames Hogan 	/* hand off edge interrupts to raw decode handler */
35160a8f8aSJames Hogan 	if (irq_status & IMG_IR_IRQ_EDGE && img_ir_raw_enabled(&priv->raw))
36160a8f8aSJames Hogan 		img_ir_isr_raw(priv, irq_status);
37160a8f8aSJames Hogan 
38160a8f8aSJames Hogan 	/* hand off hardware match interrupts to hardware decode handler */
39160a8f8aSJames Hogan 	if (irq_status & (IMG_IR_IRQ_DATA_MATCH |
40160a8f8aSJames Hogan 			  IMG_IR_IRQ_DATA_VALID |
41160a8f8aSJames Hogan 			  IMG_IR_IRQ_DATA2_VALID) &&
42160a8f8aSJames Hogan 	    img_ir_hw_enabled(&priv->hw))
43160a8f8aSJames Hogan 		img_ir_isr_hw(priv, irq_status);
44160a8f8aSJames Hogan 
45160a8f8aSJames Hogan 	spin_unlock(&priv->lock);
46160a8f8aSJames Hogan 	return IRQ_HANDLED;
47160a8f8aSJames Hogan }
48160a8f8aSJames Hogan 
img_ir_setup(struct img_ir_priv * priv)49160a8f8aSJames Hogan static void img_ir_setup(struct img_ir_priv *priv)
50160a8f8aSJames Hogan {
51160a8f8aSJames Hogan 	/* start off with interrupts disabled */
52160a8f8aSJames Hogan 	img_ir_write(priv, IMG_IR_IRQ_ENABLE, 0);
53160a8f8aSJames Hogan 
54160a8f8aSJames Hogan 	img_ir_setup_raw(priv);
55160a8f8aSJames Hogan 	img_ir_setup_hw(priv);
56160a8f8aSJames Hogan 
57160a8f8aSJames Hogan 	if (!IS_ERR(priv->clk))
58160a8f8aSJames Hogan 		clk_prepare_enable(priv->clk);
59160a8f8aSJames Hogan }
60160a8f8aSJames Hogan 
img_ir_ident(struct img_ir_priv * priv)61160a8f8aSJames Hogan static void img_ir_ident(struct img_ir_priv *priv)
62160a8f8aSJames Hogan {
63160a8f8aSJames Hogan 	u32 core_rev = img_ir_read(priv, IMG_IR_CORE_REV);
64160a8f8aSJames Hogan 
65160a8f8aSJames Hogan 	dev_info(priv->dev,
66160a8f8aSJames Hogan 		 "IMG IR Decoder (%d.%d.%d.%d) probed successfully\n",
67160a8f8aSJames Hogan 		 (core_rev & IMG_IR_DESIGNER) >> IMG_IR_DESIGNER_SHIFT,
68160a8f8aSJames Hogan 		 (core_rev & IMG_IR_MAJOR_REV) >> IMG_IR_MAJOR_REV_SHIFT,
69160a8f8aSJames Hogan 		 (core_rev & IMG_IR_MINOR_REV) >> IMG_IR_MINOR_REV_SHIFT,
70160a8f8aSJames Hogan 		 (core_rev & IMG_IR_MAINT_REV) >> IMG_IR_MAINT_REV_SHIFT);
71160a8f8aSJames Hogan 	dev_info(priv->dev, "Modes:%s%s\n",
72160a8f8aSJames Hogan 		 img_ir_hw_enabled(&priv->hw) ? " hardware" : "",
73160a8f8aSJames Hogan 		 img_ir_raw_enabled(&priv->raw) ? " raw" : "");
74160a8f8aSJames Hogan }
75160a8f8aSJames Hogan 
img_ir_probe(struct platform_device * pdev)76160a8f8aSJames Hogan static int img_ir_probe(struct platform_device *pdev)
77160a8f8aSJames Hogan {
78160a8f8aSJames Hogan 	struct img_ir_priv *priv;
79160a8f8aSJames Hogan 	int irq, error, error2;
80160a8f8aSJames Hogan 
81160a8f8aSJames Hogan 	/* Get resources from platform device */
82160a8f8aSJames Hogan 	irq = platform_get_irq(pdev, 0);
8397299a30SStephen Boyd 	if (irq < 0)
84160a8f8aSJames Hogan 		return irq;
85160a8f8aSJames Hogan 
86160a8f8aSJames Hogan 	/* Private driver data */
87160a8f8aSJames Hogan 	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
886805454bSMarkus Elfring 	if (!priv)
89160a8f8aSJames Hogan 		return -ENOMEM;
906805454bSMarkus Elfring 
91160a8f8aSJames Hogan 	platform_set_drvdata(pdev, priv);
92160a8f8aSJames Hogan 	priv->dev = &pdev->dev;
93160a8f8aSJames Hogan 	spin_lock_init(&priv->lock);
94160a8f8aSJames Hogan 
95160a8f8aSJames Hogan 	/* Ioremap the registers */
96b619c2eaSCai Huoqing 	priv->reg_base = devm_platform_ioremap_resource(pdev, 0);
97160a8f8aSJames Hogan 	if (IS_ERR(priv->reg_base))
98160a8f8aSJames Hogan 		return PTR_ERR(priv->reg_base);
99160a8f8aSJames Hogan 
100160a8f8aSJames Hogan 	/* Get core clock */
101160a8f8aSJames Hogan 	priv->clk = devm_clk_get(&pdev->dev, "core");
102160a8f8aSJames Hogan 	if (IS_ERR(priv->clk))
103160a8f8aSJames Hogan 		dev_warn(&pdev->dev, "cannot get core clock resource\n");
104cc4e8c3dSSifan Naeem 
105cc4e8c3dSSifan Naeem 	/* Get sys clock */
106cc4e8c3dSSifan Naeem 	priv->sys_clk = devm_clk_get(&pdev->dev, "sys");
107cc4e8c3dSSifan Naeem 	if (IS_ERR(priv->sys_clk))
108cc4e8c3dSSifan Naeem 		dev_warn(&pdev->dev, "cannot get sys clock resource\n");
109160a8f8aSJames Hogan 	/*
110cc4e8c3dSSifan Naeem 	 * Enabling the system clock before the register interface is
111cc4e8c3dSSifan Naeem 	 * accessed. ISR shouldn't get called with Sys Clock disabled,
112cc4e8c3dSSifan Naeem 	 * hence exiting probe with an error.
113160a8f8aSJames Hogan 	 */
114cc4e8c3dSSifan Naeem 	if (!IS_ERR(priv->sys_clk)) {
115cc4e8c3dSSifan Naeem 		error = clk_prepare_enable(priv->sys_clk);
116cc4e8c3dSSifan Naeem 		if (error) {
117cc4e8c3dSSifan Naeem 			dev_err(&pdev->dev, "cannot enable sys clock\n");
118cc4e8c3dSSifan Naeem 			return error;
119cc4e8c3dSSifan Naeem 		}
120cc4e8c3dSSifan Naeem 	}
121160a8f8aSJames Hogan 
122160a8f8aSJames Hogan 	/* Set up raw & hw decoder */
123160a8f8aSJames Hogan 	error = img_ir_probe_raw(priv);
124160a8f8aSJames Hogan 	error2 = img_ir_probe_hw(priv);
125cc4e8c3dSSifan Naeem 	if (error && error2) {
126cc4e8c3dSSifan Naeem 		if (error == -ENODEV)
127cc4e8c3dSSifan Naeem 			error = error2;
128cc4e8c3dSSifan Naeem 		goto err_probe;
129cc4e8c3dSSifan Naeem 	}
130160a8f8aSJames Hogan 
131160a8f8aSJames Hogan 	/* Get the IRQ */
132160a8f8aSJames Hogan 	priv->irq = irq;
133160a8f8aSJames Hogan 	error = request_irq(priv->irq, img_ir_isr, 0, "img-ir", priv);
134160a8f8aSJames Hogan 	if (error) {
135160a8f8aSJames Hogan 		dev_err(&pdev->dev, "cannot register IRQ %u\n",
136160a8f8aSJames Hogan 			priv->irq);
137160a8f8aSJames Hogan 		error = -EIO;
138160a8f8aSJames Hogan 		goto err_irq;
139160a8f8aSJames Hogan 	}
140160a8f8aSJames Hogan 
141160a8f8aSJames Hogan 	img_ir_ident(priv);
142160a8f8aSJames Hogan 	img_ir_setup(priv);
143160a8f8aSJames Hogan 
144160a8f8aSJames Hogan 	return 0;
145160a8f8aSJames Hogan 
146160a8f8aSJames Hogan err_irq:
147160a8f8aSJames Hogan 	img_ir_remove_hw(priv);
148160a8f8aSJames Hogan 	img_ir_remove_raw(priv);
149cc4e8c3dSSifan Naeem err_probe:
150cc4e8c3dSSifan Naeem 	if (!IS_ERR(priv->sys_clk))
151cc4e8c3dSSifan Naeem 		clk_disable_unprepare(priv->sys_clk);
152160a8f8aSJames Hogan 	return error;
153160a8f8aSJames Hogan }
154160a8f8aSJames Hogan 
img_ir_remove(struct platform_device * pdev)155*f6fc05faSUwe Kleine-König static void img_ir_remove(struct platform_device *pdev)
156160a8f8aSJames Hogan {
157160a8f8aSJames Hogan 	struct img_ir_priv *priv = platform_get_drvdata(pdev);
158160a8f8aSJames Hogan 
15980ccf4adSSifan Naeem 	free_irq(priv->irq, priv);
160160a8f8aSJames Hogan 	img_ir_remove_hw(priv);
161160a8f8aSJames Hogan 	img_ir_remove_raw(priv);
162160a8f8aSJames Hogan 
163160a8f8aSJames Hogan 	if (!IS_ERR(priv->clk))
164160a8f8aSJames Hogan 		clk_disable_unprepare(priv->clk);
165cc4e8c3dSSifan Naeem 	if (!IS_ERR(priv->sys_clk))
166cc4e8c3dSSifan Naeem 		clk_disable_unprepare(priv->sys_clk);
167160a8f8aSJames Hogan }
168160a8f8aSJames Hogan 
169160a8f8aSJames Hogan static SIMPLE_DEV_PM_OPS(img_ir_pmops, img_ir_suspend, img_ir_resume);
170160a8f8aSJames Hogan 
171160a8f8aSJames Hogan static const struct of_device_id img_ir_match[] = {
172160a8f8aSJames Hogan 	{ .compatible = "img,ir-rev1" },
173160a8f8aSJames Hogan 	{}
174160a8f8aSJames Hogan };
175160a8f8aSJames Hogan MODULE_DEVICE_TABLE(of, img_ir_match);
176160a8f8aSJames Hogan 
177160a8f8aSJames Hogan static struct platform_driver img_ir_driver = {
178160a8f8aSJames Hogan 	.driver = {
179160a8f8aSJames Hogan 		.name = "img-ir",
180160a8f8aSJames Hogan 		.of_match_table	= img_ir_match,
181160a8f8aSJames Hogan 		.pm = &img_ir_pmops,
182160a8f8aSJames Hogan 	},
183160a8f8aSJames Hogan 	.probe = img_ir_probe,
184*f6fc05faSUwe Kleine-König 	.remove_new = img_ir_remove,
185160a8f8aSJames Hogan };
186160a8f8aSJames Hogan 
187160a8f8aSJames Hogan module_platform_driver(img_ir_driver);
188160a8f8aSJames Hogan 
189160a8f8aSJames Hogan MODULE_AUTHOR("Imagination Technologies Ltd.");
190160a8f8aSJames Hogan MODULE_DESCRIPTION("ImgTec IR");
191160a8f8aSJames Hogan MODULE_LICENSE("GPL");
192