blob: e9b9c582f818e1197c78f85302023cbc8a7b9174 [file] [log] [blame]
Thomas Gleixnerc942fdd2019-05-27 08:55:06 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Alexander Bersenevb4e3e592014-06-08 15:08:10 -03002/*
3 * Driver for Allwinner sunXi IR controller
4 *
5 * Copyright (C) 2014 Alexsey Shestacov <wingrime@linux-sunxi.org>
6 * Copyright (C) 2014 Alexander Bersenev <bay@hackerdom.ru>
7 *
8 * Based on sun5i-ir.c:
9 * Copyright (C) 2007-2012 Daniel Wang
10 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
Alexander Bersenevb4e3e592014-06-08 15:08:10 -030011 */
12
13#include <linux/clk.h>
14#include <linux/interrupt.h>
15#include <linux/module.h>
16#include <linux/of_platform.h>
Hans de Goede44f8af62014-11-20 11:59:04 -030017#include <linux/reset.h>
Alexander Bersenevb4e3e592014-06-08 15:08:10 -030018#include <media/rc-core.h>
19
20#define SUNXI_IR_DEV "sunxi-ir"
21
22/* Registers */
23/* IR Control */
24#define SUNXI_IR_CTL_REG 0x00
25/* Global Enable */
26#define REG_CTL_GEN BIT(0)
27/* RX block enable */
28#define REG_CTL_RXEN BIT(1)
29/* CIR mode */
30#define REG_CTL_MD (BIT(4) | BIT(5))
31
32/* Rx Config */
33#define SUNXI_IR_RXCTL_REG 0x10
34/* Pulse Polarity Invert flag */
35#define REG_RXCTL_RPPI BIT(2)
36
37/* Rx Data */
38#define SUNXI_IR_RXFIFO_REG 0x20
39
40/* Rx Interrupt Enable */
41#define SUNXI_IR_RXINT_REG 0x2C
42/* Rx FIFO Overflow */
43#define REG_RXINT_ROI_EN BIT(0)
44/* Rx Packet End */
45#define REG_RXINT_RPEI_EN BIT(1)
46/* Rx FIFO Data Available */
47#define REG_RXINT_RAI_EN BIT(4)
48
49/* Rx FIFO available byte level */
Hans de Goedea4bca4c2014-11-20 12:10:47 -030050#define REG_RXINT_RAL(val) ((val) << 8)
Alexander Bersenevb4e3e592014-06-08 15:08:10 -030051
52/* Rx Interrupt Status */
53#define SUNXI_IR_RXSTA_REG 0x30
54/* RX FIFO Get Available Counter */
Hans de Goedea4bca4c2014-11-20 12:10:47 -030055#define REG_RXSTA_GET_AC(val) (((val) >> 8) & (ir->fifo_size * 2 - 1))
Alexander Bersenevb4e3e592014-06-08 15:08:10 -030056/* Clear all interrupt status value */
57#define REG_RXSTA_CLEARALL 0xff
58
59/* IR Sample Config */
60#define SUNXI_IR_CIR_REG 0x34
61/* CIR_REG register noise threshold */
62#define REG_CIR_NTHR(val) (((val) << 2) & (GENMASK(7, 2)))
63/* CIR_REG register idle threshold */
64#define REG_CIR_ITHR(val) (((val) << 8) & (GENMASK(15, 8)))
65
Philipp Rossak10e71202018-02-13 07:29:47 -050066/* Required frequency for IR0 or IR1 clock in CIR mode (default) */
Alexander Bersenevb4e3e592014-06-08 15:08:10 -030067#define SUNXI_IR_BASE_CLK 8000000
Alexander Bersenevb4e3e592014-06-08 15:08:10 -030068/* Noise threshold in samples */
69#define SUNXI_IR_RXNOISE 1
70/* Idle Threshold in samples */
71#define SUNXI_IR_RXIDLE 20
72/* Time after which device stops sending data in ms */
73#define SUNXI_IR_TIMEOUT 120
74
Clément Péron6b197cb2019-06-07 20:10:49 -030075/**
76 * struct sunxi_ir_quirks - Differences between SoC variants.
77 *
78 * @has_reset: SoC needs reset deasserted.
79 * @fifo_size: size of the fifo.
80 */
81struct sunxi_ir_quirks {
82 bool has_reset;
83 int fifo_size;
84};
85
Alexander Bersenevb4e3e592014-06-08 15:08:10 -030086struct sunxi_ir {
87 spinlock_t ir_lock;
88 struct rc_dev *rc;
89 void __iomem *base;
90 int irq;
Hans de Goedea4bca4c2014-11-20 12:10:47 -030091 int fifo_size;
Alexander Bersenevb4e3e592014-06-08 15:08:10 -030092 struct clk *clk;
93 struct clk *apb_clk;
Hans de Goede44f8af62014-11-20 11:59:04 -030094 struct reset_control *rst;
Alexander Bersenevb4e3e592014-06-08 15:08:10 -030095 const char *map_name;
96};
97
98static irqreturn_t sunxi_ir_irq(int irqno, void *dev_id)
99{
100 unsigned long status;
101 unsigned char dt;
102 unsigned int cnt, rc;
103 struct sunxi_ir *ir = dev_id;
Sean Young183e19f2018-08-21 15:57:52 -0400104 struct ir_raw_event rawir = {};
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300105
106 spin_lock(&ir->ir_lock);
107
108 status = readl(ir->base + SUNXI_IR_RXSTA_REG);
109
110 /* clean all pending statuses */
111 writel(status | REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
112
Hans de Goedea4bca4c2014-11-20 12:10:47 -0300113 if (status & (REG_RXINT_RAI_EN | REG_RXINT_RPEI_EN)) {
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300114 /* How many messages in fifo */
115 rc = REG_RXSTA_GET_AC(status);
116 /* Sanity check */
Hans de Goedea4bca4c2014-11-20 12:10:47 -0300117 rc = rc > ir->fifo_size ? ir->fifo_size : rc;
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300118 /* If we have data */
119 for (cnt = 0; cnt < rc; cnt++) {
120 /* for each bit in fifo */
121 dt = readb(ir->base + SUNXI_IR_RXFIFO_REG);
122 rawir.pulse = (dt & 0x80) != 0;
Philipp Rossak10e71202018-02-13 07:29:47 -0500123 rawir.duration = ((dt & 0x7f) + 1) *
124 ir->rc->rx_resolution;
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300125 ir_raw_event_store_with_filter(ir->rc, &rawir);
126 }
127 }
128
129 if (status & REG_RXINT_ROI_EN) {
130 ir_raw_event_reset(ir->rc);
131 } else if (status & REG_RXINT_RPEI_EN) {
132 ir_raw_event_set_idle(ir->rc, true);
133 ir_raw_event_handle(ir->rc);
134 }
135
136 spin_unlock(&ir->ir_lock);
137
138 return IRQ_HANDLED;
139}
140
141static int sunxi_ir_probe(struct platform_device *pdev)
142{
143 int ret = 0;
144 unsigned long tmp = 0;
145
146 struct device *dev = &pdev->dev;
147 struct device_node *dn = dev->of_node;
Clément Péron6b197cb2019-06-07 20:10:49 -0300148 const struct sunxi_ir_quirks *quirks;
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300149 struct resource *res;
150 struct sunxi_ir *ir;
Philipp Rossak10e71202018-02-13 07:29:47 -0500151 u32 b_clk_freq = SUNXI_IR_BASE_CLK;
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300152
153 ir = devm_kzalloc(dev, sizeof(struct sunxi_ir), GFP_KERNEL);
154 if (!ir)
155 return -ENOMEM;
156
Clément Péron6b197cb2019-06-07 20:10:49 -0300157 quirks = of_device_get_match_data(&pdev->dev);
158 if (!quirks) {
159 dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
160 return -ENODEV;
161 }
162
Chen-Yu Tsai768acf42015-12-22 02:27:35 -0200163 spin_lock_init(&ir->ir_lock);
164
Clément Péron6b197cb2019-06-07 20:10:49 -0300165 ir->fifo_size = quirks->fifo_size;
Hans de Goedea4bca4c2014-11-20 12:10:47 -0300166
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300167 /* Clock */
168 ir->apb_clk = devm_clk_get(dev, "apb");
169 if (IS_ERR(ir->apb_clk)) {
170 dev_err(dev, "failed to get a apb clock.\n");
171 return PTR_ERR(ir->apb_clk);
172 }
173 ir->clk = devm_clk_get(dev, "ir");
174 if (IS_ERR(ir->clk)) {
175 dev_err(dev, "failed to get a ir clock.\n");
176 return PTR_ERR(ir->clk);
177 }
178
Philipp Rossak10e71202018-02-13 07:29:47 -0500179 /* Base clock frequency (optional) */
180 of_property_read_u32(dn, "clock-frequency", &b_clk_freq);
181
Clément Péron6b197cb2019-06-07 20:10:49 -0300182 /* Reset */
183 if (quirks->has_reset) {
184 ir->rst = devm_reset_control_get_exclusive(dev, NULL);
185 if (IS_ERR(ir->rst))
186 return PTR_ERR(ir->rst);
187 ret = reset_control_deassert(ir->rst);
188 if (ret)
189 return ret;
190 }
Hans de Goede44f8af62014-11-20 11:59:04 -0300191
Philipp Rossak10e71202018-02-13 07:29:47 -0500192 ret = clk_set_rate(ir->clk, b_clk_freq);
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300193 if (ret) {
194 dev_err(dev, "set ir base clock failed!\n");
Hans de Goede44f8af62014-11-20 11:59:04 -0300195 goto exit_reset_assert;
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300196 }
Philipp Rossak10e71202018-02-13 07:29:47 -0500197 dev_dbg(dev, "set base clock frequency to %d Hz.\n", b_clk_freq);
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300198
199 if (clk_prepare_enable(ir->apb_clk)) {
200 dev_err(dev, "try to enable apb_ir_clk failed\n");
Hans de Goede44f8af62014-11-20 11:59:04 -0300201 ret = -EINVAL;
202 goto exit_reset_assert;
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300203 }
204
205 if (clk_prepare_enable(ir->clk)) {
206 dev_err(dev, "try to enable ir_clk failed\n");
207 ret = -EINVAL;
208 goto exit_clkdisable_apb_clk;
209 }
210
211 /* IO */
212 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
213 ir->base = devm_ioremap_resource(dev, res);
214 if (IS_ERR(ir->base)) {
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300215 ret = PTR_ERR(ir->base);
216 goto exit_clkdisable_clk;
217 }
218
Andi Shyti0f7499f2016-12-16 06:50:58 -0200219 ir->rc = rc_allocate_device(RC_DRIVER_IR_RAW);
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300220 if (!ir->rc) {
221 dev_err(dev, "failed to allocate device\n");
222 ret = -ENOMEM;
223 goto exit_clkdisable_clk;
224 }
225
226 ir->rc->priv = ir;
Sean Young518f4b22017-07-01 12:13:19 -0400227 ir->rc->device_name = SUNXI_IR_DEV;
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300228 ir->rc->input_phys = "sunxi-ir/input0";
229 ir->rc->input_id.bustype = BUS_HOST;
230 ir->rc->input_id.vendor = 0x0001;
231 ir->rc->input_id.product = 0x0001;
232 ir->rc->input_id.version = 0x0100;
233 ir->map_name = of_get_property(dn, "linux,rc-map-name", NULL);
234 ir->rc->map_name = ir->map_name ?: RC_MAP_EMPTY;
235 ir->rc->dev.parent = dev;
Sean Young6d741bf2017-08-07 16:20:58 -0400236 ir->rc->allowed_protocols = RC_PROTO_BIT_ALL_IR_DECODER;
Philipp Rossak10e71202018-02-13 07:29:47 -0500237 /* Frequency after IR internal divider with sample period in ns */
238 ir->rc->rx_resolution = (1000000000ul / (b_clk_freq / 64));
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300239 ir->rc->timeout = MS_TO_NS(SUNXI_IR_TIMEOUT);
240 ir->rc->driver_name = SUNXI_IR_DEV;
241
242 ret = rc_register_device(ir->rc);
243 if (ret) {
244 dev_err(dev, "failed to register rc device\n");
245 goto exit_free_dev;
246 }
247
248 platform_set_drvdata(pdev, ir);
249
250 /* IRQ */
251 ir->irq = platform_get_irq(pdev, 0);
252 if (ir->irq < 0) {
253 dev_err(dev, "no irq resource\n");
254 ret = ir->irq;
255 goto exit_free_dev;
256 }
257
258 ret = devm_request_irq(dev, ir->irq, sunxi_ir_irq, 0, SUNXI_IR_DEV, ir);
259 if (ret) {
260 dev_err(dev, "failed request irq\n");
261 goto exit_free_dev;
262 }
263
264 /* Enable CIR Mode */
265 writel(REG_CTL_MD, ir->base+SUNXI_IR_CTL_REG);
266
267 /* Set noise threshold and idle threshold */
268 writel(REG_CIR_NTHR(SUNXI_IR_RXNOISE)|REG_CIR_ITHR(SUNXI_IR_RXIDLE),
269 ir->base + SUNXI_IR_CIR_REG);
270
271 /* Invert Input Signal */
272 writel(REG_RXCTL_RPPI, ir->base + SUNXI_IR_RXCTL_REG);
273
274 /* Clear All Rx Interrupt Status */
275 writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
276
277 /*
278 * Enable IRQ on overflow, packet end, FIFO available with trigger
279 * level
280 */
281 writel(REG_RXINT_ROI_EN | REG_RXINT_RPEI_EN |
Hans de Goedea4bca4c2014-11-20 12:10:47 -0300282 REG_RXINT_RAI_EN | REG_RXINT_RAL(ir->fifo_size / 2 - 1),
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300283 ir->base + SUNXI_IR_RXINT_REG);
284
285 /* Enable IR Module */
286 tmp = readl(ir->base + SUNXI_IR_CTL_REG);
287 writel(tmp | REG_CTL_GEN | REG_CTL_RXEN, ir->base + SUNXI_IR_CTL_REG);
288
289 dev_info(dev, "initialized sunXi IR driver\n");
290 return 0;
291
292exit_free_dev:
293 rc_free_device(ir->rc);
294exit_clkdisable_clk:
295 clk_disable_unprepare(ir->clk);
296exit_clkdisable_apb_clk:
297 clk_disable_unprepare(ir->apb_clk);
Hans de Goede44f8af62014-11-20 11:59:04 -0300298exit_reset_assert:
Philipp Zabelc3d4fb02017-03-15 08:31:38 -0300299 reset_control_assert(ir->rst);
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300300
301 return ret;
302}
303
304static int sunxi_ir_remove(struct platform_device *pdev)
305{
306 unsigned long flags;
307 struct sunxi_ir *ir = platform_get_drvdata(pdev);
308
309 clk_disable_unprepare(ir->clk);
310 clk_disable_unprepare(ir->apb_clk);
Philipp Zabelc3d4fb02017-03-15 08:31:38 -0300311 reset_control_assert(ir->rst);
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300312
313 spin_lock_irqsave(&ir->ir_lock, flags);
314 /* disable IR IRQ */
315 writel(0, ir->base + SUNXI_IR_RXINT_REG);
316 /* clear All Rx Interrupt Status */
317 writel(REG_RXSTA_CLEARALL, ir->base + SUNXI_IR_RXSTA_REG);
318 /* disable IR */
319 writel(0, ir->base + SUNXI_IR_CTL_REG);
320 spin_unlock_irqrestore(&ir->ir_lock, flags);
321
322 rc_unregister_device(ir->rc);
323 return 0;
324}
325
Clément Péron6b197cb2019-06-07 20:10:49 -0300326static const struct sunxi_ir_quirks sun4i_a10_ir_quirks = {
327 .has_reset = false,
328 .fifo_size = 16,
329};
330
331static const struct sunxi_ir_quirks sun5i_a13_ir_quirks = {
332 .has_reset = false,
333 .fifo_size = 64,
334};
335
Clément Péron87d06092019-06-07 20:10:50 -0300336static const struct sunxi_ir_quirks sun6i_a31_ir_quirks = {
337 .has_reset = true,
338 .fifo_size = 64,
339};
340
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300341static const struct of_device_id sunxi_ir_match[] = {
Clément Péron6b197cb2019-06-07 20:10:49 -0300342 {
343 .compatible = "allwinner,sun4i-a10-ir",
344 .data = &sun4i_a10_ir_quirks,
345 },
346 {
347 .compatible = "allwinner,sun5i-a13-ir",
348 .data = &sun5i_a13_ir_quirks,
349 },
Clément Péron87d06092019-06-07 20:10:50 -0300350 {
351 .compatible = "allwinner,sun6i-a31-ir",
352 .data = &sun6i_a31_ir_quirks,
353 },
Clément Péron6b197cb2019-06-07 20:10:49 -0300354 {}
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300355};
Emilio Lópezea05e8b2016-02-21 22:26:34 -0300356MODULE_DEVICE_TABLE(of, sunxi_ir_match);
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300357
358static struct platform_driver sunxi_ir_driver = {
359 .probe = sunxi_ir_probe,
360 .remove = sunxi_ir_remove,
361 .driver = {
362 .name = SUNXI_IR_DEV,
Alexander Bersenevb4e3e592014-06-08 15:08:10 -0300363 .of_match_table = sunxi_ir_match,
364 },
365};
366
367module_platform_driver(sunxi_ir_driver);
368
369MODULE_DESCRIPTION("Allwinner sunXi IR controller driver");
370MODULE_AUTHOR("Alexsey Shestacov <wingrime@linux-sunxi.org>");
371MODULE_LICENSE("GPL");