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