1/*
2 * ImgTec IR Decoder found in PowerDown Controller.
3 *
4 * Copyright 2010-2014 Imagination Technologies Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
10 *
11 * This contains core img-ir code for setting up the driver. The two interfaces
12 * (raw and hardware decode) are handled separately.
13 */
14
15#include <linux/clk.h>
16#include <linux/init.h>
17#include <linux/interrupt.h>
18#include <linux/io.h>
19#include <linux/module.h>
20#include <linux/platform_device.h>
21#include <linux/slab.h>
22#include <linux/spinlock.h>
23#include "img-ir.h"
24
25static irqreturn_t img_ir_isr(int irq, void *dev_id)
26{
27	struct img_ir_priv *priv = dev_id;
28	u32 irq_status;
29
30	spin_lock(&priv->lock);
31	/* we have to clear irqs before reading */
32	irq_status = img_ir_read(priv, IMG_IR_IRQ_STATUS);
33	img_ir_write(priv, IMG_IR_IRQ_CLEAR, irq_status);
34
35	/* don't handle valid data irqs if we're only interested in matches */
36	irq_status &= img_ir_read(priv, IMG_IR_IRQ_ENABLE);
37
38	/* hand off edge interrupts to raw decode handler */
39	if (irq_status & IMG_IR_IRQ_EDGE && img_ir_raw_enabled(&priv->raw))
40		img_ir_isr_raw(priv, irq_status);
41
42	/* hand off hardware match interrupts to hardware decode handler */
43	if (irq_status & (IMG_IR_IRQ_DATA_MATCH |
44			  IMG_IR_IRQ_DATA_VALID |
45			  IMG_IR_IRQ_DATA2_VALID) &&
46	    img_ir_hw_enabled(&priv->hw))
47		img_ir_isr_hw(priv, irq_status);
48
49	spin_unlock(&priv->lock);
50	return IRQ_HANDLED;
51}
52
53static void img_ir_setup(struct img_ir_priv *priv)
54{
55	/* start off with interrupts disabled */
56	img_ir_write(priv, IMG_IR_IRQ_ENABLE, 0);
57
58	img_ir_setup_raw(priv);
59	img_ir_setup_hw(priv);
60
61	if (!IS_ERR(priv->clk))
62		clk_prepare_enable(priv->clk);
63}
64
65static void img_ir_ident(struct img_ir_priv *priv)
66{
67	u32 core_rev = img_ir_read(priv, IMG_IR_CORE_REV);
68
69	dev_info(priv->dev,
70		 "IMG IR Decoder (%d.%d.%d.%d) probed successfully\n",
71		 (core_rev & IMG_IR_DESIGNER) >> IMG_IR_DESIGNER_SHIFT,
72		 (core_rev & IMG_IR_MAJOR_REV) >> IMG_IR_MAJOR_REV_SHIFT,
73		 (core_rev & IMG_IR_MINOR_REV) >> IMG_IR_MINOR_REV_SHIFT,
74		 (core_rev & IMG_IR_MAINT_REV) >> IMG_IR_MAINT_REV_SHIFT);
75	dev_info(priv->dev, "Modes:%s%s\n",
76		 img_ir_hw_enabled(&priv->hw) ? " hardware" : "",
77		 img_ir_raw_enabled(&priv->raw) ? " raw" : "");
78}
79
80static int img_ir_probe(struct platform_device *pdev)
81{
82	struct img_ir_priv *priv;
83	struct resource *res_regs;
84	int irq, error, error2;
85
86	/* Get resources from platform device */
87	irq = platform_get_irq(pdev, 0);
88	if (irq < 0) {
89		dev_err(&pdev->dev, "cannot find IRQ resource\n");
90		return irq;
91	}
92
93	/* Private driver data */
94	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
95	if (!priv) {
96		dev_err(&pdev->dev, "cannot allocate device data\n");
97		return -ENOMEM;
98	}
99	platform_set_drvdata(pdev, priv);
100	priv->dev = &pdev->dev;
101	spin_lock_init(&priv->lock);
102
103	/* Ioremap the registers */
104	res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
105	priv->reg_base = devm_ioremap_resource(&pdev->dev, res_regs);
106	if (IS_ERR(priv->reg_base))
107		return PTR_ERR(priv->reg_base);
108
109	/* Get core clock */
110	priv->clk = devm_clk_get(&pdev->dev, "core");
111	if (IS_ERR(priv->clk))
112		dev_warn(&pdev->dev, "cannot get core clock resource\n");
113
114	/* Get sys clock */
115	priv->sys_clk = devm_clk_get(&pdev->dev, "sys");
116	if (IS_ERR(priv->sys_clk))
117		dev_warn(&pdev->dev, "cannot get sys clock resource\n");
118	/*
119	 * Enabling the system clock before the register interface is
120	 * accessed. ISR shouldn't get called with Sys Clock disabled,
121	 * hence exiting probe with an error.
122	 */
123	if (!IS_ERR(priv->sys_clk)) {
124		error = clk_prepare_enable(priv->sys_clk);
125		if (error) {
126			dev_err(&pdev->dev, "cannot enable sys clock\n");
127			return error;
128		}
129	}
130
131	/* Set up raw & hw decoder */
132	error = img_ir_probe_raw(priv);
133	error2 = img_ir_probe_hw(priv);
134	if (error && error2) {
135		if (error == -ENODEV)
136			error = error2;
137		goto err_probe;
138	}
139
140	/* Get the IRQ */
141	priv->irq = irq;
142	error = request_irq(priv->irq, img_ir_isr, 0, "img-ir", priv);
143	if (error) {
144		dev_err(&pdev->dev, "cannot register IRQ %u\n",
145			priv->irq);
146		error = -EIO;
147		goto err_irq;
148	}
149
150	img_ir_ident(priv);
151	img_ir_setup(priv);
152
153	return 0;
154
155err_irq:
156	img_ir_remove_hw(priv);
157	img_ir_remove_raw(priv);
158err_probe:
159	if (!IS_ERR(priv->sys_clk))
160		clk_disable_unprepare(priv->sys_clk);
161	return error;
162}
163
164static int img_ir_remove(struct platform_device *pdev)
165{
166	struct img_ir_priv *priv = platform_get_drvdata(pdev);
167
168	free_irq(priv->irq, priv);
169	img_ir_remove_hw(priv);
170	img_ir_remove_raw(priv);
171
172	if (!IS_ERR(priv->clk))
173		clk_disable_unprepare(priv->clk);
174	if (!IS_ERR(priv->sys_clk))
175		clk_disable_unprepare(priv->sys_clk);
176	return 0;
177}
178
179static SIMPLE_DEV_PM_OPS(img_ir_pmops, img_ir_suspend, img_ir_resume);
180
181static const struct of_device_id img_ir_match[] = {
182	{ .compatible = "img,ir-rev1" },
183	{}
184};
185MODULE_DEVICE_TABLE(of, img_ir_match);
186
187static struct platform_driver img_ir_driver = {
188	.driver = {
189		.name = "img-ir",
190		.of_match_table	= img_ir_match,
191		.pm = &img_ir_pmops,
192	},
193	.probe = img_ir_probe,
194	.remove = img_ir_remove,
195};
196
197module_platform_driver(img_ir_driver);
198
199MODULE_AUTHOR("Imagination Technologies Ltd.");
200MODULE_DESCRIPTION("ImgTec IR");
201MODULE_LICENSE("GPL");
202