176 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			176 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /*
 | ||
|  |  * Copyright (c) 2006-2023, RT-Thread Development Team | ||
|  |  * | ||
|  |  * SPDX-License-Identifier: Apache-2.0 | ||
|  |  * | ||
|  |  * Change Logs: | ||
|  |  * Date           Author       Notes | ||
|  |  * 2023-01-30     GuEe-GUI     first version | ||
|  |  */ | ||
|  | 
 | ||
|  | #include <rthw.h>
 | ||
|  | #include <rtthread.h>
 | ||
|  | 
 | ||
|  | #define DBG_TAG "pic.gic*"
 | ||
|  | #define DBG_LVL DBG_LOG
 | ||
|  | #include <rtdbg.h>
 | ||
|  | 
 | ||
|  | #include <drivers/pic.h>
 | ||
|  | 
 | ||
|  | #include "pic-gicv2.h"
 | ||
|  | #include "pic-gic-common.h"
 | ||
|  | 
 | ||
|  | void gic_common_init_quirk_ofw(const struct rt_ofw_node *ic_np, const struct gic_quirk *quirks, void *data) | ||
|  | { | ||
|  |     for (; quirks->desc; ++quirks) | ||
|  |     { | ||
|  |         if (!quirks->compatible || !rt_ofw_node_is_compatible(ic_np, quirks->compatible)) | ||
|  |         { | ||
|  |             continue; | ||
|  |         } | ||
|  | 
 | ||
|  |         RT_ASSERT(quirks->init != RT_NULL); | ||
|  | 
 | ||
|  |         if (!quirks->init(data)) | ||
|  |         { | ||
|  |             LOG_I("Enable workaround for %s", quirks->desc); | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | void gic_common_init_quirk_hw(rt_uint32_t iidr, const struct gic_quirk *quirks, void *data) | ||
|  | { | ||
|  |     for (; quirks->desc; ++quirks) | ||
|  |     { | ||
|  |         if (quirks->compatible) | ||
|  |         { | ||
|  |             continue; | ||
|  |         } | ||
|  | 
 | ||
|  |         if (quirks->iidr == (iidr & quirks->iidr_mask)) | ||
|  |         { | ||
|  |             RT_ASSERT(quirks->init != RT_NULL); | ||
|  | 
 | ||
|  |             if (!quirks->init(data)) | ||
|  |             { | ||
|  |                 LOG_I("Enable workaround for %s", quirks->desc); | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | void gic_common_sgi_config(void *base, void *data, int irq_base) | ||
|  | { | ||
|  | #ifdef RT_USING_SMP
 | ||
|  |     if (irq_base < 2) | ||
|  |     { | ||
|  |         struct rt_pic_irq *pirq; | ||
|  | 
 | ||
|  | #define DECLARE_GIC_IPI(ipi, hwirq)             \
 | ||
|  |         rt_pic_config_ipi(data, ipi, hwirq);    \ | ||
|  |         pirq = rt_pic_find_ipi(data, ipi);      \ | ||
|  |         pirq->mode = RT_IRQ_MODE_EDGE_RISING;   \ | ||
|  | 
 | ||
|  |         DECLARE_GIC_IPI(RT_SCHEDULE_IPI, 0); | ||
|  |         DECLARE_GIC_IPI(RT_STOP_IPI, 1); | ||
|  | 
 | ||
|  | #undef DECLARE_GIC_IPI
 | ||
|  |     } | ||
|  | #endif /* RT_USING_SMP */
 | ||
|  | } | ||
|  | 
 | ||
|  | rt_err_t gic_common_configure_irq(void *base, int irq, rt_uint32_t mode, void (*sync_access)(void *), void *data) | ||
|  | { | ||
|  |     rt_err_t err = RT_EOK; | ||
|  |     rt_ubase_t level; | ||
|  |     rt_uint32_t val, oldval; | ||
|  |     rt_uint32_t confoff = (irq / 16) * 4; | ||
|  |     rt_uint32_t confmask = 0x2 << ((irq % 16) * 2); | ||
|  |     static struct rt_spinlock ic_lock = { 0 }; | ||
|  | 
 | ||
|  |     level = rt_spin_lock_irqsave(&ic_lock); | ||
|  | 
 | ||
|  |     val = oldval = HWREG32(base + confoff); | ||
|  | 
 | ||
|  |     if (mode & RT_IRQ_MODE_LEVEL_MASK) | ||
|  |     { | ||
|  |         /* Level-sensitive */ | ||
|  |         val &= ~confmask; | ||
|  |     } | ||
|  |     else if (mode & RT_IRQ_MODE_EDGE_BOTH) | ||
|  |     { | ||
|  |         /* Edge-triggered */ | ||
|  |         val |= confmask; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (val != oldval) | ||
|  |     { | ||
|  |         HWREG32(base + confoff) = val; | ||
|  | 
 | ||
|  |         if (HWREG32(base + confoff) != val) | ||
|  |         { | ||
|  |             err = -RT_EINVAL; | ||
|  |         } | ||
|  |         if (sync_access) | ||
|  |         { | ||
|  |             sync_access(data); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     rt_spin_unlock_irqrestore(&ic_lock, level); | ||
|  | 
 | ||
|  |     return err; | ||
|  | } | ||
|  | 
 | ||
|  | void gic_common_dist_config(void *base, int max_irqs, void (*sync_access)(void *), void *data) | ||
|  | { | ||
|  |     rt_uint32_t i; | ||
|  | 
 | ||
|  |     /* Set all global interrupts to be level triggered, active low. */ | ||
|  |     for (i = 32; i < max_irqs; i += 16) | ||
|  |     { | ||
|  |         HWREG32(base + GIC_DIST_CONFIG + i / 4) = GICD_INT_ACTLOW_LVLTRIG; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* Set priority on all global interrupts. */ | ||
|  |     for (i = 32; i < max_irqs; i += 4) | ||
|  |     { | ||
|  |         HWREG32(base + GIC_DIST_PRI + i * 4 / 4) = GICD_INT_DEF_PRI_X4; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* Disable all SPIs. */ | ||
|  |     for (i = 32; i < max_irqs; i += 32) | ||
|  |     { | ||
|  |         HWREG32(base + GIC_DIST_ACTIVE_CLEAR + i / 8) = GICD_INT_EN_CLR_X32; | ||
|  |         HWREG32(base + GIC_DIST_ENABLE_CLEAR + i / 8) = GICD_INT_EN_CLR_X32; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (sync_access) | ||
|  |     { | ||
|  |         sync_access(data); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | void gic_common_cpu_config(void *base, int nr, void (*sync_access)(void *), void *data) | ||
|  | { | ||
|  |     rt_uint32_t i; | ||
|  | 
 | ||
|  |     /* Disable all SGIs, PPIs. */ | ||
|  |     for (i = 0; i < nr; i += 32) | ||
|  |     { | ||
|  |         HWREG32(base + GIC_DIST_ACTIVE_CLEAR + i / 8) = GICD_INT_EN_CLR_X32; | ||
|  |         HWREG32(base + GIC_DIST_ENABLE_CLEAR + i / 8) = GICD_INT_EN_CLR_X32; | ||
|  |     } | ||
|  | 
 | ||
|  |     /* Set priority on all PPI and SGI. */ | ||
|  |     for (i = 0; i < nr; i += 4) | ||
|  |     { | ||
|  |         HWREG32(base + GIC_DIST_PRI + i * 4 / 4) = GICD_INT_DEF_PRI_X4; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (sync_access) | ||
|  |     { | ||
|  |         sync_access(data); | ||
|  |     } | ||
|  | } |