| /* |
| * Copyright 2014, NICTA |
| * |
| * This software may be distributed and modified according to the terms of |
| * the BSD 2-Clause license. Note that NO WARRANTY is provided. |
| * See "LICENSE_BSD2.txt" for details. |
| * |
| * @TAG(NICTA_BSD) |
| */ |
| |
| #include "timer_common.h" |
| #include <string.h> |
| #include <stdlib.h> |
| #include <utils/util.h> |
| #include <vka/capops.h> |
| #include <sel4platsupport/device.h> |
| |
| #ifdef CONFIG_LIB_SEL4_VSPACE |
| |
| static void |
| timer_common_destroy_frame_internal(timer_common_data_t *data, vka_t *vka, vspace_t *vspace) |
| { |
| if (data->vaddr != NULL) { |
| vspace_unmap_pages(vspace, data->vaddr, 1, seL4_PageBits, VSPACE_PRESERVE); |
| } |
| if (data->frame.capPtr != 0) { |
| vka_cnode_delete(&data->frame); |
| vka_cspace_free(vka, data->frame.capPtr); |
| } |
| } |
| |
| void |
| timer_common_destroy_frame(seL4_timer_t *timer, vka_t *vka, vspace_t *vspace) |
| { |
| timer_common_data_t *timer_data = (timer_common_data_t *) timer->data; |
| if (timer_data != NULL) { |
| timer_common_destroy_frame_internal(timer_data, vka, vspace); |
| } |
| } |
| |
| static void |
| timer_common_destroy_internal(timer_common_data_t *timer_data, vka_t *vka, vspace_t *vspace) |
| { |
| if (timer_data != NULL) { |
| timer_common_destroy_frame_internal(timer_data, vka, vspace); |
| if (timer_data->irq != 0) { |
| seL4_IRQHandler_Clear(timer_data->irq); |
| timer_common_cleanup_irq(vka, timer_data->irq); |
| } |
| free(timer_data); |
| } |
| } |
| |
| void |
| timer_common_handle_irq(seL4_timer_t *timer, uint32_t irq) |
| { |
| timer_common_data_t *data = (timer_common_data_t *) timer->data; |
| timer_handle_irq(timer->timer, irq); |
| int error = seL4_IRQHandler_Ack(data->irq); |
| if (error != seL4_NoError) { |
| ZF_LOGE("Failed to ack irq %d, error %d", irq, error); |
| } |
| } |
| |
| void |
| timer_common_cleanup_irq(vka_t *vka, seL4_CPtr irq) |
| { |
| cspacepath_t path; |
| vka_cspace_make_path(vka, irq, &path); |
| vka_cnode_delete(&path); |
| vka_cspace_free(vka, irq); |
| } |
| |
| timer_common_data_t * |
| timer_common_init_frame(vspace_t *vspace, simple_t *simple, vka_t *vka, void *paddr) |
| { |
| int error; |
| timer_common_data_t *timer_data = NULL; |
| |
| timer_data = malloc(sizeof(timer_common_data_t)); |
| if (timer_data == NULL) { |
| ZF_LOGE("Failed to allocate timer_common_data_t size: %zu\n", sizeof(timer_common_data_t)); |
| goto error; |
| } |
| memset(timer_data, 0, sizeof(timer_common_data_t)); |
| |
| error = sel4platsupport_copy_frame_cap(vka, simple, paddr, seL4_PageBits, &timer_data->frame); |
| if (error != seL4_NoError) { |
| goto error; |
| } |
| |
| /* map in the frame */ |
| timer_data->vaddr = vspace_map_pages(vspace, &timer_data->frame.capPtr, NULL, seL4_AllRights, |
| 1, seL4_PageBits, 0); |
| ZF_LOGV("Mapped timer at %p\n", timer_data->vaddr); |
| if (timer_data->vaddr == NULL) { |
| ZF_LOGE("Failed to map page at vaddr %p\n", timer_data->vaddr); |
| goto error; |
| } |
| |
| return timer_data; |
| error: |
| timer_common_destroy_internal(timer_data, vka, vspace); |
| return NULL; |
| } |
| |
| timer_common_data_t * |
| timer_common_init(vspace_t *vspace, simple_t *simple, |
| vka_t *vka, seL4_CPtr notification, uint32_t irq_number, void *paddr) |
| { |
| int error; |
| cspacepath_t path; |
| timer_common_data_t *timer_data; |
| |
| timer_data = timer_common_init_frame(vspace, simple, vka, paddr); |
| if (timer_data == NULL) { |
| goto error; |
| } |
| |
| error = sel4platsupport_copy_irq_cap(vka, simple, irq_number, &path); |
| timer_data->irq = path.capPtr; |
| if (error != seL4_NoError) { |
| goto error; |
| } |
| |
| /* set the end point */ |
| error = seL4_IRQHandler_SetNotification(timer_data->irq, notification); |
| if (error != seL4_NoError) { |
| ZF_LOGE("seL4_IRQHandler_SetNotification failed with error %d\n", error); |
| goto error; |
| } |
| |
| ZF_LOGV("Timer initialised\n"); |
| /* success */ |
| return timer_data; |
| |
| error: |
| /* clean up on failure */ |
| timer_common_destroy_internal(timer_data, vka, vspace); |
| |
| return NULL; |
| } |
| |
| void |
| timer_common_destroy(seL4_timer_t *timer, vka_t *vka, vspace_t *vspace) |
| { |
| timer_common_data_t *data = (timer_common_data_t *) timer->data; |
| if (timer->timer) { |
| timer_stop(timer->timer); |
| } |
| timer_common_destroy_internal(data, vka, vspace); |
| free(timer); |
| } |
| |
| #endif /* CONFIG_LIB_SEL4_VSPACE */ |