diff --git a/NOTES b/NOTES new file mode 100644 index 0000000000000000000000000000000000000000..429d9f4ce8a6ae9e20d4456325a5d7c206c8c6fe --- /dev/null +++ b/NOTES @@ -0,0 +1,27 @@ +General +======= + +Patches and improvements are always welcome. +Please consider that I barely had 3 months to prepare all of this from start to +finish, so some things may not be particularly smart or pretty, or even +complete. + + +MPPB (v2) +======= + +* NoC DMA channels silently fail and can not be reset when source and + destination of a transfer falls programmed within the same memory + (this may not be true, I have not tried programming zero-sized transfers yet) + +* There is no atomic mechanism that protects against concurrent programming + of NoC DMA channels. This means that a Xentium needs to either reserve one + or more channels for a time, or all channels must be assigned beforehand + +* there is no MMU + +Xentium +======= + +* Xentium-Clang cannot produce position independent executables, which makes + memory management less comfortable diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 8a327cc3b73a5593e2e9689d38c87eb077238583..71857b9b20fb9137489de75438d5e4dc2d8f3838 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -2,6 +2,22 @@ menu "SPARC Configuration" ### Arch specific settings +choice + prompt "Select CPU type" + default LEON3 + +config LEON2 + bool "LEON2" + help + Configure for LEON2. + +config LEON3 + bool "LEON3" + help + Configure for LEON2. + +endchoice + config MMU bool "Enable SPARC Reference MMU support" diff --git a/arch/sparc/include/asm/irq.h b/arch/sparc/include/asm/irq.h new file mode 100644 index 0000000000000000000000000000000000000000..4c3394ec03ebd370e0f4f6c682aca77325864192 --- /dev/null +++ b/arch/sparc/include/asm/irq.h @@ -0,0 +1,28 @@ +/** + * @file arch/sparc/include/irq.h + */ + +#ifndef _SPARC_IRQ_H_ +#define _SPARC_IRQ_H_ + +#include <asm/leon_reg.h> + + +void leon_irq_init(void); + + +/* in the LEON3, interrupts 1-15 are primary, 16-31 are extended */ +#ifdef CONFIG_LEON3 +#define LEON_WANT_EIRQ(x) (x) +#define LEON_REAL_EIRQ(x) (x) +#endif /* CONFIG_LEON3 */ + +/* in the LEON2, interrupts 1-15 are primary, 0-31 are extended, we treat them + * as IRLs 16...n */ +#ifdef CONFIG_LEON2 +#define LEON_WANT_EIRQ(x) (LEON2_IRL_SIZE + (x)) +#define LEON_REAL_EIRQ(x) ((x) - LEON2_IRL_SIZE) +#endif /* CONFIG_LEON2 */ + + +#endif /* _SPARC_IRQ_H_ */ diff --git a/arch/sparc/include/asm/leon_reg.h b/arch/sparc/include/asm/leon_reg.h new file mode 100644 index 0000000000000000000000000000000000000000..92ed092cf8d691af7a28dde819ae4d86fb21e9e6 --- /dev/null +++ b/arch/sparc/include/asm/leon_reg.h @@ -0,0 +1,190 @@ +/** + * @file arch/sparc/include/asm/leon_reg.h + * + * @author Armin Luntzer (armin.luntzer@univie.ac.at) + * @date 2015 + * + * @copyright GPLv2 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * + */ + +#ifndef _SPARC_ASM_LEON_REG_H_ +#define _SPARC_ASM_LEON_REG_H_ + +#include <kernel/types.h> + + + +#define LEON3_BASE_ADDRESS_APB 0x80000000 + +#define LEON3_BASE_ADDRESS_FTMCTRL 0x80000000 +#define LEON3_BASE_ADDRESS_APBUART 0x80000100 +#define LEON3_BASE_ADDRESS_IRQMP 0x80000200 +#define LEON3_BASE_ADDRESS_GPTIMER 0x80000300 +#define LEON3_BASE_ADDRESS_GRGPIO_1 0x80000900 +#define LEON3_BASE_ADDRESS_GRGPIO_2 0x80000A00 +#define LEON3_BASE_ADDRESS_AHBSTAT 0x80000F00 +#define LEON3_BASE_ADDRESS_GRTIMER 0x80100600 + +#define LEON3_IRL_SIZE 16 /* number of interrupts on primary */ +#define LEON3_EIRL_SIZE 16 /* number of interrupts on extended */ + + + + + +#define LEON2_BASE_ADDRESS_IRQ 0x80000090 +#define LEON2_BASE_ADDRESS_EIRQ 0x800000B0 + +#define LEON2_IRL_SIZE 16 /* number of interrupts on primary */ +#define LEON2_EIRL_SIZE 32 /* number of interrupts on extended */ + + + +struct leon2_irqctrl_registermap { + uint32_t irq_mask; + uint32_t irq_pending; + uint32_t irq_force; + uint32_t irq_clear; +}; + +struct leon2_eirqctrl_registermap { + uint32_t eirq_mask; + uint32_t eirq_pending; + uint32_t eirq_status; + uint32_t eirq_clear; +}; + + + + + +/* FTMCTRL Memory Controller Registers [p. 44] */ + +struct leon3_ftmctrl_registermap { + uint32_t mcfg1; + uint32_t mcfg2; + uint32_t mcfg3; +}; + +struct leon3_apbuart_registermap { + uint32_t data; + uint32_t status; + uint32_t ctrl; + uint32_t scaler; +}; + +struct leon3_grgpio_registermap { + uint32_t ioport_data; + uint32_t ioport_output_value; + uint32_t ioport_direction; + uint32_t irq_mask; + uint32_t irq_polarity; + uint32_t irq_edge; +}; + +struct leon3_irqctrl_registermap { + uint32_t irq_level; /* 0x00 */ + uint32_t irq_pending; /* 0x04 */ + uint32_t irq_force; /* 0x08 */ + uint32_t irq_clear; /* 0x0C */ + uint32_t mp_status; /* 0x10 */ + uint32_t mp_broadcast; /* 0x14 */ + uint32_t unused01; /* 0x18 */ + uint32_t unused02; /* 0x1C */ + uint32_t unused03; /* 0x20 */ + uint32_t unused04; /* 0x24 */ + uint32_t unused05; /* 0x28 */ + uint32_t unused06; /* 0x2C */ + uint32_t unused07; /* 0x30 */ + uint32_t unused08; /* 0x34 */ + uint32_t unused09; /* 0x38 */ + uint32_t unused10; /* 0x3C */ + uint32_t irq_mpmask[2]; /* 0x40 CPU 0 */ + /* 0x44 CPU 1 */ + uint32_t unused11; /* 0x48 */ + uint32_t unused12; /* 0x4C */ + uint32_t unused13; /* 0x50 */ + uint32_t unused14; /* 0x54 */ + uint32_t unused15; /* 0x58 */ + uint32_t unused16; /* 0x5C */ + uint32_t unused17; /* 0x60 */ + uint32_t unused18; /* 0x64 */ + uint32_t unused19; /* 0x68 */ + uint32_t unused20; /* 0x6C */ + uint32_t unused21; /* 0x70 */ + uint32_t unused22; /* 0x74 */ + uint32_t unused23; /* 0x78 */ + uint32_t unused24; /* 0x7C */ + uint32_t irq_mpforce[2]; /* 0x80 CPU 0*/ + /* 0x84 CPU 1*/ + uint32_t unused25; /* 0x88 */ + uint32_t unused26; /* 0x8C */ + uint32_t unused27; /* 0x90 */ + uint32_t unused28; /* 0x94 */ + uint32_t unused29; /* 0x98 */ + uint32_t unused30; /* 0x9C */ + uint32_t unused31; /* 0xA0 */ + uint32_t unused32; /* 0xA4 */ + uint32_t unused33; /* 0xA8 */ + uint32_t unused34; /* 0xAC */ + uint32_t unused35; /* 0xB0 */ + uint32_t unused36; /* 0xB4 */ + uint32_t unused37; /* 0xB8 */ + uint32_t unused38; /* 0xBC */ + uint32_t extended_irq_id[2]; /* 0xC0 CPU 0*/ + /* 0xC4 CPU 1*/ +}; + + + +struct leon3_ahbstat_registermap { + uint32_t status; + uint32_t failing_address; +}; + + + +struct gptimer_timer { + uint32_t value; + uint32_t reload; + uint32_t ctrl; + uint32_t unused; +}; + +struct gptimer_unit { + uint32_t scaler; + uint32_t scaler_reload; + uint32_t config; + uint32_t unused; + struct gptimer_timer timer[4]; +}; + +struct grtimer_timer { + uint32_t value; + uint32_t reload; + uint32_t ctrl; + uint32_t latch_value; +}; + +struct grtimer_unit { + uint32_t scaler; + uint32_t scaler_reload; + uint32_t config; + + uint32_t irq_select; + + struct grtimer_timer timer[2]; +}; + +#endif /* _SPARC_ASM_LEON_REG_H_ */ diff --git a/arch/sparc/include/asm/spinlock.h b/arch/sparc/include/asm/spinlock.h new file mode 100644 index 0000000000000000000000000000000000000000..25c104948b8023aca44775b743b68856570bbeef --- /dev/null +++ b/arch/sparc/include/asm/spinlock.h @@ -0,0 +1,212 @@ +/** + * @file spinlock.h + * @author Armin Luntzer (armin.luntzer@univie.ac.at) + * @date December, 2013 + * @brief spin locking + * + * @copyright Armin Luntzer (armin.luntzer@univie.ac.at) + * @copyright David S. Miller (davem@caip.rutgers.edu), 1997 (some parts) + * + * @copyright + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef _ARCH_SPARC_ASM_SPINLOCK_H_ +#define _ARCH_SPARC_ASM_SPINLOCK_H_ + +#include <compiler.h> +#include <kernel/types.h> + +#define __SPINLOCK +struct spinlock { + uint8_t lock; + uint32_t lock_recursion; +}; + + +/* the sparc PIL field */ +#define PSR_PIL 0x00000f00 + + + +/** + * @brief save and disable the processor interrupt level state + * @return PSR + * @warning make sure to call a save/restore pair from within the same stack frame + */ + +#define __spin_lock_save_irq __spin_lock_save_irq +__attribute__((unused)) +static uint32_t spin_lock_save_irq(void) +{ + uint32_t psr; + uint32_t tmp; + + + __asm__ __volatile__( + "rd %%psr, %0 \n\t" + "or %0, %2, %1 \n\t" + "wr %1, 0, %%psr \n\t" + "nop \n\t" + "nop \n\t" + "nop \n\t" + : "=&r" (psr), "=r" (tmp) + : "i" (PSR_PIL) + : "memory"); + return psr; +} + + +/** + * @brief Restore the processor interrupt level state + */ + +#define __spin_lock_restore_irq __spin_lock_restore_irq +__attribute__((unused)) +static void spin_lock_restore_irq(uint32_t psr) +{ + uint32_t tmp; + + __asm__ __volatile__( + "rd %%psr, %0 \n\t" + "and %2, %1, %2 \n\t" + "andn %0, %1, %0 \n\t" + "wr %0, %2, %%psr \n\t" + "nop \n\t" + "nop \n\t" + "nop \n\t" + : "=&r" (tmp) + : "i" (PSR_PIL), "r" (psr) + : "memory"); +} + + +/** + * @brief MPPB LEON-side spin lock + * @param lock a struct spinlock + * + * @warning will silently fail AND deadlock everytime YOU are stupid + * @note it is, however, save to use with interrupts (sort of) + */ + +#define __spin_lock __spin_lock +__attribute__((unused)) +static void spin_lock(struct spinlock *lock) +{ + uint32_t psr_flags; + + if (unlikely(lock->lock_recursion)) + return; + + psr_flags = spin_lock_save_irq(); + + lock->lock_recursion = 1; + + __asm__ __volatile__( + "1: \n\t" + "ldstub [%0], %%g2 \n\t" + "andcc %%g2, %%g2, %%g2 \n\t" + "bnz,a 1b \n\t" + " nop \n\n" + : + : "r" (&lock->lock) + : "g2", "memory", "cc"); + + lock->lock_recursion = 0; + + spin_lock_restore_irq(psr_flags); +} + + +/** + * @brief spin lock which does not care about interrupts + * @param lock a struct spinlock + */ + +__attribute__((unused)) +static void spin_lock_raw(struct spinlock *lock) +{ + if (unlikely(lock->lock_recursion)) + return; + + lock->lock_recursion = 1; + + __asm__ __volatile__( + "1: \n\t" + "ldstub [%0], %%g2 \n\t" + "andcc %%g2, %%g2, %%g2 \n\t" + "bnz,a 1b \n\t" + " nop \n\n" + : + : "r" (&lock->lock) + : "g2", "memory", "cc"); + + lock->lock_recursion = 0; +} + +/** + * @brief lock check + * @returns success or failure + */ + +#define __spin_is_locked __spin_is_locked +__attribute__((unused)) +static int spin_is_locked(struct spinlock *lock) +{ + return (lock->lock != 0); +} + + +/** + * @brief spins forever until lock opens + */ + +#define __spin_unlock_wait __spin_unlock_wait +__attribute__((unused)) +static void spin_unlock_wait(struct spinlock *lock) +{ + barrier(); + + while(spin_is_locked(lock)); +} + +/** + * @brief spin lock + * @param lock a struct spinlock + * @return success or failure + */ + +#define __spin_try_lock __spin_try_lock +__attribute__((unused)) +static int spin_try_lock(struct spinlock *lock) +{ + uint32_t retval; + + __asm__ __volatile__("ldstub [%1], %0" : "=r" (retval) : "r" (&lock->lock) : "memory"); + + return (retval == 0); +} + +/** + * @brief side spin-unlock + * @param lock a struct spinlock + */ + +#define __spin_unlock __spin_unlock +__attribute__((unused)) +static void spin_unlock(struct spinlock *lock) +{ + __asm__ __volatile__("swap [%0], %%g0 \n\t" : : "r" (&lock->lock) : "memory"); +} + + + +#endif diff --git a/arch/sparc/include/srmmu.h b/arch/sparc/include/srmmu.h index 32683574a2ba060ccb99cb13934f0a7667923977..b62cc4d2902d663503f5f0ef3053a8c117cd7fd6 100644 --- a/arch/sparc/include/srmmu.h +++ b/arch/sparc/include/srmmu.h @@ -9,11 +9,7 @@ #define _SPARC_SRMMU_H_ -#include <stddef.h> - - -/* XXX: not here */ -#define __BIG_ENDIAN_BITFIELD __BIG_ENDIAN_BITFIELD +#include <kernel/types.h> #define SRMMU_CTRL_IMPL_MASK 0xf0000000 diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index c84383837c306aae1ce44316c320a3388f3a504b..5d760a657b2659e0da6fe3361ca29f84b35a9dd9 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile @@ -15,5 +15,6 @@ obj-y += stacktrace.o obj-y += stack.o obj-y += traps/data_access_exception_trap.o obj-y += traps/data_access_exception.o +obj-y += irq.o #libs-y += lib/ diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c new file mode 100644 index 0000000000000000000000000000000000000000..8a2aadb6286693deed5d76fae058755d701f5326 --- /dev/null +++ b/arch/sparc/kernel/irq.c @@ -0,0 +1,1062 @@ +/** + * @file arch/sparc/kernel/irq.c + * + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * @todo eventually replace catch_interrupt() libgloss/newlib functionality + * with local/custom code and rework the globals + * @todo irq configuration should only be done through a syscall, + * so traps are disabled + * + * @brief an IRQ manager that dispatches interrupts to registered + * handler functions + * + * Implements an interrupt handler that supports registration of a predefined + * arbitrary number of handler function per interrupt with immediate or deferred + * execution priorities. Callbacks are tracked in linked lists that should + * always be allocated in a contiguous block of memory for proper cache + * hit rate. + * + * @image html sparc/sparc_irq_manager.png "IRQ manager" + * + * @note this implements the processor specific IRQ logic + * + * @todo once we have fully functional AMBA bus scan, we might want to rework + * the static interrupt assignments... + * + * + * @note We do NOT cache-bypass read from interrupt control registers when + * handling interrupts. It should not be necessary and can + * potentially save a few cycles. + */ + + + + +#include <kernel/irq.h> +#include <kernel/kmem.h> +#include <kernel/printk.h> + +#include <errno.h> +#include <list.h> + +#include <asm/io.h> +#include <asm/leon.h> +#include <asm/leon_reg.h> +#include <asm/spinlock.h> +#include <asm/irq.h> + + +struct irl_vector_elem { + irq_handler_t handler; + enum isr_exec_priority priority; + void *data; + struct list_head handler_node; +}; + + +/* maximum number of interrupt handlers we will allow */ +#define IRL_POOL_SIZE 64 +/* the maximum queue size for deferred handlers */ +#define IRL_QUEUE_SIZE 64 + + + +#ifdef CONFIG_LEON2 +#define IRL_SIZE LEON2_IRL_SIZE +#define EIRL_SIZE LEON2_IRL_SIZE + +static struct leon2_irqctrl_registermap *leon_irqctrl_regs; +static struct leon2_eirqctrl_registermap *leon_eirqctrl_regs; + +#endif /* CONFIG_LEON2 */ + +#ifdef CONFIG_LEON3 + +#define IRL_SIZE LEON3_IRL_SIZE +#define EIRL_SIZE LEON3_IRL_SIZE + +static struct leon3_irqctrl_registermap *leon_irqctrl_regs; + +#endif /* CONFIG_LEON3 */ + + +static struct list_head irl_pool_head; +static struct list_head irl_queue_head; +static struct list_head irq_queue_pool_head; + +static struct list_head *irl_vector; +static struct list_head *eirl_vector; + +static struct irl_vector_elem *irl_pool; +static struct irl_vector_elem *irl_queue_pool; + + +static unsigned int leon_eirq; + + +#ifdef CONFIG_IRQ_STATS_COLLECT + +#include <kernel/string.h> +#include <kernel/sysctl.h> + +static struct { + uint32_t irl; + uint32_t eirl; + uint32_t irl_irq[IRL_SIZE]; + uint32_t eirl_irq[EIRL_SIZE]; +} irqstat; + + +static ssize_t irl_show(__attribute__((unused)) struct sysobj *sobj, + __attribute__((unused)) struct sobj_attribute *sattr, + char *buf) +{ + uint32_t irq; + + + if (!strcmp("irl", sattr->name)) + return sprintf(buf, "%lu", irqstat.irl); + + irq = atoi(sattr->name); + + if (irq > IRL_SIZE) + return 0; + + return sprintf(buf, "%lu", irqstat.irl_irq[irq]); +} + +static ssize_t irl_store(__attribute__((unused)) struct sysobj *sobj, + __attribute__((unused)) struct sobj_attribute *sattr, + __attribute__((unused)) const char *buf, + __attribute__((unused)) size_t len) +{ + uint32_t irq; + + + if (!strcmp("irl", sattr->name)) { + irqstat.irl = 0; + return 0; + } + + irq = atoi(sattr->name); + + if (irq > IRL_SIZE) + return 0; + + irqstat.irl_irq[irq] = 0; + + return 0; +} + +static ssize_t eirl_show(__attribute__((unused)) struct sysobj *sobj, + __attribute__((unused)) struct sobj_attribute *sattr, + char *buf) +{ + uint32_t irq; + + + if (!strcmp("eirl", sattr->name)) + return sprintf(buf, "%lu", irqstat.eirl); + + irq = atoi(sattr->name); + + if (irq > EIRL_SIZE) + return 0; + + return sprintf(buf, "%lu", irqstat.eirl_irq[irq]); +} + +static ssize_t eirl_store(__attribute__((unused)) struct sysobj *sobj, + __attribute__((unused)) struct sobj_attribute *sattr, + __attribute__((unused)) const char *buf, + __attribute__((unused)) size_t len) +{ + uint32_t irq; + + + if (!strcmp("eirl", sattr->name)) { + irqstat.eirl = 0; + return 0; + } + + irq = atoi(sattr->name); + + if (irq > EIRL_SIZE) + return 0; + + irqstat.eirl_irq[irq] = 0; + + return 0; +} + +__extension__ +static struct sobj_attribute irl_attr[] = { + __ATTR(irl, irl_show, irl_store), + __ATTR(0, irl_show, irl_store), __ATTR(1, irl_show, irl_store), + __ATTR(2, irl_show, irl_store), __ATTR(3, irl_show, irl_store), + __ATTR(4, irl_show, irl_store), __ATTR(5, irl_show, irl_store), + __ATTR(6, irl_show, irl_store), __ATTR(7, irl_show, irl_store), + __ATTR(8, irl_show, irl_store), __ATTR(9, irl_show, irl_store), + __ATTR(10, irl_show, irl_store), __ATTR(11, irl_show, irl_store), + __ATTR(12, irl_show, irl_store), __ATTR(13, irl_show, irl_store), + __ATTR(14, irl_show, irl_store), __ATTR(15, irl_show, irl_store), + }; + +static struct sobj_attribute *irl_attributes[] = { + &irl_attr[0], &irl_attr[1], &irl_attr[2], &irl_attr[3], + &irl_attr[4], &irl_attr[5], &irl_attr[6], &irl_attr[7], + &irl_attr[8], &irl_attr[9], &irl_attr[10], &irl_attr[11], + &irl_attr[12], &irl_attr[13], &irl_attr[14], &irl_attr[15], + &irl_attr[16], NULL}; + +__extension__ +static struct sobj_attribute eirl_attr[] = { + __ATTR(eirl, eirl_show, eirl_store), + __ATTR(0, eirl_show, eirl_store), __ATTR(1, eirl_show, eirl_store), + __ATTR(2, eirl_show, eirl_store), __ATTR(3, eirl_show, eirl_store), + __ATTR(4, eirl_show, eirl_store), __ATTR(5, eirl_show, eirl_store), + __ATTR(6, eirl_show, eirl_store), __ATTR(7, eirl_show, eirl_store), + __ATTR(8, eirl_show, eirl_store), __ATTR(9, eirl_show, eirl_store), + __ATTR(10, eirl_show, eirl_store), __ATTR(11, eirl_show, eirl_store), + __ATTR(12, eirl_show, eirl_store), __ATTR(13, eirl_show, eirl_store), + __ATTR(14, eirl_show, eirl_store), __ATTR(15, eirl_show, eirl_store), +#ifdef CONFIG_LEON2 + __ATTR(16, eirl_show, eirl_store), __ATTR(17, eirl_show, eirl_store), + __ATTR(18, eirl_show, eirl_store), __ATTR(19, eirl_show, eirl_store), + __ATTR(20, eirl_show, eirl_store), __ATTR(21, eirl_show, eirl_store), + __ATTR(22, eirl_show, eirl_store), __ATTR(23, eirl_show, eirl_store), + __ATTR(24, eirl_show, eirl_store), __ATTR(25, eirl_show, eirl_store), + __ATTR(26, eirl_show, eirl_store), __ATTR(27, eirl_show, eirl_store), + __ATTR(28, eirl_show, eirl_store), __ATTR(29, eirl_show, eirl_store), + __ATTR(30, eirl_show, eirl_store), __ATTR(31, eirl_show, eirl_store), +#endif /* CONFIG_LEON2 */ +}; + +__extension__ +static struct sobj_attribute *eirl_attributes[] = { + &eirl_attr[0], &eirl_attr[1], &eirl_attr[2], &eirl_attr[3], + &eirl_attr[4], &eirl_attr[5], &eirl_attr[6], &eirl_attr[7], + &eirl_attr[8], &eirl_attr[9], &eirl_attr[10], &eirl_attr[11], + &eirl_attr[12], &eirl_attr[13], &eirl_attr[14], &eirl_attr[15], + &eirl_attr[16], + +#ifdef CONFIG_LEON2 + &eirl_attr[17], &eirl_attr[18], &eirl_attr[19], &eirl_attr[20], + &eirl_attr[21], &eirl_attr[22], &eirl_attr[23], &eirl_attr[24], + &eirl_attr[25], &eirl_attr[26], &eirl_attr[27], &eirl_attr[28], + &eirl_attr[29], &eirl_attr[30], &eirl_attr[31], &eirl_attr[32], +#endif /* CONFIG_LEON2 */ + NULL}; + +#endif /* CONFIG_IRQ_STATS_COLLECT */ + + +/* XXX we use BCC's libgloss implementation for now */ +extern int catch_interrupt (int func, int irq); + + +/** + * @brief clear (acknowledge) a pending IRQ + * + * @param irq the interrupt to clear + */ + +static void leon_clear_irq(unsigned int irq) +{ +#ifdef CONFIG_LEON3 + iowrite32be((1 << irq), &leon_irqctrl_regs->irq_clear); +#endif /* CONFIG_LEON3 */ + +#ifdef CONFIG_LEON2 + if (irq < IRL_SIZE) + iowrite32be((1 << irq), &leon_irqctrl_regs->irq_clear); + else if (irq < (IRL_SIZE + EIRL_SIZE)) +#ifdef CONFIG_MPPB /* what what??? O.o */ + iowrite32be((1 << LEON_REAL_EIRQ(irq)), + &leon_eirqctrl_regs->eirq_status); +#else + iowrite32be((1 << LEON_REAL_EIRQ(irq)), + &leon_eirqctrl_regs->eirq_clear); +#endif +#endif /* CONFIG_LEON2 */ +} + + +/** + * @brief unmask an IRQ + * + * @param irq the interrupt to unmask + * @param cpu the cpu for which the interrupt is to be unmasked + */ + +static void leon_unmask_irq(unsigned int irq, unsigned int cpu) +{ + uint32_t mask; + + +#ifdef CONFIG_LEON3 + mask = ioread32be(&leon_irqctrl_regs->irq_mpmask[cpu]); + mask |= (1 << irq); + iowrite32be(mask, &leon_irqctrl_regs->irq_mpmask[cpu]); +#endif /* CONFIG_LEON3 */ + +#ifdef CONFIG_LEON2 + if (irq < IRL_SIZE) { + mask = ioread32be(&leon_irqctrl_regs->irq_mask); + mask |= (1 << irq); + iowrite32be(mask, &leon_irqctrl_regs->irq_mask); + } else if (irq < (IRL_SIZE + EIRL_SIZE)) { + mask = ioread32be(&leon_eirqctrl_regs->eirq_mask); + mask |= (1 << LEON_REAL_EIRQ(irq)); + iowrite32be(mask, &leon_eirqctrl_regs->eirq_mask); + } +#endif /* CONFIG_LEON2 */ +} + + +/** + * @brief mask an IRQ + * + * @param irq the interrupt to mask + * @param cpu the cpu for which the interrupt is to be masked + */ + +static void leon_mask_irq(unsigned int irq, unsigned int cpu) +{ + uint32_t mask; + + +#ifdef CONFIG_LEON3 + mask = ioread32be(&leon_irqctrl_regs->irq_mpmask[cpu]); + + mask &= ~(1 << irq); + + iowrite32be(mask, &leon_irqctrl_regs->irq_mpmask[cpu]); +#endif /* CONFIG_LEON3 */ + +#ifdef CONFIG_LEON2 + if (irq < IRL_SIZE) { + mask = ioread32be(&leon_irqctrl_regs->irq_mask); + mask &= ~(1 << irq); + iowrite32be(mask, &leon_irqctrl_regs->irq_mask); + } else if (irq < (IRL_SIZE + EIRL_SIZE)) { + mask = ioread32be(&leon_eirqctrl_regs->eirq_mask); + mask &= ~(1 << LEON_REAL_EIRQ(irq)); + iowrite32be(mask, &leon_eirqctrl_regs->eirq_mask); + } +#endif /* CONFIG_LEON2 */ +} + +/** + * @brief enable an interrupt + * + * @param irq the interrupt to enable + * @param cpu the cpu for which the interrupt is to be enabled + */ + +static void leon_enable_irq(unsigned int irq, unsigned int cpu) +{ + leon_clear_irq(irq); + + leon_unmask_irq(irq, cpu); +} + + +/** + * @brief disable an interrupt + * + * @param irq the interrupt to disable + * @param cpu the cpu for which the interrupt is to be disabled + */ + +static void leon_disable_irq(unsigned int irq, unsigned int cpu) +{ + leon_clear_irq(irq); + + leon_mask_irq(irq, cpu); +} + + +/** + * @brief queue a handler for delayed exectuion + * + * @param p_elem a pointer to a struct IRQVectorElem + * + * @returns 0 on success + */ + +static int leon_irq_queue(struct irl_vector_elem *p_elem) +{ + uint32_t psr_flags; + struct irl_vector_elem *p_queue; + + if (unlikely(list_empty(&irq_queue_pool_head))) + return -EBUSY; + + + psr_flags = spin_lock_save_irq(); + + p_queue = list_entry((&irq_queue_pool_head)->next, + struct irl_vector_elem, handler_node); + + p_queue->handler = p_elem->handler; + p_queue->priority = p_elem->priority; + p_queue->data = p_elem->data; + + list_move_tail(&p_queue->handler_node, &irl_queue_head); + + spin_lock_restore_irq(psr_flags); + + return 0; +} + + +/** + * + * @brief execute deferred (low-priority) handlers + */ + +void leon_irq_queue_execute(void) +{ + struct irl_vector_elem *p_elem = NULL; + struct irl_vector_elem *p_tmp; + + + if (list_empty(&irl_queue_head)) + return; + + list_for_each_entry_safe(p_elem, p_tmp, + &irl_queue_head, handler_node) { + + list_del(&p_elem->handler_node); + + if (likely(p_elem->handler)) { + + if (p_elem->handler(p_elem->data)) + leon_irq_queue(p_elem); + else + list_add_tail(&p_elem->handler_node, + &irq_queue_pool_head); + + } else { + list_add_tail(&p_elem->handler_node, + &irq_queue_pool_head); + } + } +} + + +/** + * @brief he central interrupt handling routine + * + * @param irq an interrupt number + * + * @returns always 0 + * + * @note handler return codes ignored for now + * + * XXX maybe we want to keep acking IRQs as in eirq_dispatch... + */ + +static int leon_irq_dispatch(unsigned int irq) +{ + struct irl_vector_elem *p_elem; + + +#ifdef CONFIG_IRQ_STATS_COLLECT + irqstat.irl++; + irqstat.irl_irq[irq]++; +#endif /* CONFIG_IRQ_STATS_COLLECT */ + + list_for_each_entry(p_elem, &irl_vector[irq], handler_node) { + if (likely(p_elem->priority == ISR_PRIORITY_NOW)) + p_elem->handler(p_elem->data); + else if (leon_irq_queue(p_elem) < 0) + p_elem->handler(p_elem->data); + } + + return 0; +} + + +/** + * @brief the central interrupt handling routine for extended interrupts + * + * @param irq an extended interrupt number + * + * @returns always 0 + * + * @note handler return codes ignored for now + */ + +static int leon_eirq_dispatch(unsigned int irq) +{ + unsigned int eirq; +#ifdef CONFIG_LEON3 + unsigned int cpu; +#endif /* CONFIG_LEON3 */ + + struct irl_vector_elem *p_elem; + struct irl_vector_elem *p_tmp; + + +#ifdef CONFIG_IRQ_STATS_COLLECT + irqstat.irl++; + irqstat.irl_irq[irq]++; +#endif /* CONFIG_IRQ_STATS_COLLECT */ + +#ifdef CONFIG_LEON3 + cpu = leon3_cpuid(); +#endif /* CONFIG_LEON3 */ + + + /* keep acknowleding pending EIRQs */ + /* XXX this is a potential death trap :) */ + while (1) { + +#ifdef CONFIG_LEON3 + /* no pending EIRQs remain */ + if (!(leon_irqctrl_regs->irq_pending >> IRL_SIZE)) + break; + + eirq = leon_irqctrl_regs->extended_irq_id[cpu]; +#endif /* CONFIG_LEON3 */ + +#ifdef CONFIG_LEON2 + eirq = leon_eirqctrl_regs->eirq_status; + + + if (!(eirq & 0x20)) /* no pending EIRQs remain */ + break; +#endif /* CONFIG_LEON2 */ + + eirq &= 0x1F; /* get the actual EIRQ number */ + + leon_clear_irq(LEON_WANT_EIRQ(eirq)); + + +#ifdef CONFIG_IRQ_STATS_COLLECT + irqstat.eirl++; + irqstat.eirl_irq[eirq]++; +#endif /* CONFIG_IRQ_STATS_COLLECT */ + + list_for_each_entry_safe(p_elem, p_tmp, &eirl_vector[eirq], + handler_node) { + + /* deferred ISRs are executed immediately if + * queueing fails + */ + if (likely(p_elem->priority == ISR_PRIORITY_NOW)) + p_elem->handler(p_elem->data); + else if (leon_irq_queue(p_elem) < 0) + p_elem->handler(p_elem->data); + } + } + + + leon_clear_irq(irq); + + return 0; +} + + +/** + * @brief register a handler function to the primary interrupt line + * + * @param irq an interrupt number + * @param priority a handler priority (immediate or deferred) + * @param handler a handler function + * @param data a pointer to arbitrary user data; passed to + * handler function + * + * @returns 0 on success + */ + +int irl_register_handler(unsigned int irq, + enum isr_exec_priority priority, + irq_handler_t handler, + void *data) +{ + uint32_t psr_flags; + + struct irl_vector_elem *p_elem; + + + if (list_empty(&irl_pool_head)) + return -ENOMEM; + + if (irq > IRL_SIZE) + return -EINVAL; + + if (!handler) + return -EINVAL; + + psr_flags = spin_lock_save_irq(); + + p_elem = list_entry((&irl_pool_head)->next, struct irl_vector_elem, + handler_node); + + p_elem->handler = handler; + p_elem->priority = priority; + p_elem->data = data; + + list_move_tail(&p_elem->handler_node, &irl_vector[irq]); + + spin_lock_restore_irq(psr_flags); + +#ifdef CONFIG_LEON3 + /* XXX for now, just register to the current CPU */ + leon_enable_irq(irq, leon3_cpuid()); +#endif /* CONFIG_LEON3 */ +#ifdef CONFIG_LEON2 + leon_enable_irq(irq, 0); +#endif /* CONFIG_LEON2 */ + + /* XXX this is the call to whatever the low level handler is */ + return catch_interrupt(((int) leon_irq_dispatch), irq); +} + + +/** + * @brief register a handler function to the extended interrupt line + * + * @param IRQ an extended interrupt number + * @param priority a handler priority + * @param handler a handler function + * @param data a pointer to arbitrary user data + * + * @returns 0 on success + * + * XXX once this is confirmed working as planned, merge with function above, + * we only need one of those + */ + +static int eirl_register_handler(unsigned int irq, + enum isr_exec_priority priority, + irq_handler_t handler, + void *data) +{ + uint32_t psr_flags; + + struct irl_vector_elem *p_elem; + + + if (list_empty(&irl_pool_head)) + return -ENOMEM; + + if (irq > (IRL_SIZE + EIRL_SIZE)) + return -EINVAL; + + if (irq < IRL_SIZE) + return -EINVAL; + + if (!handler) + return -EINVAL; + + psr_flags = spin_lock_save_irq(); + + p_elem = list_entry((&irl_pool_head)->next, struct irl_vector_elem, + handler_node); + + p_elem->handler = handler; + p_elem->priority = priority; + p_elem->data = data; + + list_move_tail(&p_elem->handler_node, + &eirl_vector[LEON_REAL_EIRQ(irq)]); + + spin_lock_restore_irq(psr_flags); +#ifdef CONFIG_LEON3 + /* XXX for now, just register to the current CPU */ + leon_enable_irq(irq, leon3_cpuid()); +#endif /* CONFIG_LEON3 */ +#ifdef CONFIG_LEON2 + leon_enable_irq(irq, 0); +#endif /* CONFIG_LEON2 */ + + return 0; +} + + +/** + * @brief de-register a handler function to the primary interrupt line + * + * @param IRQ an interrupt number + * @param handler a handler function + * @param data a pointer to arbitrary user data + * + * @note in case of duplicate handlers, ALL encountered will + * be removed + * @returns 0 on success + */ + +static int irl_deregister_handler(unsigned int irq, + irq_handler_t handler, + void *data) +{ + uint32_t psr_flags; + + struct irl_vector_elem *p_elem; + struct irl_vector_elem *p_tmp; + + + if (irq > 0xF) + return -EINVAL; + + list_for_each_entry_safe(p_elem, p_tmp, + &irl_vector[irq], handler_node) { + + if (p_elem->handler == handler) { + if (p_elem->data == data) { + + p_elem->handler = NULL; + p_elem->data = NULL; + p_elem->priority = -1; + + psr_flags = spin_lock_save_irq(); + list_move_tail(&p_elem->handler_node, + &irl_pool_head); + spin_lock_restore_irq(psr_flags); + + if (irl_vector[irq].next == &irl_vector[irq]) + leon_disable_irq(irq, leon3_cpuid()); + } + } + } + + return 0; +} + + + +/** + * @brief de-register a handler function to the primary interrupt line + * + * @param irq an interrupt number + * @param handler a handler function + * @param data a pointer to arbitrary user data + * + * @note in case of duplicate handlers, ALL encountered will + * be removed + * @returns 0 on success + * + * XXX also think about merging with above + */ + +static int eirl_deregister_handler(unsigned int irq, + irq_handler_t handler, + void *data) +{ + uint32_t psr_flags; + + struct irl_vector_elem *p_elem; + struct irl_vector_elem *p_tmp; + + + /* XXX magic: extnded interrupts are 16...31 */ + if (irq > 0x1F) + return -EINVAL; + + if (irq < 0x10) + return -EINVAL; + + list_for_each_entry_safe(p_elem, p_tmp, + &eirl_vector[irq], handler_node) { + + if (p_elem->handler == handler) { + if (p_elem->data == data) { + + p_elem->handler = NULL; + p_elem->data = NULL; + p_elem->priority = -1; + + psr_flags = spin_lock_save_irq(); + list_move_tail(&p_elem->handler_node, + &irl_pool_head); + spin_lock_restore_irq(psr_flags); + + if (eirl_vector[irq].next == &eirl_vector[irq]) + leon_disable_irq(irq, leon3_cpuid()); + + } + } + } + + return 0; +} + + +/** + * @brief initialise some of the internals + * + * @returns 0 on success + */ + +static int irq_dispatch_init(void) +{ + size_t i; + +#ifdef CONFIG_IRQ_STATS_COLLECT + struct sysset *sset; + struct sysobj *sobj; +#endif /* CONFIG_IRQ_STATS_COLLECT */ + + + irl_vector = (struct list_head *) + kzalloc(IRL_SIZE * sizeof(struct list_head)); + if (!irl_vector) + return -1; + + + eirl_vector = (struct list_head *) + kzalloc(EIRL_SIZE * sizeof(struct list_head)); + if (!eirl_vector) + return -1; + + irl_pool = (struct irl_vector_elem *) + kzalloc(IRL_POOL_SIZE * sizeof(struct irl_vector_elem)); + if(!irl_pool) + return -1; + + + irl_queue_pool = (struct irl_vector_elem *) + kzalloc(IRL_QUEUE_SIZE * sizeof(struct irl_vector_elem)); + if(!irl_queue_pool) + return -1; + + + + INIT_LIST_HEAD(&irl_pool_head); + INIT_LIST_HEAD(&irl_queue_head); + INIT_LIST_HEAD(&irq_queue_pool_head); + + for (i = 0; i < IRL_SIZE; i++) + INIT_LIST_HEAD(&irl_vector[i]); + + for (i = 0; i < EIRL_SIZE; i++) + INIT_LIST_HEAD(&eirl_vector[i]); + + for (i = 0; i < IRL_POOL_SIZE; i++) + list_add_tail(&irl_pool[i].handler_node, &irl_pool_head); + + for (i = 0; i < IRL_QUEUE_SIZE; i++) + list_add_tail(&irl_queue_pool[i].handler_node, + &irq_queue_pool_head); + +#ifdef CONFIG_IRQ_STATS_COLLECT + sset = sysset_create_and_add("irl", NULL, sys_set); + + sobj = sysobj_create(); + + if (!sobj) + return -1; + + sobj->sattr = irl_attributes; + sysobj_add(sobj, NULL, sset, "primary"); + + sobj = sysobj_create(); + + if (!sobj) + return -1; + + sobj->sattr = eirl_attributes; + sysobj_add(sobj, NULL, sset, "secondary"); +#endif /* CONFIG_IRQ_STATS_COLLECT */ + + return 0; +} + + +/** + * @brief probe for extended IRQ controller + */ + +static void leon_setup_eirq(void) +{ + unsigned int eirq; + +#ifdef CONFIG_LEON3 + /* probe for extended IRQ controller, see GR712UM, p75 */ + eirq = (ioread32be(&leon_irqctrl_regs->mp_status) >> 16) & 0xf; +#endif /* CONFIG_LEON3 */ +#ifdef CONFIG_LEON2 + eirq = 10; /* yep... */ +#endif /* CONFIG_LEON2 */ + + if (!eirq || eirq > IRL_SIZE) { + pr_err("IRQ: extended irq controller invalid: %d\n", eirq); +#if 1 + BUG(); +#else + return; +#endif + } + + leon_eirq = eirq; + + BUG_ON(catch_interrupt((int) leon_eirq_dispatch, leon_eirq)); + +#ifdef CONFIG_LEON3 + /* XXX for now, just register to the current CPU */ + leon_enable_irq(leon_eirq, leon3_cpuid()); +#endif /* CONFIG_LEON3 */ +#ifdef CONFIG_LEON2 + leon_enable_irq(leon_eirq, 0); +#endif /* CONFIG_LEON2 */ + +} + + +/** + * @brief set interrupt hardware priority + * + * @param mask the interrupt selection mask to apply + * + * @param level selected interrupt level 1== HIGH, 0 == LOW + */ + +__attribute__((unused)) +static void leon_irq_set_level(uint32_t irq_mask, uint32_t level) +{ + uint32_t flags; + + +#ifdef CONFIG_LEON3 + flags = ioread32be(&leon_irqctrl_regs->irq_level); +#endif /* CONFIG_LEON3 */ +#ifdef CONFIG_LEON2 + flags = ioread32be(&leon_irqctrl_regs->irq_mask) >> 17; +#endif /* CONFIG_LEON2 */ + + if (!level) + flags &= ~irq_mask; + else + flags |= irq_mask; + +#ifdef CONFIG_LEON3 + iowrite32be(flags, &leon_irqctrl_regs->irq_level); +#endif /* CONFIG_LEON3 */ +#ifdef CONFIG_LEON2 + iowrite32be(flags << 17, &leon_irqctrl_regs->irq_mask); +#endif /* CONFIG_LEON2 */ + +} + +/** + * @brief enable an IRQ + * + * @param data a struct irq_data + */ + +static unsigned int enable_irq(struct irq_data *data) +{ + if (data->irq < IRL_SIZE) + return irl_register_handler(data->irq, data->priority, + data->handler, data->data); + + return eirl_register_handler(data->irq, data->priority, + data->handler, data->data); +} + +/** + * @brief disable an IRQ + * + * @param data a struct irq_data + */ + +static void disable_irq(struct irq_data *data) +{ + if (data->irq > 0xF) { + eirl_deregister_handler(data->irq, data->handler, data->data); + } else if (data->irq < 0x10) { + irl_deregister_handler(data->irq, data->handler, data->data); + } +} + + +/** + * @brief mask an IRQ + */ + +static void mask_irq(struct irq_data *data) +{ + leon_mask_irq(data->irq, leon3_cpuid()); +} + + +/** + * @brief mask an IRQ + * + * @param data a struct irq_data + */ + +static void unmask_irq(struct irq_data *data) +{ + leon_unmask_irq(data->irq, leon3_cpuid()); +} + + +/** + * @brief mask an IRQ + */ + +static void execute_deferred_irq(void) +{ + leon_irq_queue_execute(); +} + + + +/** + * the configuration for the high-level irq manager + */ + +static struct irq_dev leon_irq = { + .irq_enable = enable_irq, + .irq_disable = disable_irq, + .irq_mask = mask_irq, + .irq_unmask = unmask_irq, + .irq_deferred = execute_deferred_irq, +}; + + +/** + * @brief initialise the IRQ subsystem on the LEON + */ + +void leon_irq_init(void) +{ +#ifdef CONFIG_LEON3 + /* XXX should determine that from AMBA PNP scan */ + leon_irqctrl_regs = (struct leon3_irqctrl_registermap *) + LEON3_BASE_ADDRESS_IRQMP; + + /* mask all interrupts on this (boot) CPU */ + iowrite32be(0, &leon_irqctrl_regs->irq_mpmask[leon3_cpuid()]); +#endif /* CONFIG_LEON3 */ +#ifdef CONFIG_LEON2 + leon_irqctrl_regs = (struct leon2_irqctrl_registermap *) + LEON2_BASE_ADDRESS_IRQ; + leon_eirqctrl_regs = (struct leon2_eirqctrl_registermap *) + LEON2_BASE_ADDRESS_EIRQ; + + + iowrite32be(0, &leon_irqctrl_regs->irq_mask); +#endif /* CONFIG_LEON2 */ + + irq_init(&leon_irq); + + BUG_ON(irq_dispatch_init()); + + /* set up extended interrupt controller if found */ + leon_setup_eirq(); +} diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index f25f695fb311eea52d2df883cf4583718e79d899..f3ea4cc2c496ba9ea1032625ae26f0a31ffd23ec 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -6,6 +6,7 @@ #include <init.h> #include <mm.h> +#include <asm/irq.h> #include <compiler.h> #include <page.h> @@ -61,7 +62,7 @@ static void reserve_kernel_stack(void) static void mem_init(void) { sp_banks[0].base_addr = 0x40000000; - sp_banks[0].num_bytes = 0x00800000; + sp_banks[0].num_bytes = 0x02800000; #if (SPARC_PHYS_BANKS > 0) sp_banks[1].base_addr = 0x60000000; @@ -88,4 +89,6 @@ void setup_arch(void) reserve_kernel_stack(); BUG_ON(stack_migrate(NULL, _kernel_stack_top)); + + leon_irq_init(); } diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h index 9e9cf3972678d8b1ecbe6e332826cbf74b8380d6..58bd95380cb76906414e7097238e23fa78ea6742 100644 --- a/include/asm-generic/io.h +++ b/include/asm-generic/io.h @@ -25,14 +25,15 @@ #ifndef _ASM_GENERIC_IO_H_ #define _ASM_GENERIC_IO_H_ -#include <stdint.h> +#include <asm/io.h> +#include <kernel/types.h> #ifndef __raw_readb #define __raw_readb __raw_readb static inline uint8_t __raw_readb(const volatile void *addr) { - return *(const volatile uint8_t *)addr; + return (*(const volatile uint8_t *) addr); } #endif @@ -40,7 +41,7 @@ static inline uint8_t __raw_readb(const volatile void *addr) #define __raw_readw __raw_readw static inline uint16_t __raw_readw(const volatile void *addr) { - return *(const volatile uint16_t *)addr; + return (*(const volatile uint16_t *) addr); } #endif @@ -48,7 +49,7 @@ static inline uint16_t __raw_readw(const volatile void *addr) #define __raw_readl __raw_readl static inline uint32_t __raw_readl(const volatile void *addr) { - return *(const volatile uint32_t *)addr; + return (*(const volatile uint32_t *) addr); } #endif @@ -56,7 +57,7 @@ static inline uint32_t __raw_readl(const volatile void *addr) #define __raw_writeb __raw_writeb static inline void __raw_writeb(uint8_t w, volatile void *addr) { - *(volatile uint8_t *)addr = w; + (*(volatile uint8_t *) addr) = w; } #endif @@ -64,7 +65,7 @@ static inline void __raw_writeb(uint8_t w, volatile void *addr) #define __raw_writew __raw_writew static inline void __raw_writew(uint16_t w, volatile void *addr) { - *(volatile uint16_t *)addr = w; + (*(volatile uint16_t *) addr) = w; } #endif @@ -72,7 +73,7 @@ static inline void __raw_writew(uint16_t w, volatile void *addr) #define __raw_writel __raw_writel static inline void __raw_writel(uint32_t l, volatile void *addr) { - *(volatile uint32_t *)addr = l; + (*(volatile uint32_t *) addr) = l; } #endif diff --git a/include/asm-generic/spinlock.h b/include/asm-generic/spinlock.h new file mode 100644 index 0000000000000000000000000000000000000000..81c9280a397eaf7c292cdc12468175594db1c661 --- /dev/null +++ b/include/asm-generic/spinlock.h @@ -0,0 +1,86 @@ +/** + * @file include/asm-generic/spinlock.h + * + * @copyright GPLv2 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * @note this is nothing but a placeholder... + */ + +#ifndef _ASM_GENERIC_SPINLOCK_H_ +#define _ASM_GENERIC_SPINLOCK_H_ + +#include <asm/spinlock.h> + + +#ifndef __SPINLOCK +#define __SPINLOCK +struct spinlock { +} +#endif + +#ifndef __spin_lock_save_irq +#define __spin_lock_save_irq __spin_lock_save_irq +static unsigned long spin_lock_save_irq(void) +{ + return 0; +} +#endif + + +#ifndef __spin_lock_restore_irq +#define __spin_lock_restore_irq __spin_lock_restore_irq +static void spin_lock_restore_irq(__attribute__((unused)) uint32_t psr) +{ +} +#endif + + +#ifndef __spin_lock +#define __spin_lock __spin_lock +static void spin_lock(__attribute__((unused)) struct spinlock *lock) +{ +} +#endif + + +#ifndef __spin_is_locked +#define __spin_is_locked __spin_is_locked +static int spin_is_locked(__attribute__((unused)) struct spinlock *lock) +{ + return 0; +} +#endif + + +#ifndef __spin_unlock_wait +#define __spin_unlock_wait __spin_unlock_wait +static void spin_unlock_wait(__attribute__((unused)) struct spinlock *lock) +{ +} +#endif + +#ifndef __spin_try_lock +#define __spin_try_lock __spin_try_lock +static int spin_try_lock(__attribute__((unused)) struct spinlock *lock) +{ + return 0; +} +#endif + +#ifndef __spin_unlock +#define __spin_unlock __spin_unlock +static void spin_unlock(__attribute__((unused)) struct spinlock *lock) +{ +} +#endif + +#define /* _ASM_GENERIC_SPINLOCK_H_ */ diff --git a/include/kernel/export.h b/include/kernel/export.h new file mode 100644 index 0000000000000000000000000000000000000000..0a0f4b85ed1dc10be4d110f7abbda388a98f6987 --- /dev/null +++ b/include/kernel/export.h @@ -0,0 +1,52 @@ +/** + * @file include/kernel/export.h + * + * @note derived from include/linux/export.h */ +#ifndef _KERNEL_EXPORT_H_ +#define _KERNEL_EXPORT_H_ + + +struct kernel_symbol { + unsigned long value; + const char *name; +}; + + +#define __KERNEL_SYMBOL(x) x +#define __KERNEL_SYMBOL_STR(x) #x + + +/* Indirect, so macros are expanded before pasting. */ +#define KERNEL_SYMBOL(x) __KERNEL_SYMBOL(x) +#define KERNEL_SYMBOL_STR(x) __KERNEL_SYMBOL_STR(x) + +#if 0 +/* For every exported symbol, place a struct in the __ksymtab section */ +#define ___EXPORT_SYMBOL(sym, sec) \ + extern typeof(sym) sym; \ + static const char __kstrtab_##sym[] \ + __attribute__((section("__ksymtab_strings"), aligned(1))) \ + = KERNEL_SYMBOL_STR(sym); \ + static const struct kernel_symbol __ksymtab_##sym \ + __attribute__((used)) \ + __attribute__((section("___ksymtab" sec "+" #sym), used)) \ + = { (unsigned long)&sym, __kstrtab_##sym } +#else +#define ___EXPORT_SYMBOL(sym, sec) \ + extern typeof(sym) sym; \ + static const char __kstrtab_##sym[] \ + = KERNEL_SYMBOL_STR(sym); \ + static const struct kernel_symbol __ksymtab_##sym \ + __attribute__((used)) \ + = { (unsigned long)&sym, __kstrtab_##sym } + +#endif + +#define __EXPORT_SYMBOL ___EXPORT_SYMBOL + +#define EXPORT_SYMBOL(sym) \ + __EXPORT_SYMBOL(sym, "") + + + +#endif /* _KERNEL_EXPORT_H_ */ diff --git a/include/kernel/init.h b/include/kernel/init.h index f5f8b1a6ea9cfb1787a5e80ca194c6e78a6f351f..1ea11195a63232208a36dc5dba728bf27ec72172 100644 --- a/include/kernel/init.h +++ b/include/kernel/init.h @@ -2,4 +2,16 @@ * @file include/kernel/init.h */ +#ifndef _KERNEL_INIT_H_ +#define _KERNEL_INIT_H_ + +typedef int (*initcall_t)(void); + +/* TODO: initcalls on startup for compiled-in modules */ +#define define_initcall(fn) \ + static initcall_t _initcall_##fn __attribute__((used)) \ + __attribute__((__section__(".initcall"))) = fn; + void setup_arch(void); + +#endif /* _KERNEL_INIT_H_ */ diff --git a/include/kernel/irq.h b/include/kernel/irq.h new file mode 100644 index 0000000000000000000000000000000000000000..dc3afdc1547f4e4f776dec1666c03f7b86d92b45 --- /dev/null +++ b/include/kernel/irq.h @@ -0,0 +1,62 @@ +/** + * @file include/kernel/irq.h + * @author Armin Luntzer (armin.luntzer@univie.ac.at) + * @date December, 2013 + * + * @copyright GPLv2 + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef _KERNEL_IRQ_H_ +#define _KERNEL_IRQ_H_ + +#include <kernel/types.h> + + +enum irqreturn { + IRQ_NONE = 0, + IRQ_HANDLED = 1 +}; + +typedef enum irqreturn irqreturn_t; + +typedef irqreturn_t (*irq_handler_t)(void *); +enum isr_exec_priority {ISR_PRIORITY_NOW, ISR_PRIORITY_DEFERRED}; + + +struct irq_data { + unsigned int irq; + enum isr_exec_priority priority; + irq_handler_t handler; + void *data; +}; + +struct irq_dev { + unsigned int (*irq_enable) (struct irq_data *data); + void (*irq_disable) (struct irq_data *data); + void (*irq_mask) (struct irq_data *data); + void (*irq_unmask) (struct irq_data *data); + void (*irq_deferred) (void); +}; + + + +void irq_init(struct irq_dev *dev); + +int irq_free(unsigned int irq, irq_handler_t handler, void *data); + +int irq_request(unsigned int irq, enum isr_exec_priority priority, + irq_handler_t handler, void *data); + +int irq_exec_deferred(void); + +#endif /* _KERNEL_IRQ_H_ */ diff --git a/include/kernel/kmem.h b/include/kernel/kmem.h index 4332b4035b6d50a3c3a6aa37ed3323279450d760..39f8f91aea8ebdcfa0407c151465112a3276a8d3 100644 --- a/include/kernel/kmem.h +++ b/include/kernel/kmem.h @@ -8,6 +8,7 @@ #include <stddef.h> void *kmalloc(size_t size); +void *kzalloc(size_t size); void *kcalloc(size_t nmemb, size_t size); void *krealloc(void *ptr, size_t size); diff --git a/include/kernel/module.h b/include/kernel/module.h index 944b26dfac2f3b7300bec0847757fe305533c40f..281745ff9efe69f4138b1dc3fef6c656848d8f56 100644 --- a/include/kernel/module.h +++ b/include/kernel/module.h @@ -1,10 +1,19 @@ #ifndef _KERNEL_MODULE_H_ #define _KERNEL_MODULE_H_ - +#include <kernel/init.h> #include <kernel/elf.h> + +#define module_init(initfunc) \ + int _module_init(void) __attribute__((alias(#initfunc))); + +#define module_exit(exitfunc) \ + int _module_exit(void) __attribute__((alias(#exitfunc))); + + + struct module_section { char *name; unsigned long addr; diff --git a/include/kernel/string.h b/include/kernel/string.h new file mode 100644 index 0000000000000000000000000000000000000000..37e749021b6ea417addb524176adfa0f14dd6fff --- /dev/null +++ b/include/kernel/string.h @@ -0,0 +1,20 @@ +/** + * @file include/kernel/string.h + */ + +#ifndef _KERNEL_STRING_H_ +#define _KERNEL_STRING_H_ + +int sprintf(char *str, const char *format, ...); +int strcmp(const char *s1, const char *s2); +char *strpbrk(const char *s, const char *accept); +char *strsep(char **stringp, const char *delim); +size_t strlen(const char *s); + +void *memcpy(void *dest, const void *src, size_t n); + +int isspace(int c); +int atoi(const char *nptr); + + +#endif /* _KERNEL_STRING_H_ */ diff --git a/include/kernel/types.h b/include/kernel/types.h index 4463c12650b340b2cb72d295299fffcfd3208e54..e4f58b015b8c68b004d21b092d443c5808276398 100644 --- a/include/kernel/types.h +++ b/include/kernel/types.h @@ -5,6 +5,8 @@ #ifndef _KERNEL_TYPES_H_ #define _KERNEL_TYPES_H_ +#include <compiler.h> + #include <stddef.h> #include <stdbool.h> @@ -16,6 +18,12 @@ #endif +/* d'oh! -.- */ +#if defined(CONFIG_LEON2) || defined(CONFIG_LEON3) +#define __BIG_ENDIAN_BITFIELD __BIG_ENDIAN_BITFIELD +#endif + + /* BCC is at least 4.4.2 */ #if defined(GCC_VERSION) && (GCC_VERSION >= 404020) #include <stdint.h> @@ -36,6 +44,15 @@ typedef signed short int16_t; typedef unsigned char uint8_t; typedef signed char int8_t; +compile_time_assert(sizeof(uint64_t) == 8, TYPES_UINT64_SIZE_INVALID); +compile_time_assert(sizeof( int64_t) == 8, TYPES__INT64_SIZE_INVALID); +compile_time_assert(sizeof(uint32_t) == 4, TYPES_UINT32_SIZE_INVALID); +compile_time_assert(sizeof( int32_t) == 4, TYPES__INT32_SIZE_INVALID); +compile_time_assert(sizeof(uint16_t) == 2, TYPES_UINT16_SIZE_INVALID); +compile_time_assert(sizeof( int16_t) == 2, TYPES__INT16_SIZE_INVALID); +compile_time_assert(sizeof(uint8_t) == 1, TYPES_UINT_8_SIZE_INVALID); +compile_time_assert(sizeof( int8_t) == 1, TYPES__INT_8_SIZE_INVALID); + #endif #ifndef __SIZEOF_LONG__ @@ -44,4 +61,7 @@ typedef signed char int8_t; + + + #endif /* _KERNEL_TYPES_H_ */ diff --git a/include/mppb.h b/include/mppb.h new file mode 100644 index 0000000000000000000000000000000000000000..8d9e53626c5a95cf44242ad632017c3a621c974e --- /dev/null +++ b/include/mppb.h @@ -0,0 +1,28 @@ +/** + * @file include/mppb.h + */ + +#ifndef _MPPB_H_ +#define _MPPB_H_ + + + +#define MPPB_NOC_DMA_BASE_ADDR 0x30100000 + +#define MPPB_SDRAM_AHB_BASE 0x40000000 +#define MPPB_SDRAM_AHB_SIZE 0x0FFFFFFF + +#define MPPB_SDRAM_NOC_BASE 0x50000000 +#define MPPB_SDRAM_NOC_SIZE 0x0FFFFFFF + +#define MPPB_SRAM_NOC_BASE 0x30000000 +#define MPPB_SRAM_NOC_SIZE 0x0003FFFF + +/* get the IRL for a channel, on the MPPB, this is equivalent to the channel + * number on the secondary interrupt controller + */ +#define MPPB_NOC_GET_DMA_IRL(chan) (chan) + + + +#endif /* _MPPB_H_ */ diff --git a/include/noc.h b/include/noc.h new file mode 100644 index 0000000000000000000000000000000000000000..ba558d27e67e577e2005aa32e2a5da239c0d96d3 --- /dev/null +++ b/include/noc.h @@ -0,0 +1,37 @@ +/** + * @file include/noc.h + */ + +#ifndef _NOC_H_ +#define _NOC_H_ + + +#ifdef CONFIG_MPPB + +#include <mppb.h> + +#define NOC_DMA_BASE_ADDR MPPB_NOC_DMA_BASE_ADDR + +#define NOC_SCRATCH_BUFFER_BASE MPPB_SRAM_NOC_BASE +#define NOC_SCRATCH_BUFFER_SIZE MPPB_SRAM_NOC_SIZE + +#define NOC_GET_DMA_IRL(chan) MPPB_NOC_GET_DMA_IRL(chan) + +#endif /* CONFIG_MPPB */ + + +#ifdef CONFIG_SSDP + +#include <ssdp.h> + +#define NOC_DMA_BASE_ADDR SSDP_NOC_DMA_BASE_ADDR + +#define NOC_SCRATCH_BUFFER_BASE SSDP_SRAM_NOC_BASE +#define NOC_SCRATCH_BUFFER_SIZE SSDP_SRAM_NOC_SIZE + +#define NOC_GET_DMA_IRL(chan) SSDP_NOC_GET_DMA_IRL(chan) + +#endif /* CONFIG_SSDP */ + + +#endif /* _NOC_H_ */ diff --git a/include/noc_dma.h b/include/noc_dma.h new file mode 100644 index 0000000000000000000000000000000000000000..a66e61684e89f1af40746dd1ab6339fba3d1874d --- /dev/null +++ b/include/noc_dma.h @@ -0,0 +1,115 @@ +/** + * @file include/noc_dma.h + */ + + +#ifndef _NOC_DMA_H_ +#define _NOC_DMA_H_ + +#include <noc.h> +#include <kernel/types.h> + + + +#define NOC_DMA_CHANNELS 8 + +#define NOC_DMA_ACCESS_SIZE_8 0x0 +#define NOC_DMA_ACCESS_SIZE_16 0x1 +#define NOC_DMA_ACCESS_SIZE_32 0x2 +#define NOC_DMA_ACCESS_SIZE_64 0x3 + + +#define NOC_DMA_PRIORITY_LOW 0x0 +#define NOC_DMA_PRIORITY_HIGH 0x1 + +#define NOC_DMA_IRQ_FWD_NONE 0x0 +#define NOC_DMA_IRQ_FWD_IN 0x1 +#define NOC_DMA_IRQ_FWD_OUT 0x2 +#define NOC_DMA_IRQ_FWD_BOTH 0x3 + + +#define NOC_DMA_STRIDE_MAX 32767 +#define NOC_DMA_STRIDE_MIN -32767 + +#define NOC_DMA_SIZE_MAX 65535 + +#define NOC_DMA_CHANNEL_START 0x1 +#define NOC_DMA_CHANNEL_BUSY NOC_DMA_CHANNEL_START + + +#define NOC_DMA_STRIDE_SRC_OFFSET 16 +#define NOC_DMA_STRIDE_DST_MASK 0xffff + +#define NOC_DMA_SIZE_Y_OFFSET 16 +#define NOC_DMA_SIZE_X_MASK 0xffff + + +#define NOC_DMA_STRIDES(src, dst) ((src << NOC_DMA_STRIDE_SRC_OFFSET) | \ + (dst & NOC_DMA_STRIDE_DST_MASK)) + +#define NOC_DMA_SIZES(x, y) ((y << NOC_DMA_SIZE_Y_OFFSET) | \ + (x & NOC_DMA_SIZE_X_MASK)) + + +#ifdef CONFIG_NOC_DMA_TRANSFER_QUEUE_SIZE +#define NOC_DMA_TRANSFER_QUEUE_SIZE CONFIG_NOC_DMA_TRANSFER_QUEUE_SIZE +#else +#define NOC_DMA_TRANSFER_QUEUE_SIZE 32 +#endif + +enum noc_dma_elem_size {BYTE = NOC_DMA_ACCESS_SIZE_8, + HALFWORD = NOC_DMA_ACCESS_SIZE_16, + WORD = NOC_DMA_ACCESS_SIZE_32, + DOUBLEWORD = NOC_DMA_ACCESS_SIZE_64}; + +enum noc_dma_priority {LOW = NOC_DMA_PRIORITY_LOW, + HIGH = NOC_DMA_PRIORITY_HIGH}; + +enum noc_dma_irq_fwd {OFF = NOC_DMA_IRQ_FWD_NONE, + IN = NOC_DMA_IRQ_FWD_IN, + OUT = NOC_DMA_IRQ_FWD_OUT, + BOTH = NOC_DMA_IRQ_FWD_BOTH}; + +struct noc_dma_transfer { + + void *src; + void *dst; + + uint16_t x_elem; /* number of elements in x */ + uint16_t y_elem; /* number of elements in y */ + + int16_t x_stride_src; /* width of stride in source x */ + int16_t y_stride_src; /* width of stride in source y */ + + int16_t x_stride_dst; /* width of stride in destination x */ + int16_t y_stride_dst; /* width of stride in destination y */ + + + uint16_t mtu; /* maximum packet size */ + enum noc_dma_elem_size elem_size; /* the element type size */ + enum noc_dma_irq_fwd irq_fwd; /* irq notification mode */ + enum noc_dma_priority priority; /* transfer priority */ + + /* the caller may specify a callback with user data that is executed + * on transfer completion + */ + int (*callback)(void *); + void *userdata; + + struct list_head node; +}; + +int noc_dma_req_xfer(void *src, void *dst, uint16_t x_elem, uint16_t y_elem, + enum noc_dma_elem_size elem_size, + int16_t x_stride_src, int16_t x_stride_dst, + int16_t y_stride_src, int16_t y_stride_dst, + enum noc_dma_priority dma_priority, uint16_t mtu, + int (*callback)(void *), void *userdata); + +int noc_dma_req_lin_xfer(void *src, void *dst, + uint16_t elem, enum noc_dma_elem_size elem_size, + enum noc_dma_priority dma_priority, uint16_t mtu, + int (*callback)(void *), void *userdata); + + +#endif /* _NOC_DMA_H_ */ diff --git a/include/ssdp.h b/include/ssdp.h new file mode 100644 index 0000000000000000000000000000000000000000..7db4ed41e019fcd6c09b6e17809cb277c753ae3d --- /dev/null +++ b/include/ssdp.h @@ -0,0 +1,29 @@ +/** + * @file include/ssdp.h + */ + +#ifndef _SSDP_H_ +#define _SSDP_H_ + +#warning "Configuration not yet known, using MPPBv2 as baseline" + + +#define SSDP_NOC_DMA_BASE_ADDR 0x30100000 + +#define SSDP_SDRAM_AHB_BASE 0x40000000 +#define SSDP_SDRAM_AHB_SIZE 0x0FFFFFFF + +#define SSDP_SDRAM_NOC_BASE 0x50000000 +#define SSDP_SDRAM_NOC_SIZE 0x0FFFFFFF + +#define SSDP_SRAM_NOC_BASE 0x30000000 +#define SSDP_SRAM_NOC_SIZE 0x0003FFFF + +/* get the IRL for a channel, on the SSDP, this is equivalent to the channel + * number on the secondary interrupt controller + */ +#define SSDP_NOC_GET_DMA_IRL(chan) (chan) + + + +#endif /* _SSDP_H_ */ diff --git a/init/Kconfig b/init/Kconfig index 0c176d991eade6e5197d6c0d572e12632c5a7457..a10aa3e58d829140ab50624e7ae3477b40112e8b 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -16,7 +16,7 @@ config KALLSYMS help Create a kernel image symbol lookup table for use in loadable modules. Note: If disabled, modules can still export symbols for other modules - to use, but no kernel functions will be resolvable. + to use, but no kernel functions will be resolvable. If unsure, say Y. diff --git a/init/main.c b/init/main.c index faec954e2c8489101dcc4a3720d650c61549c9af..d4914a379b79af6c9a4a701b66bc650619b18612 100644 --- a/init/main.c +++ b/init/main.c @@ -14,7 +14,9 @@ #include <kernel/printk.h> #include <kernel/kmem.h> #include <kernel/sbrk.h> +#include <kernel/sysctl.h> +#define MSG "MAIN: " void module_image_load_embedded(void); void *module_lookup_embedded(char *mod_name); @@ -24,6 +26,9 @@ void *module_read_embedded(char *mod_name); static void kernel_init(void) { setup_arch(); +#ifdef CONFIG_SYSCTL + sysctl_init(); +#endif } @@ -39,14 +44,14 @@ int main(void) /* load the embedded AR image */ module_image_load_embedded(); - +#if 0 /* look up a kernel symbol */ printk("%s at %p\n", "printk", lookup_symbol("printk")); -#if 0 - /* look up a file in the embedded image */ - printk("%s at %p\n", "testmodule.ko", - module_lookup_embedded("testmodule.ko")); + /* look up a file in the embedded image */ + printk("%s at %p\n", "noc_dma.ko", + module_lookup_embedded("noc_dma.ko")); +#endif /* to load an arbitrary image, you may upload it via grmon, e.g. * load -binary kernel/test.ko 0xA0000000 * then load it: @@ -59,13 +64,15 @@ int main(void) * it directly, until we have a MNA trap */ - addr = module_read_embedded("testmodule.ko"); + addr = module_read_embedded("noc_dma.ko"); + + pr_debug(MSG "noc_dma module address is %p\n", addr); if (addr) module_load(&m, addr); -#endif +#if 0 modules_list_loaded(); - +#endif return 0; } diff --git a/init/modules-image.c b/init/modules-image.c index dec2c27a2790fda6590b2ccbee2cb6011f91bd18..e00937b47968bdcdcb459ebaf1ad739f38fcfee6 100644 --- a/init/modules-image.c +++ b/init/modules-image.c @@ -18,8 +18,10 @@ void module_image_load_embedded(void) ar_load(&_binary_modules_image_start, (unsigned int)&_binary_modules_image_size, &mod_ar); +#if 0 ar_list_files(&mod_ar); ar_list_symbols(&mod_ar); +#endif } diff --git a/kernel/Kconfig b/kernel/Kconfig index 3863ba2af5c0aad0f3f092e58c0ba7a340945d77..f6495b9f62f756729e972e873a598c73a586a570 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -18,3 +18,77 @@ config KERNEL_LEVEL 7: informational 8: debug +config IRQ_STATS_COLLECT + bool "Enable IRQ statistics via sysctl" + depends on SYSCTL + default n + help + Collect interrupt statistics and expose them in the + systl tree. + +config SOC + bool "Enable System-On-Chip configurations" + default n + +choice + prompt "Select board type" + default MPPB + depends on SOC + +config MPPB + bool "MPPBv2" + depends on SOC && LEON2 + help + Configure parameters for the MPPBv2 + +config SSDP + bool "SSDP" + depends on SOC && LEON3 + help + Configure parameters for the SSDP + +endchoice + +menu "System-on-Chip drivers" + +depends on SOC + +config NOC_DMA + tristate "NoC 2D DMA driver" + default m + depends on SOC && (MPPB || SSDP) + help + Build the MPPBv2/SSDP NoC DMA driver. + +config NOC_DMA_TRANSFER_QUEUE_SIZE + int "NoC DMA transfer queue size" + default "32" + depends on NOC_DMA + help + Configures the size of the DMA transfer queue. + +config NOC_DMA_STATS_COLLECT + bool "Enable NoC DMA statistics via sysctl" + depends on SYSCTL + default n + help + Collect DMA transfer statistics and expose them in the + systl tree. + +config NOC_DMA_SAME_MASTER_TRANSFER_WORKAROUND + bool "Enable workaround for NoC DMA transfers within same master" + default n + help + Enable a workaround for NoC DMA transfers with the address space + of the same master. On the MPPB this will result in a stuck channel, + as the transfer never completes. If you say Y here, the requested + transfer will be initialised as two transfers, one of high priority + that copies the source data into a scratch buffer (the NoC SRAM tile) + and a low priority transfer that copies the contents of the scratch + buffer to the destination. Results may be unpredictable, + you have been warned. + +endmenu + + + diff --git a/kernel/Makefile b/kernel/Makefile index 25d52d8ec9a7b250800f24e6be0eb1f9d07f01c9..b163e872b5bd2602f4eaa45fc8c2e6a2f71e1395 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -3,3 +3,5 @@ obj-y += ksym.o obj-$(CONFIG_KERNEL_PRINTK) += printk.o obj-y += bitmap.o obj-y += module.o +obj-$(CONFIG_NOC_DMA) += noc_dma.o +obj-y += irq.o diff --git a/kernel/irq.c b/kernel/irq.c new file mode 100644 index 0000000000000000000000000000000000000000..3253f93c8e062d328da17e822a378258ea901411 --- /dev/null +++ b/kernel/irq.c @@ -0,0 +1,91 @@ +/** + * @file kernel/irq.c + * @author Armin Luntzer (armin.luntzer@univie.ac.at) + * + * this implements the high-level IRQ logic (which is admittedly not very + * thought-through) + */ + + +#include <errno.h> +#include <kernel/irq.h> +#include <kernel/export.h> + +static struct irq_dev *irq_ctrl; + + +/** + * @brief request a slot for an interrupt handler + */ + +int irq_request(unsigned int irq, enum isr_exec_priority priority, + irq_handler_t handler, void *data) +{ + struct irq_data cfg; + + + if (!irq_ctrl) + return -EINVAL; + + if (!irq_ctrl->irq_enable) + return -EINVAL; + + cfg.irq = irq; + cfg.priority = priority; + cfg.handler = handler; + cfg.data = data; + + return irq_ctrl->irq_enable(&cfg); +} +EXPORT_SYMBOL(irq_request); + +/** + * @brief release an interrupt handler + */ + +int irq_free(unsigned int irq, irq_handler_t handler, void *data) +{ + struct irq_data cfg; + + + if (!irq_ctrl) + return -EINVAL; + + if (!irq_ctrl->irq_disable) + return -EINVAL; + + cfg.irq = irq; + cfg.handler = handler; + cfg.data = data; + + irq_ctrl->irq_disable(&cfg); + + return 0; +} +EXPORT_SYMBOL(irq_free); + + +/** + * @brief execute deferred interrupt handlers + */ + +int irq_exec_deferred(void) +{ + if (!irq_ctrl) + return -EINVAL; + + if (irq_ctrl->irq_deferred) + irq_ctrl->irq_deferred(); + + return 0; +} + + +/** + * @brief initialise the IRQ system + */ + +void irq_init(struct irq_dev *dev) +{ + irq_ctrl = dev; +} diff --git a/kernel/kmem.c b/kernel/kmem.c index 46dcf8dd9b58ba6929461e596c7c9e17faa9c31b..2f89dc26a82d7c29232fc2ddf5b867601a4083b2 100644 --- a/kernel/kmem.c +++ b/kernel/kmem.c @@ -248,6 +248,24 @@ void *kcalloc(size_t nmemb, size_t size) } +/** + * @brief allocates size bytes and returns a pointer to the allocated memory, + * suitably aligned for any built-in type. The memory is set to zero. + * + * @param size the number of bytes to allocate + * + * @returns a pointer or NULL on error or size == 0 + * + * @note this should be preferred over kcalloc(n, 1), as it saves the extra + * argument and hence produces less code + */ + +void *kzalloc(size_t size) +{ + return kcalloc(size, 1); +} + + /** * @brief changes the size of the memory block pointed to by ptr to size bytes. * The contents will be unchanged in the range from the start of the diff --git a/kernel/module.c b/kernel/module.c index 22d36d6e1820f79df7441bbd5f4319e41f4bf893..bdab605808299a8d46e954fb7f4ad6a8f4f8090c 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -858,7 +858,11 @@ static int module_relocate(struct elf_module *m) symstr); - if (!(get_symbol_type(m, symstr) & STT_FUNC)) { + if (!(get_symbol_type(m, symstr) & STT_OBJECT)) { + pr_debug(MOD "\tERROR, object data resolution not yet implemented, symbol %s may not function as intended. If it is a variable, declare it static as a workaround\n", symstr); + } + + if (!(get_symbol_type(m, symstr) & (STT_FUNC | STT_OBJECT))) { pr_err(MOD "\tERROR, unresolved symbol %s\n", symstr); return -1; } @@ -871,7 +875,7 @@ static int module_relocate(struct elf_module *m) } - pr_info(MOD "\tSymbol %s at %lx\n", symstr, sym); + pr_debug(MOD "\tSymbol %s at %lx\n", symstr, sym); apply_relocate_add(m, &relatab[i], sym); diff --git a/kernel/noc_dma.c b/kernel/noc_dma.c new file mode 100644 index 0000000000000000000000000000000000000000..4d9053866f8a6fdc16cab0c134c8279780b211b8 --- /dev/null +++ b/kernel/noc_dma.c @@ -0,0 +1,819 @@ +/** + * @file kernel/noc_dma.c + * + * @note parameter limits are usually not checked, as they are implicit by type + * + * @todo stuck channel detection (needs threading or at least a timer service) + * and reset (if possible, transfer size 0 maybe? need to test...) + */ + + +#include <kernel/module.h> +#include <kernel/kernel.h> +#include <kernel/printk.h> +#include <kernel/string.h> +#include <kernel/kmem.h> +#include <kernel/sysctl.h> +#include <kernel/types.h> +#include <kernel/export.h> + + +#include <asm/irq.h> +#include <kernel/irq.h> + +#include <asm-generic/io.h> +#include <errno.h> +#include <list.h> + +#include <noc_dma.h> + + +#if !defined(CONFIG_MPPB) && !defined(CONFIG_SSDP) +#error "Unsupported platform" +#endif + +#define MSG "NOC DMA: " + + +__extension__ +struct noc_dma_channel_ctrl { + union { + uint32_t ctrl; +#if defined(__BIG_ENDIAN_BITFIELD) + struct { + uint32_t max_pkt_sz :16; + uint32_t reserved :11; + uint32_t acc_sz :2; + uint32_t irq_fwd :2; + uint32_t priority :1; + }__attribute__((packed)); +#endif + }; +}; + +/** + * NoC DMA channel configuration register layout + * @see MPPB datasheet v4.03, p63 + */ +__extension__ +struct noc_dma_channel { + + union { + uint32_t start; + uint32_t status; +#if defined(__BIG_ENDIAN_BITFIELD) + struct { + uint32_t res01 :31; /* reserved */ + uint32_t start_status:1; + }__attribute__((packed)); +#endif + }; + + union { + uint32_t control; +#if defined(__BIG_ENDIAN_BITFIELD) + struct noc_dma_channel_ctrl ctrl; +#endif + }; + + uint32_t dst; + + uint32_t src; + + union { + uint32_t sizes; + struct { + uint16_t sz_y; + uint16_t sz_x; + }__attribute__((packed)); + }; + + union { + uint32_t strides_x; + struct { + uint16_t str_x_src; + uint16_t str_x_dst; + }__attribute__((packed)); + }; + + union { + uint32_t strides_y; + struct { + uint16_t str_y_src; + uint16_t str_y_dst; + }__attribute__((packed)); + }; + + uint32_t res02[9]; + +}__attribute__((packed)); + +/* make sure the structure has the same size as the register */ +compile_time_assert(sizeof(struct noc_dma_channel) == 0x40, + NOC_DMA_CHANNEL_CONFIG_SIZE_INVALID); + + +/* we use this to track channels in our channel pool */ +struct noc_dma_channel_node { + + unsigned int irl; /* the interrupt line of this channel */ + + struct noc_dma_channel *channel; + struct noc_dma_transfer *transfer; + + struct list_head node; +}; + + + + + +/* + * XXX this is statically assigned for now. If we have an MMU or if we can + * detect the from something like AMBA PnP, we perhaps want + * to mmap/configure the address on initialisation + */ +static struct noc_dma_channels { + struct noc_dma_channel chan[NOC_DMA_CHANNELS]; +} *noc_dma = (struct noc_dma_channels *) NOC_DMA_BASE_ADDR; + + +static struct list_head dma_channel_pool_head; +static struct list_head dma_transfer_pool_head; +static struct list_head dma_transfer_queue_head; + +/* we could set this up at runtime, but what for? */ +static struct noc_dma_transfer dma_transfer_pool[NOC_DMA_TRANSFER_QUEUE_SIZE]; + +static struct noc_dma_channel_node dma_channel_pool[NOC_DMA_CHANNELS]; + + +#ifdef CONFIG_NOC_DMA_STATS_COLLECT +static struct { + unsigned int transfers_queued; + unsigned int transfers_requested; + unsigned int transfers_completed; + + unsigned int bytes_requested; + unsigned int bytes_transferred; +} noc_dma_stats; + + + +__extension__ +static ssize_t noc_dma_show(struct sysobj *sobj __attribute__((unused)), + struct sobj_attribute *sattr, + char *buf) +{ + if (!strcmp(sattr->name, "transfers_queued")) + return sprintf(buf, "%d", noc_dma_stats.transfers_queued); + + if (!strcmp(sattr->name, "transfers_requested")) + return sprintf(buf, "%d", noc_dma_stats.transfers_requested); + + if (!strcmp(sattr->name, "transfers_completed")) + return sprintf(buf, "%d", noc_dma_stats.transfers_completed); + + if (!strcmp(sattr->name, "bytes_requested")) + return sprintf(buf, "%d", noc_dma_stats.bytes_requested); + + if (!strcmp(sattr->name, "bytes_transferred")) + return sprintf(buf, "%d", noc_dma_stats.bytes_transferred); + + return 0; +} + +__extension__ +static ssize_t noc_dma_store(struct sysobj *sobj __attribute__((unused)), + struct sobj_attribute *sattr, + const char *buf __attribute__((unused)), + size_t len __attribute__((unused))) +{ + if (!strcmp(sattr->name, "reset_stats")) { + noc_dma_stats.transfers_requested = 0; + noc_dma_stats.transfers_completed = 0; + noc_dma_stats.bytes_requested = 0; + noc_dma_stats.bytes_transferred = 0; + } + + return 0; +} + +__extension__ +static struct sobj_attribute noc_dma_attr[] = { + __ATTR(transfers_queued, noc_dma_show, NULL), + __ATTR(transfers_requested, noc_dma_show, NULL), + __ATTR(transfers_completed, noc_dma_show, NULL), + __ATTR(bytes_requested, noc_dma_show, NULL), + __ATTR(bytes_transferred, noc_dma_show, NULL), + __ATTR(reset_stats, NULL, noc_dma_store) +}; + +__extension__ +static struct sobj_attribute *noc_dma_attributes[] = { + &noc_dma_attr[0], &noc_dma_attr[1], &noc_dma_attr[2], + &noc_dma_attr[3], &noc_dma_attr[4], &noc_dma_attr[5], + NULL}; + +#endif /* CONFIG_NOC_DMA_STATS_COLLECT */ + + + + +/** + * @brief check if a NoC DMA channel is in use + * + * @param chan a struct noc_dma_channel + * + * @returns 0 if not busy + */ + +static int noc_dma_channel_busy(struct noc_dma_channel *chan) +{ + return ioread32be(&chan->status) & NOC_DMA_CHANNEL_BUSY; +} + + +/** + * @brief set source and destination strides in x + * + * @param chan a struct noc_dma_channel + * @param stride_src the source stride + * @param stride_dst the destination stride + */ + +static void noc_dma_set_strides_x(struct noc_dma_channel *chan, + int16_t stride_src, int16_t stride_dst) +{ + iowrite32be(NOC_DMA_STRIDES(stride_src, stride_dst), &chan->strides_x); +} + + +/** + * @brief set source and destination strides in y + * + * @param chan a struct noc_dma_channel + * @param stride_src the source stride + * @param stride_dst the destination stride + */ + +static void noc_dma_set_strides_y(struct noc_dma_channel *chan, + int16_t stride_src, int16_t stride_dst) +{ + iowrite32be(NOC_DMA_STRIDES(stride_src, stride_dst), &chan->strides_y); +} + + +/** + * @brief set transfer sizes in x and y + * + * @param chan a struct noc_dma_channel + * @param size_x the number of elements to transfer in x + * @param size_y the number of elements to transfer in y + */ + +static void noc_dma_set_sizes(struct noc_dma_channel *chan, + int16_t size_x, uint16_t size_y) +{ + iowrite32be(NOC_DMA_SIZES(size_x, size_y), &chan->sizes); +} + + +/** + * @brief set transfer source address + * + * @param chan a struct noc_dma_channel + * @param src the source memory location + */ + +static void noc_dma_set_src(struct noc_dma_channel *chan, void *src) +{ + iowrite32be((uint32_t) src, &chan->src); +} + + +/** + * @brief set transfer destination address + * + * @param chan a struct noc_dma_channel + * @param dst the destination memory location + */ + +static void noc_dma_set_dst(struct noc_dma_channel *chan, void *dst) +{ + iowrite32be((uint32_t) dst, &chan->dst); +} + + +/** + * @brief start a DMA transfer + * @param chan a struct noc_dma_channel + * + * @returns 0 on success, -EBUSY if channel is active + */ + +static int noc_dma_start_transfer(struct noc_dma_channel *chan) +{ + if (noc_dma_channel_busy(chan)) + return -EBUSY; + + iowrite32be(NOC_DMA_CHANNEL_START, &chan->start); + + return 0; +} + + +/** + * @brief sets the channel configuration for a transfer + * + * @param chan a struct noc_dma_channel + * @param max_pkt_size the maximum packet size to use during the transfer + * @param elem_size the size of a transfer element + * @param irq_fwd the irq notification mode on transfer completion + * @param priority the transfer priority + * + * @returns 0 on success, -EBUSY if channel is active + */ + +static int noc_dma_set_config(struct noc_dma_channel *chan, + int16_t max_pkt_size, + enum noc_dma_elem_size elem_size, + enum noc_dma_irq_fwd irq_fwd, + enum noc_dma_priority priority) +{ + struct noc_dma_channel_ctrl ctrl; + + + if (noc_dma_channel_busy(chan)) + return -EBUSY; + +#if defined(__BIG_ENDIAN_BITFIELD) + ctrl.max_pkt_sz = max_pkt_size; + ctrl.acc_sz = elem_size; + ctrl.irq_fwd = irq_fwd; + ctrl.priority = priority; +#else +#error "Not implemented." +#endif + + iowrite32be(ctrl.ctrl, &chan->ctrl); + + return 0; +} + + +/** + * @brief initialises the data parameters for a one dimensional DMA transfer + * + * @param chan a struct noc_dma_channel + * @param chan a struct noc_dma_transfer + * + * @returns 0 on success, EINVAL on error, EBUSY if channel is busy + */ + +static int noc_dma_init_transfer(struct noc_dma_channel *c, + struct noc_dma_transfer *t) +{ + + if (noc_dma_channel_busy(c)) + return -EBUSY; + + /* src == dst memory will result in a stuck DMA channel */ + /* TODO implement workaround via scratch buffer */ + if (((int) t->dst & 0xF0000000) == ((int) t->src & 0xF0000000)) { + pr_err(MSG "Cannot transfer withing same memory\n"); + return -EINVAL; + } + + /* no need to verify stride/size limits, they are implicity by type */ + + noc_dma_set_src(c, t->src); + noc_dma_set_dst(c, t->dst); + + noc_dma_set_strides_x(c, t->x_stride_src, t->x_stride_dst); + noc_dma_set_strides_y(c, t->y_stride_src, t->y_stride_dst); + + noc_dma_set_sizes(c, t->x_elem, t->y_elem); + + noc_dma_set_config(c, t->mtu, t->elem_size, t->irq_fwd, t->priority); + + return 0; +} + + +static void noc_dma_release_transfer(struct noc_dma_channel_node *c, + struct noc_dma_transfer *t) +{ + t->callback = NULL; + t->userdata = NULL; + + c->transfer = NULL; + + list_add_tail(&c->node, &dma_channel_pool_head); + list_add_tail(&t->node, &dma_transfer_pool_head); +} + + +/** + * @brief initialises the data parameters for a one dimensional DMA transfer + * + * @param chan a struct noc_dma_channel + * @param chan a struct noc_dma_transfer + * + * @returns 0 on success, EINVAL on error, EBUSY if channel is busy + */ + +static int noc_dma_execute_transfer(struct noc_dma_channel *c, + struct noc_dma_transfer *t) +{ + int ret; + + struct noc_dma_channel_node *n; + + + ret = noc_dma_init_transfer(c, t); + if (ret) { + n = container_of((struct noc_dma_channel **) &c, + struct noc_dma_channel_node, channel); + noc_dma_release_transfer(n, t); + return ret; + } + + ret = noc_dma_start_transfer(c); + if (ret) + return ret; + + return 0; +} + + +/** + * @brief reserve a NoC DMA channel + * @note the channel will automatically deregister on interrupt + * + * @param t a pointer to a struct noc_dma_transfer + * + * @warning channel will never be released when a transfer gets stuck, but + * @todo either rely on an (external) timer or write a timer management routine... (probably the best idea) + * @todo (if so, make it tickless...) + */ + +static inline +struct noc_dma_channel *noc_dma_request_channel(struct noc_dma_transfer *t) +{ + struct noc_dma_channel_node *c; + + + if(unlikely(list_empty(&dma_channel_pool_head))) + return NULL; + + c = list_entry((&dma_channel_pool_head)->next, + struct noc_dma_channel_node, node); + + list_del(&c->node); + + /* link transfer to node of this DMA channel */ + c->transfer = t; + +#ifdef CONFIG_NOC_DMA_STATS_COLLECT + noc_dma_stats.transfers_requested++; +#endif + return c->channel; +} + + +/** + * @brief program queued DMA transfers + * @note will program transfers until all available DMA channels are in use + * or queue is empty + */ + +static inline void noc_dma_program_transfer(void) +{ + struct noc_dma_channel *chan; + + struct noc_dma_transfer *p_elem = NULL; + struct noc_dma_transfer *p_tmp; + + + if(list_empty(&dma_transfer_queue_head)) + return; + + + list_for_each_entry_safe(p_elem, p_tmp, + &dma_transfer_queue_head, node) { + + chan = noc_dma_request_channel(p_elem); + + if (!chan) + break; + + list_del(&p_elem->node); + + noc_dma_execute_transfer(chan, p_elem); + +#ifdef CONFIG_NOC_DMA_STATS_COLLECT + noc_dma_stats.transfers_queued--; +#endif + } +} + + +/** + * @brief callback function for NoC DMA IRQs + * + * @param userdata userdata pointer supplied by the IRQ system + * + * @note will execute a caller-provideable callback notification function + * + * @return always 0 + * + * + */ + +/* XXX the user-supplied callbacks should not be executed in the ISR + * for obvious reasons. Need to fix that once we have threads. + */ +#warning "NoC DMA: user callbacks are executed in interrupt mode" + +static irqreturn_t noc_dma_service_irq_handler(void *userdata) +{ + struct noc_dma_transfer *t; + struct noc_dma_channel_node *c; + + + + /* every actual channel has its own node registered to the IRQ system */ + c = (struct noc_dma_channel_node *) userdata; + + t = c->transfer; + + if (t->callback) + t->callback(t->userdata); + + noc_dma_release_transfer(c, t); + +#ifdef CONFIG_NOC_DMA_STATS_COLLECT + noc_dma_stats.transfers_completed++; +#endif + /* activate queued */ + noc_dma_program_transfer(); + + + return 0; +} + + +static void noc_dma_register_transfer(struct noc_dma_transfer *t) +{ + list_add_tail(&t->node, &dma_transfer_queue_head); + noc_dma_program_transfer(); + +#ifdef CONFIG_NOC_DMA_STATS_COLLECT + noc_dma_stats.transfers_queued++; +#endif +} + + +static struct noc_dma_transfer *noc_dma_get_transfer_slot(void) +{ + struct noc_dma_transfer *t; + + + if(unlikely(list_empty(&dma_transfer_pool_head))) + return NULL; + + t = list_entry((&dma_transfer_pool_head)->next, + struct noc_dma_transfer, node); + + list_del(&t->node); + + return t; +} + + + +#ifdef CONFIG_NOC_DMA_STATS_COLLECT +/** + * @brief sysctl node setup + */ + +static int noc_dma_sysctl_setup(void) +{ + struct sysobj *sobj; + + + sobj = sysobj_create(); + if (!sobj) + return -ENOMEM; + + sobj->sattr = noc_dma_attributes; + sysobj_add(sobj, NULL, driver_set, "noc_dma"); + + return 0; +} +#endif /* CONFIG_NOC_DMA_STATS_COLLECT */ + + +/** + * @brief driver cleanup function + */ + +static int noc_dma_exit(void) +{ + printk(MSG "module_exit() not implemented\n"); + + return 0; +} + + +/** + * @brief driver initialisation + */ + +static int noc_dma_init(void) +{ + unsigned int i; + + + + pr_info(MSG "initialising\n"); + + + INIT_LIST_HEAD(&dma_channel_pool_head); + INIT_LIST_HEAD(&dma_transfer_pool_head); + INIT_LIST_HEAD(&dma_transfer_queue_head); + + for(i = 0; i < NOC_DMA_CHANNELS; i++) { + + dma_channel_pool[i].channel = &noc_dma->chan[i]; + dma_channel_pool[i].irl = NOC_GET_DMA_IRL(i); + + list_add_tail(&dma_channel_pool[i].node, + &dma_channel_pool_head); + +#ifdef CONFIG_SSDP +#warning "SSDP DMA channel IRQ numbers unknown, using MPPB as baseline" +#endif + /* DMA EIRQ numbers are 0...NOC_DMA_CHANNELS */ + irq_request(LEON_WANT_EIRQ(i), ISR_PRIORITY_NOW, + &noc_dma_service_irq_handler, + &dma_channel_pool[i]); + } + + for (i = 0; i < NOC_DMA_TRANSFER_QUEUE_SIZE; i++) { + list_add_tail(&dma_transfer_pool[i].node, + &dma_transfer_pool_head); + } + +#ifdef CONFIG_NOC_DMA_STATS_COLLECT + if (noc_dma_sysctl_setup()) + return -1; +#endif + + return 0; +} + +module_init(noc_dma_init); +module_exit(noc_dma_exit); +/* XXX define_initcall(noc_dma_init); */ + + + +/** + * @brief request an arbitrary DMA transfer + * + * @param src the source address + * @param dst the destination address + * + * @param x_elem the number of elements in x + * @param y_elem the number of elements in y + * @param size the element size (BYTE, HALFWORD, WORD, DOUBLEWORD) + * + * @param x_stride_src the width of stride in source x + * @param x_stride_dst the width of stride in destination x + * + * @param y_stride_src the width of stride in source y + * @param y_stride_dst the width of stride in destination y + * + * @param mtu the maximum transfer unit of a NoC packet + * + * @param callback a user-supplied callback function, executed on successful + * transfer + * @param userdata a pointer to arbitrary userdata, passed to callback function + */ + +int noc_dma_req_xfer(void *src, void *dst, uint16_t x_elem, uint16_t y_elem, + enum noc_dma_elem_size elem_size, + int16_t x_stride_src, int16_t x_stride_dst, + int16_t y_stride_src, int16_t y_stride_dst, + enum noc_dma_priority dma_priority, uint16_t mtu, + int (*callback)(void *), void *userdata) +{ + struct noc_dma_transfer *t; + + + + if (!src) + return -EINVAL; + + if (!dst) + return -EINVAL; + + if(!x_elem) + return -EINVAL; + + if(!y_elem) + return -EINVAL; + +#ifdef CONFIG_NOC_DMA_SAME_MASTER_TRANSFER_WORKAROUND + if (((int) dst & 0xF0000000) == ((int) src & 0xF0000000)) { + int ret; + + /* at least catch that -.- */ + if (((int) src & 0xF0000000) == + (NOC_SCRATCH_BUFFER_BASE & 0xF0000000)) + return -EINVAL; + + pr_crit(MSG "Transfer withing same memory requested, enabling " + "workaround via scratch buffer. The result of this " + "transfer may not be as desired. You have been " + "warned.\n"); + ret = noc_dma_req_xfer(src, (void *) NOC_SCRATCH_BUFFER_BASE, + x_elem, y_elem, elem_size, + x_stride_src, x_stride_dst, + y_stride_src, y_stride_dst, + HIGH, mtu, NULL, NULL); + if (ret) + return ret; + + /* this would actually work fine, if we had enough scratch + * buffer for all 8 channels or at least some management of it + * let's mark this as TODO + */ + src = (void *) NOC_SCRATCH_BUFFER_BASE; + dma_priority = LOW; + } +#endif + + t = noc_dma_get_transfer_slot(); + if(!t) + return -EBUSY; + + t->src = src; + t->dst = dst; + + t->x_elem = x_elem; + t->y_elem = y_elem; + + t->elem_size = elem_size; + + t->x_stride_src = x_stride_src; + t->x_stride_dst = x_stride_dst; + + t->y_stride_src = y_stride_src; + t->y_stride_dst = y_stride_dst; + + t->mtu = mtu; + + /* transfers registered here will be auto-released */ + t->irq_fwd = BOTH; + + t->priority = dma_priority; + + t->callback = callback; + t->userdata = userdata; + + noc_dma_register_transfer(t); + + + return 0; +} +EXPORT_SYMBOL(noc_dma_req_xfer); + + +/** + * @brief request a linear array DMA transfer + * + * @param src the source address + * @param dst the destination address + * + * @param elem the number of elements + * @param size the element size (BYTE, HALFWORD, WORD, DOUBLEWORD) + * + * @param mtu the maximum transfer unit of a NoC packet + * + * @param callback a user-supplied callback function, executed on successful + * transfer + * @param userdata a pointer to arbitrary userdata, passed to callback function + */ + +int noc_dma_req_lin_xfer(void *src, void *dst, + uint16_t elem, enum noc_dma_elem_size elem_size, + enum noc_dma_priority dma_priority, uint16_t mtu, + int (*callback)(void *), void *userdata) +{ + return noc_dma_req_xfer(src, dst, elem, 1, elem_size, + 1, 1, 1, 1, LOW, mtu, callback, userdata); +} +EXPORT_SYMBOL(noc_dma_req_lin_xfer); diff --git a/lib/Makefile b/lib/Makefile index c5c84f0d404b8b95c407b82f28495af0810f52ae..a0d140f8548b76aebc27a67a1597e5ebc55097bb 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -3,3 +3,4 @@ lib-$(CONFIG_MM) += mm.o lib-$(CONFIG_PAGE_MAP) += page.o lib-$(CONFIG_AR) += ar.o lib-$(CONFIG_CHUNK) += chunk.o +lib-y += string.o diff --git a/lib/string.c b/lib/string.c new file mode 100644 index 0000000000000000000000000000000000000000..c1d537e351599c051d95af905247ae4282e1a1b6 --- /dev/null +++ b/lib/string.c @@ -0,0 +1,264 @@ +/*+ + * @file lib/string.c + * + * @note some of theses are just wrappers + * @todo these functions are very generic, for performance, add architecture + * specific implementations + */ + + +#include <kernel/export.h> +#include <kernel/types.h> + + +/** + * @brief compare two strings s1 and s2 + * + * @returns <0, 0 or > 0 if s1 is less than, matches or greater than s2 + */ + +#include <kernel/printk.h> +int strcmp(const char *s1, const char *s2) +{ + unsigned char c1, c2; + + do { + c1 = (*(s1++)); + c2 = (*(s2++)); + + if (c1 != c2) { + if (c1 < c2) + return -1; + else + return 1; + } + + } while (c1); + + return 0; +} +EXPORT_SYMBOL(strcmp); + +/** + * @brief locate first occurrence of a set of characters in string accept + * + * @param s the string to be searched + * @param accept the characters to search for + * + * @returns a pointer to the character in s that matches one of the characters + * in accept + */ + +char *strpbrk(const char *s, const char *accept) +{ + const char *c1, *c2; + + + for (c1 = s; *c1 != '\0'; c1++) + for (c2 = accept; (*c2) != '\0'; c2++) + if ((*c1) == (*c2)) + return (char *) c1; + + return NULL; +} +EXPORT_SYMBOL(strpbrk); + + + +/** + * @brief split a string into tokens + * + * @param stringp the string to be searched + * @param delim the characters to search for + * + * @note stringp is updated to point past the token + * + * @returns the original location of stringp + */ + +char *strsep(char **stringp, const char *delim) +{ + char *start; + char *end; + + + start = *stringp; + + if (!start) + return NULL; + + end = strpbrk(start, delim); + + if (end) { + (*end)++; + (*end) = '\0'; + } + + *stringp = end; + + return start; +} +EXPORT_SYMBOL(strsep); + + +/** + * @brief calculate the length of a string + * + * @param s the string + * + * @returns the number of characters in s + */ + +size_t strlen(const char *s) +{ + const char *c; + + + for (c = s; (*c) != '\0'; c++); + + return c - s; +} +EXPORT_SYMBOL(strlen); + + +/** + * @brief copy a memory area + * + * @param dest the destination memory area + * @param src the source memory area + * @param n the number of bytes to copy + * + * @returns a pointer to dest + */ + +void *memcpy(void *dest, const void *src, size_t n) +{ + char *d; + + const char *s; + + + d = dest; + s = src; + + while (n--) { + (*d) = (*s); + d++; + s++; + } + + return dest; +} + +EXPORT_SYMBOL(memcpy); + + + + +#include <stdarg.h> +#include <limits.h> + +int vsnprintf(char *str, size_t size, const char *format, va_list ap); + +/** + * @brief print a string into a buffer + * + * @param str the destination buffer + * @param format the format string buffer + * @param ... arguments to the format string + * + * @return the number of characters written to buf + */ + +int sprintf(char *str, const char *format, ...) +{ + int n; + va_list ap; + + + va_start(ap, format); + n = vsnprintf(str, INT_MAX, format, ap); + va_end(ap); + + return n; +} +EXPORT_SYMBOL(sprintf); + + +/** + * @brief check if a character is a white space + * + * @param c the character to test + * + * @returns 0 if not a space + * + * @note c must have the value of an unsigned char or EOF + * + */ + +int isspace(int c) +{ + if (c == ' ') + return 1; + + if (c == '\t') + return 1; + + if (c == '\n') + return 1; + + if (c == '\v') + return 1; + + if (c == '\f') + return 1; + + if (c == '\r') + return 1; + + + return 0; +} +EXPORT_SYMBOL(isspace); + +/** + * @brief convert a string to an integer + * + * @param nptr the string to convert + * + * @returns res the converted integer + */ + +int atoi(const char *nptr) +{ + unsigned int d; + + int res = 0; + int neg= 0; + + + for (; isspace(*nptr); ++nptr); + + if (*nptr == '-') { + nptr++; + neg = 1; + } else if (*nptr == '+') { + nptr++; + } + + while (1) { + d = (*nptr) - '0'; + + if (d > 9) + break; + + res = res * 10 + (int) d; + nptr++; + } + + if (neg) + res = -res; + + return res; +} +EXPORT_SYMBOL(atoi); diff --git a/lib/sysctl.c b/lib/sysctl.c index 03394426c6bec39ac8ac5651d2c8d99364884039..34412d54a38298c1cd016363bf9a79dd814fd5cd 100644 --- a/lib/sysctl.c +++ b/lib/sysctl.c @@ -46,24 +46,23 @@ * @note explicit references to linux source files are not always given */ +#include <kernel/printk.h> +#include <kernel/kmem.h> +#include <kernel/sysctl.h> +#include <kernel/export.h> +#include <kernel/string.h> -#include <stdio.h> - -#include <stdlib.h> -#include <string.h> #include <errno.h> #include <list.h> -#include <kernel/sysctl.h> - +/* our standard sets */ struct sysset *sys_set; struct sysset *driver_set; - static struct sysset *sysset_get(struct sysset *s); @@ -158,7 +157,7 @@ struct sysobj *sysobj_create(void) struct sysobj *sobj; - sobj = malloc(sizeof(*sobj)); + sobj = (struct sysobj *) kmalloc(sizeof(*sobj)); if (!sobj) return NULL; @@ -167,7 +166,7 @@ struct sysobj *sysobj_create(void) return sobj; } - +EXPORT_SYMBOL(sysobj_create); /** * @brief add a sysobject and optionally set its sysset as parent @@ -265,12 +264,12 @@ void sysobj_list_attr(struct sysobj *sobj) sattr = sobj->sattr; - printf("{"); + printk("{"); while ((*sattr)) { - printf(" %s", (*sattr)->name); + printk(" %s", (*sattr)->name); sattr++; } - printf(" }"); + printk(" }"); } @@ -415,13 +414,11 @@ struct sysset *sysset_create(const char *name, { struct sysset *sysset; - sysset = malloc(sizeof(*sysset)); + sysset = kcalloc(1, sizeof(*sysset)); if (!sysset) return NULL; - bzero(sysset, sizeof(*sysset)); - sysobj_set_name(&sysset->sobj, name); @@ -470,7 +467,7 @@ struct sysset *sysset_create_and_add(const char *name, __extension__ struct sysobj *sysset_find_obj(struct sysset *sysset, const char *path) { - char str[256]; + char str[256]; /* XXX */ char *token; struct sysobj *s; @@ -480,7 +477,9 @@ struct sysobj *sysset_find_obj(struct sysset *sysset, const char *path) memcpy(str, path, strlen(path) + 1); - token = strtok(str, "/"); + token = str; + + strsep(&token, "/"); /* root node */ if(strcmp(sysobj_name(&sysset->sobj), token)) @@ -488,7 +487,7 @@ struct sysobj *sysset_find_obj(struct sysset *sysset, const char *path) while (1) { - token = strtok(NULL, "/"); + strsep(&token, "/"); if (!token) return ret; @@ -532,21 +531,21 @@ void sysset_show_tree(struct sysset *sysset) rec++; for(i = 0; i < rec; i++) - printf(" "); + printk(" "); - printf("|__ %s\n", sysobj_name(&sysset->sobj)); + printk("|__ %s\n", sysobj_name(&sysset->sobj)); list_for_each_entry(k, &sysset->list, entry) { if (!k->child) { for(i = 0; i < rec+1; i++) - printf(" "); - printf("|__ %s ", sysobj_name(k)); + printk(" "); + printk("|__ %s ", sysobj_name(k)); sysobj_list_attr(k); - printf("\n"); + printk("\n"); } else { sysset_show_tree(container_of(k->child, struct sysset, sobj)); diff --git a/samples/noc_dma/noc_dma_demo.c b/samples/noc_dma/noc_dma_demo.c new file mode 100644 index 0000000000000000000000000000000000000000..fd78022356bf754df4dc9a6eed43fd779bf65dea --- /dev/null +++ b/samples/noc_dma/noc_dma_demo.c @@ -0,0 +1,79 @@ +#include <kernel/kmem.h> +#include <kernel/kernel.h> +#include <kernel/printk.h> +#include <noc_dma.h> +#include <noc.h> + +static void noc_dma_2d_print(unsigned long *p, size_t x_sz, size_t y_sz) +{ + size_t x; + size_t y; + + + for (y = 0; y < y_sz; y++) { + for (x = 0; x < x_sz; x++) { + if (p[x + y * x_sz]) + printk("%02x ", p[x + y * x_sz]); + else + printk(" . ", p[x + y * x_sz]); + } + printk("\n"); + } +} + + +/** + * @brief execute transfer types 1-4 and reproduce equivalent figure + * @see MPPB datasheet v4.03, p62, Tbl. 73 + */ + +void noc_dma_test_transfers(void) +{ + int i; + + unsigned long *src; + unsigned long *dst; + + + const uint16_t x_size = 16; + const uint16_t y_size = 19; + + + src = kcalloc(x_size * y_size, sizeof(unsigned long)); + BUG_ON(!src); + + dst = (unsigned long *) NOC_SCRATCH_BUFFER_BASE; + BUG_ON(!dst); + + /* initialise */ + for (i = 0; i < (x_size * y_size); i++) { + src[i] = i + 1; + dst[i] = 0; + } + /* base */ + BUG_ON(noc_dma_req_lin_xfer(src, (char *) dst + 0x0, 32, WORD, LOW, 256, + NULL, NULL)); + /* type 1 */ + BUG_ON(noc_dma_req_xfer(src, (char *) dst + 0x100, 16, 1, WORD, + 2, 1, 0, 0, LOW, 256, + NULL, NULL)); + + /* type 2 */ + BUG_ON(noc_dma_req_xfer(src, (char *) dst + 0x1C0, 6, 2, WORD, + 1, 1, 16, 16, LOW, 256, + NULL, NULL)); + + /* type 3 */ + BUG_ON(noc_dma_req_xfer(src, (char *) dst + 0x2C0, 4, 2, WORD, + 1, 16, 4, 1, LOW, 256, + NULL, NULL)); + /* type 4 */ + BUG_ON(noc_dma_req_xfer(src, (char *) dst + 0x44C, 4, 4, WORD, + 1, -1, 4, 4, LOW, 256, + NULL, NULL)); + printk("\n--- {DST} ---\n"); + noc_dma_2d_print(dst, x_size, y_size); + + kfree(src); +} +