1 /* Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
2
3 * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
4 * the the People's Republic of China and other countries.
5 * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
6
7 * DISCLAIMER
8 * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
9 * IF YOU NEED TO INTEGRATE THIRD PARTY¡¯S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
10 * IN ALLWINNERS¡¯SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
11 * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
12 * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
13 * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
14 * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY¡¯S TECHNOLOGY.
15
16
17 * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
18 * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
19 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
20 * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
21 * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include <stdio.h>
32 #include <stdlib.h>
33
34 #include <interrupt.h>
35 #include <hal_clk.h>
36 #include <hal_gpio.h>
37 #include <hal_reset.h>
38 #include <hal_cfg.h>
39 #include <script.h>
40
41 #include "common_cir.h"
42 #include "platform_cir.h"
43 #include "sunxi_hal_cir.h"
44
45 #ifdef CONFIG_DRIVERS_IR_DEBUG
46 #define CIR_INFO(fmt, arg...) printf("%s()%d " fmt, __func__, __LINE__, ##arg)
47 #else
48 #define CIR_INFO(fmt, arg...) do{}while(0);
49 #endif
50
51 #define CIR_ERR(fmt, arg...) printf("%s()%d " fmt, __func__, __LINE__, ##arg)
52
53 static uint32_t base[CIR_MASTER_NUM] = {
54 SUNXI_IRADC_PBASE,
55 };
56
57 static uint32_t irq[CIR_MASTER_NUM] = {
58 SUNXI_IRQ_IRADC,
59 };
60
61 static cir_gpio_t pin[CIR_MASTER_NUM] = {
62 {GPIOB(7), 5, 0},
63 };
64
65 sunxi_cir_t sunxi_cir[CIR_MASTER_NUM];
66
sunxi_cir_callback_register(cir_port_t port,cir_callback_t callback)67 void sunxi_cir_callback_register(cir_port_t port, cir_callback_t callback)
68 {
69 sunxi_cir_t *cir = &sunxi_cir[port];
70 cir->callback = callback;
71 }
72
sunxi_cir_handler(int irq,void * dev)73 static irqreturn_t sunxi_cir_handler(int irq, void *dev)
74 {
75 sunxi_cir_t *cir = (sunxi_cir_t *)dev;
76
77 uint32_t int_flag, count;
78 uint32_t reg_data, i = 0;
79
80 int_flag = readl(cir->base + CIR_RXSTA);
81
82
83 writel(int_flag, cir->base + CIR_RXSTA);
84
85 count = (int_flag & RAC) >> RAC_OFFSET;
86
87 for(i = 0; i < count; i++)
88 {
89 reg_data = readl(cir->base + CIR_RXFIFO);
90 if (cir->callback)
91 cir->callback(cir->port, RA, reg_data);
92 }
93
94 if ((int_flag & ROI) && cir->callback) {
95 cir->callback(cir->port, ROI, 0);
96 }
97
98 if ((int_flag & RPE) && cir->callback)
99 {
100 cir->callback(cir->port, RA, 0);
101 cir->callback(cir->port, RPE, 0);
102 }
103
104 return 0;
105 }
106
sunxi_cir_mode_enable(cir_port_t port,uint8_t enable)107 void sunxi_cir_mode_enable(cir_port_t port, uint8_t enable)
108 {
109 sunxi_cir_t *cir = &sunxi_cir[port];
110 int reg_val;
111
112 if (!cir->status)
113 return ;
114
115 reg_val = readl(cir->base + CIR_CTRL);
116
117 if (enable)
118 reg_val |= CIR_ENABLE;
119 else
120 reg_val &= ~CIR_ENABLE;
121
122 writel(reg_val, cir->base + CIR_CTRL);
123 }
124
sunxi_cir_mode_config(cir_port_t port,cir_mode_t mode)125 void sunxi_cir_mode_config(cir_port_t port, cir_mode_t mode)
126 {
127 sunxi_cir_t *cir = &sunxi_cir[port];
128 int reg_val;
129
130 if (!cir->status)
131 return ;
132
133 reg_val = readl(cir->base + CIR_CTRL);
134
135 reg_val &= ~CIR_MODE;
136 reg_val |= (mode << CIR_MODE_OFFSET);
137
138 writel(reg_val, cir->base + CIR_CTRL);
139 }
140
sunxi_cir_sample_clock_select(cir_port_t port,cir_sample_clock_t div)141 void sunxi_cir_sample_clock_select(cir_port_t port, cir_sample_clock_t div)
142 {
143 sunxi_cir_t *cir = &sunxi_cir[port];
144 int reg_val = 0;
145
146 if (!cir->status)
147 return ;
148
149 reg_val = readl(cir->base + CIR_CONFIG);
150
151 if (div == CIR_CLK) {
152 reg_val &= ~SCS;
153 reg_val |= SCS2;
154 } else {
155 reg_val &= ~SCS2;
156 reg_val |= div;
157 }
158
159 writel(reg_val, cir->base + CIR_CONFIG);
160 }
161
sunxi_cir_sample_noise_threshold(cir_port_t port,int8_t threshold)162 void sunxi_cir_sample_noise_threshold(cir_port_t port, int8_t threshold)
163 {
164 sunxi_cir_t *cir = &sunxi_cir[port];
165 int reg_val = 0;
166
167 if (!cir->status || threshold > 0x3f)
168 return ;
169
170 reg_val = readl(cir->base + CIR_CONFIG);
171
172 reg_val &= ~NTHR;
173 reg_val |= (threshold << NTHR_OFFSET);
174
175 writel(reg_val, cir->base + CIR_CONFIG);
176 }
177
sunxi_cir_sample_idle_threshold(cir_port_t port,int8_t threshold)178 void sunxi_cir_sample_idle_threshold(cir_port_t port, int8_t threshold)
179 {
180 sunxi_cir_t *cir = &sunxi_cir[port];
181 int reg_val = 0;
182
183 if (!cir->status || threshold > 0x3f)
184 return ;
185
186 reg_val = readl(cir->base + CIR_CONFIG);
187
188 reg_val &= ~ITHR;
189 reg_val |= (threshold << ITHR_OFFSET);
190
191 writel(reg_val, cir->base + CIR_CONFIG);
192 }
193
sunxi_cir_sample_active_threshold(cir_port_t port,int8_t threshold)194 void sunxi_cir_sample_active_threshold(cir_port_t port, int8_t threshold)
195 {
196 sunxi_cir_t *cir = &sunxi_cir[port];
197 int reg_val = 0;
198
199 if (!cir->status || threshold > 0x3f)
200 return ;
201
202 reg_val = readl(cir->base + CIR_CONFIG);
203
204 reg_val &= ~ATHR;
205 reg_val |= (threshold << ATHR_OFFSET);
206
207 writel(reg_val, cir->base + CIR_CONFIG);
208 }
209
sunxi_cir_sample_active_thrctrl(cir_port_t port,int8_t enable)210 void sunxi_cir_sample_active_thrctrl(cir_port_t port, int8_t enable)
211 {
212 sunxi_cir_t *cir = &sunxi_cir[port];
213 int reg_val = 0;
214
215 if (!cir->status)
216 return ;
217
218 reg_val = readl(cir->base + CIR_CONFIG);
219
220 if (enable)
221 reg_val |= ATHC;
222 else
223 reg_val &= ~ATHC;
224
225 writel(reg_val, cir->base + CIR_CONFIG);
226 }
227
sunxi_cir_fifo_level(cir_port_t port,int8_t size)228 void sunxi_cir_fifo_level(cir_port_t port, int8_t size)
229 {
230 sunxi_cir_t *cir = &sunxi_cir[port];
231 int reg_val = 0;
232
233 if (!cir->status || size > 0x3f + 1)
234 return ;
235
236 reg_val = readl(cir->base + CIR_RXINT);
237
238 reg_val &= ~RAL;
239 reg_val |= ((size -1) << RAL_OFFSET);
240
241 writel(reg_val, cir->base + CIR_RXINT);
242 }
243
sunxi_cir_irq_enable(cir_port_t port,int enable)244 void sunxi_cir_irq_enable(cir_port_t port, int enable)
245 {
246 sunxi_cir_t *cir = &sunxi_cir[port];
247 int reg_val = 0;
248
249 if (!cir->status)
250 return ;
251
252 reg_val = readl(cir->base + CIR_RXINT);
253
254 reg_val &= ~IRQ_MASK;
255 enable &= IRQ_MASK;
256 reg_val |= enable;
257
258 writel(reg_val, cir->base + CIR_RXINT);
259 }
260
sunxi_cir_irq_disable(cir_port_t port)261 void sunxi_cir_irq_disable(cir_port_t port)
262 {
263 sunxi_cir_t *cir = &sunxi_cir[port];
264 int reg_val = 0;
265
266 if (!cir->status)
267 return ;
268
269 reg_val = readl(cir->base + CIR_RXINT);
270
271 reg_val &= ~IRQ_MASK;
272
273 writel(reg_val, cir->base + CIR_RXINT);
274 }
275
sunxi_cir_signal_invert(cir_port_t port,uint8_t invert)276 void sunxi_cir_signal_invert(cir_port_t port, uint8_t invert)
277 {
278 sunxi_cir_t *cir = &sunxi_cir[port];
279 int reg_val = 0;
280
281 if (!cir->status)
282 return ;
283
284 reg_val = readl(cir->base + CIR_RXCTRL);
285
286 if (invert)
287 reg_val |= RPPI;
288 else
289 reg_val &= ~RPPI;
290
291 writel(reg_val, cir->base + CIR_RXCTRL);
292 }
293
sunxi_cir_module_enable(cir_port_t port,int8_t enable)294 void sunxi_cir_module_enable(cir_port_t port, int8_t enable)
295 {
296 sunxi_cir_t *cir = &sunxi_cir[port];
297 int reg_val = 0;
298
299 if (!cir->status)
300 return ;
301
302 reg_val = readl(cir->base + CIR_CTRL);
303
304 if (enable)
305 reg_val |= (GEN | RXEN);
306 else
307 reg_val &= ~(GEN | RXEN);
308
309 writel(reg_val, cir->base + CIR_CTRL);
310 }
311
sunxi_cir_gpio_init(sunxi_cir_t * cir)312 static int sunxi_cir_gpio_init(sunxi_cir_t *cir)
313 {
314
315 user_gpio_set_t irpin = {0};
316 cir_gpio_t pin_cir;
317
318 Hal_Cfg_GetKeyValue("cir", "cir_pin", (int32_t *)&irpin, (sizeof(user_gpio_set_t) + 3) / sizeof(int));
319
320 pin_cir.gpio = (irpin.port - 1) * PINS_PER_BANK + irpin.port_num;
321
322 pin_cir.enable_mux = irpin.mul_sel;
323 pin_cir.disable_mux = 0;
324
325 return hal_gpio_pinmux_set_function(pin_cir.gpio, pin_cir.enable_mux);
326
327 }
328
sunxi_cir_gpio_exit(sunxi_cir_t * cir)329 static int sunxi_cir_gpio_exit(sunxi_cir_t *cir)
330 {
331 cir_gpio_t *cir_pin = cir->pin;
332
333 return hal_gpio_pinmux_set_function(cir_pin->gpio, cir_pin->disable_mux);
334 }
335
336 #if defined(CONFIG_ARCH_SUN20IW1)
sunxi_cir_clk_init(sunxi_cir_t * cir)337 static int sunxi_cir_clk_init(sunxi_cir_t *cir)
338 {
339 int ret = 0;
340
341 cir->cir_clk_type_R = HAL_SUNXI_R_CCU;
342 cir->cir_clk_type_FIXED = HAL_SUNXI_FIXED_CCU;
343
344 cir->m_clk_id = CLK_R_APB0_IRRX;
345 cir->p_clk_id = CLK_SRC_HOSC24M;
346 cir->b_clk_id = CLK_R_APB0_BUS_IRRX;
347
348 cir->mclk = hal_clock_get(cir->cir_clk_type_R, cir->m_clk_id);
349 if (hal_clock_enable(cir->mclk)) {
350 CIR_ERR("cir mclk enabled failed\n");
351 return -1;
352 }
353
354 cir->pclk = hal_clock_get(cir->cir_clk_type_FIXED, cir->p_clk_id);
355 if (hal_clock_enable(cir->pclk)) {
356 CIR_ERR("cir pclk enabled failed\n");
357 return -1;
358 }
359
360 cir->bclk = hal_clock_get(cir->cir_clk_type_R, cir->b_clk_id);
361 if (hal_clock_enable(cir->bclk)) {
362 CIR_ERR("cir bclk enabled failed\n");
363 return -1;
364 }
365
366 ret = hal_clk_set_parent(cir->mclk, cir->pclk);
367 if (ret) {
368 printf("hal_clk_set_parent failed\n");
369 return -1;
370 }
371
372 hal_reset_type_t cir_reset_type = HAL_SUNXI_R_RESET;
373 hal_reset_id_t cir_reset_id = RST_R_APB0_BUS_IRRX;
374
375 cir->cir_reset = hal_reset_control_get(cir_reset_type, cir_reset_id);
376 if (hal_reset_control_deassert(cir->cir_reset)) {
377 CIR_ERR("cir reset deassert failed\n");
378 return -1;
379 }
380
381 return 0;
382 }
383 #else
sunxi_cir_clk_init(sunxi_cir_t * cir)384 static int sunxi_cir_clk_init(sunxi_cir_t *cir)
385 {
386 int ret = 0;
387 int TEST_CLK_TYPE = 1;
388 int TEST_CLK_DATA = 1;
389 int TEST_RESET_TYPE = 1;
390 int TEST_RESET_DATA = 1;
391
392 cir->test_clk_type = TEST_CLK_TYPE;
393
394 cir->test_clk_id = TEST_CLK_DATA;
395
396 cir->test_clk = hal_clock_get(cir->test_clk_type, cir->test_clk_id);
397 if (hal_clock_enable(cir->test_clk)) {
398 CIR_ERR("cir TEST_CLK enabled failed\n");
399 return -1;
400 }
401
402 hal_reset_type_t cir_reset_type = TEST_RESET_TYPE;
403 hal_reset_id_t cir_reset_id = TEST_RESET_DATA;
404
405 cir->cir_reset = hal_reset_control_get(cir_reset_type, cir_reset_id);
406 if (hal_reset_control_deassert(cir->cir_reset)) {
407 CIR_ERR("cir reset deassert failed\n");
408 return -1;
409 }
410
411 return 0;
412 }
413
414 #endif
415
416 #if defined(CONFIG_ARCH_SUN20IW1)
sunxi_cir_clk_exit(sunxi_cir_t * cir)417 static int sunxi_cir_clk_exit(sunxi_cir_t *cir)
418 {
419 hal_clock_disable(cir->bclk);
420 hal_clock_put(cir->bclk);
421
422 hal_clock_disable(cir->pclk);
423 hal_clock_put(cir->pclk);
424
425 hal_clock_disable(cir->mclk);
426 hal_clock_put(cir->mclk);
427
428 hal_reset_control_assert(cir->cir_reset);
429 hal_reset_control_put(cir->cir_reset);
430
431 return 0;
432 }
433 #else
434
sunxi_cir_clk_exit(sunxi_cir_t * cir)435 static int sunxi_cir_clk_exit(sunxi_cir_t *cir)
436 {
437 hal_clock_disable(cir->test_clk);
438 hal_clock_put(cir->test_clk);
439
440 hal_reset_control_assert(cir->cir_reset);
441 hal_reset_control_put(cir->cir_reset);
442
443 return 0;
444 }
445 #endif
sunxi_cir_hw_init(sunxi_cir_t * cir)446 static cir_status_t sunxi_cir_hw_init(sunxi_cir_t *cir)
447 {
448 if (sunxi_cir_clk_init(cir))
449 return CIR_CLK_ERR;
450
451 if (sunxi_cir_gpio_init(cir))
452 return CIR_PIN_ERR;
453
454 if (request_irq(cir->irq, sunxi_cir_handler, 0, "cir-irq", cir)) {
455 printf("cir request irq err\n");
456 return CIR_IRQ_ERR;
457 }
458 enable_irq(cir->irq);
459
460 return CIR_OK;
461 }
462
sunxi_cir_hw_exit(sunxi_cir_t * cir)463 static void sunxi_cir_hw_exit(sunxi_cir_t *cir)
464 {
465 disable_irq(cir->irq);
466 free_irq(cir->irq, cir);
467 sunxi_cir_gpio_exit(cir);
468 sunxi_cir_clk_exit(cir);
469 }
470
471 #ifdef CONFIG_STANDBY
sunxi_cir_suspend(cir_port_t port)472 void sunxi_cir_suspend(cir_port_t port)
473 {
474 sunxi_cir_t *cir = &sunxi_cir[port];
475 disable_irq(cir->irq);
476 hal_clock_disable(cir->bclk);
477 hal_clock_disable(cir->mclk);
478 sunxi_cir_gpio_exit(cir);
479
480 return;
481 }
482
sunxi_cir_resume(cir_port_t port)483 void sunxi_cir_resume(cir_port_t port)
484 {
485 sunxi_cir_t *cir = &sunxi_cir[port];
486 sunxi_cir_gpio_init(cir);
487 sunxi_cir_clk_init(cir);
488
489 enable_irq(cir->irq);
490
491 return;
492 }
493 #endif
494
sunxi_cir_init(cir_port_t port)495 cir_status_t sunxi_cir_init(cir_port_t port)
496 {
497 sunxi_cir_t *cir = &sunxi_cir[port];
498 cir_status_t ret = 0;
499
500 cir->port = port;
501 cir->base = base[port];
502 cir->irq = irq[port];
503 cir->pin = &pin[port];
504 cir->status = 1;
505
506 ret = sunxi_cir_hw_init(cir);
507 if (ret)
508 {
509 CIR_ERR("cir[%d] hardware init error, ret:%d\n", port, ret);
510 return ret;
511 }
512
513 return ret;
514 }
515
sunxi_cir_deinit(cir_port_t port)516 void sunxi_cir_deinit(cir_port_t port)
517 {
518 sunxi_cir_t *cir = &sunxi_cir[port];
519 cir->status = 0;
520
521 sunxi_cir_hw_exit(cir);
522 }
523