1/* 2 * Copyright 2019, Data61 3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO) 4 * ABN 41 687 119 230. 5 * 6 * This software may be distributed and modified according to the terms of 7 * the BSD 2-Clause license. Note that NO WARRANTY is provided. 8 * See "LICENSE_BSD2.txt" for details. 9 * 10 * @TAG(DATA61_BSD) 11 */ 12 13#include <stdio.h> 14#include <assert.h> 15#include <errno.h> 16#include <stdlib.h> 17 18#include <utils/util.h> 19 20#include <platsupport/timer.h> 21#include <platsupport/plat/pwm.h> 22 23 24#define PWMSCALE_MASK MASK(4) 25#define PWMSTICKY BIT(8) 26#define PWMZEROCMP BIT(9) 27#define PWMENALWAYS BIT(12) 28#define PWMENONESHOT BIT(13) 29#define PWMCMP0IP BIT(28) 30#define PWMCMP1IP BIT(29) 31#define PWMCMP2IP BIT(30) 32#define PWMCMP3IP BIT(31) 33#define PWMCMP_WIDTH 16 34#define PWMCMP_MASK MASK(PWMCMP_WIDTH) 35 36/* Largest timeout that can be programmed */ 37#define MAX_TIMEOUT_NS (BIT(31) * (NS_IN_S / PWM_INPUT_FREQ)) 38 39 40int pwm_start(pwm_t *pwm) 41{ 42 pwm->pwm_map->pwmcfg |= PWMENALWAYS; 43 return 0; 44} 45 46int pwm_stop(pwm_t *pwm) 47{ 48 /* Disable timer. */ 49 pwm->pwm_map->pwmcmp0 = PWMCMP_MASK; 50 pwm->pwm_map->pwmcfg &= ~(PWMENALWAYS|PWMENONESHOT|PWMCMP0IP|PWMCMP1IP|PWMCMP2IP|PWMCMP3IP); 51 pwm->pwm_map->pwmcount = 0; 52 53 return 0; 54} 55 56int pwm_set_timeout(pwm_t *pwm, uint64_t ns, bool periodic) 57{ 58 if(pwm->mode == UPCOUNTER) { 59 ZF_LOGE("pwm is in UPCOUNTER mode and doesn't support setting timeouts."); 60 return -1; 61 } 62 // Clear whatever state the timer is in. 63 pwm_stop(pwm); 64 size_t num_ticks = ns / (NS_IN_S / PWM_INPUT_FREQ); 65 if (num_ticks > MAX_TIMEOUT_NS) { 66 ZF_LOGE("Cannot program a timeout larget than %ld ns", MAX_TIMEOUT_NS); 67 return -1; 68 } 69 70 /* We calculate the prescale by dividing the number of ticks we need 71 * by the width of the comparison register. The remainder is how much 72 * we want to prescale by, however the prescale value needs to be a 73 * power of 2 so we take the log2() and then increment it by 1 if it 74 * would otherwise be too low. 75 */ 76 size_t prescale = num_ticks >> (PWMCMP_WIDTH); 77 size_t base_2 = LOG_BASE_2(prescale); 78 if (BIT(base_2)< prescale) { 79 base_2++; 80 } 81 assert(prescale < BIT(PWMSCALE_MASK)); 82 83 /* There will be a loss of resolution by this shift. 84 * We add 1 so that we sleep for at least as long as needed. 85 */ 86 pwm->pwm_map->pwmcmp0 = (num_ticks >> base_2) +1; 87 /* assert we didn't overflow... */ 88 assert((num_ticks >> base_2) +1); 89 /* Reset the counter mode and prescaler, for some reason this doesn't work in pwm_stop */ 90 pwm->pwm_map->pwmcfg &= ~(PWMSCALE_MASK); 91 if (periodic) { 92 pwm->pwm_map->pwmcfg |= PWMENALWAYS | (base_2 & PWMSCALE_MASK); 93 } else { 94 pwm->pwm_map->pwmcfg |= PWMENONESHOT | (base_2 & PWMSCALE_MASK); 95 } 96 97 return 0; 98} 99 100void pwm_handle_irq(pwm_t *pwm, uint32_t irq) 101{ 102 if(pwm->mode == UPCOUNTER) { 103 pwm->time_h++; 104 } 105 106 pwm->pwm_map->pwmcfg &= ~(PWMCMP0IP|PWMCMP1IP|PWMCMP2IP|PWMCMP3IP); 107} 108 109uint64_t pwm_get_time(pwm_t *pwm) 110{ 111 /* Include unhandled interrupt. */ 112 if (pwm->pwm_map->pwmcfg & PWMCMP0IP) { 113 pwm->time_h++; 114 pwm->pwm_map->pwmcfg &= ~PWMCMP0IP; 115 } 116 117 uint64_t num_ticks = (pwm->time_h * (PWMCMP_MASK << PWMSCALE_MASK) + pwm->pwm_map->pwmcount); 118 uint64_t time = num_ticks * (NS_IN_S / PWM_INPUT_FREQ); 119 return time; 120} 121 122int pwm_init(pwm_t *pwm, pwm_config_t config) 123{ 124 pwm->pwm_map = (volatile struct pwm_map*) config.vaddr; 125 uint8_t scale = 0; 126 if (config.mode == UPCOUNTER) { 127 scale = PWMSCALE_MASK; 128 } 129 pwm->mode = config.mode; 130 pwm->pwm_map->pwmcmp0 = PWMCMP_MASK; 131 pwm->pwm_map->pwmcmp1 = PWMCMP_MASK; 132 pwm->pwm_map->pwmcmp2 = PWMCMP_MASK; 133 pwm->pwm_map->pwmcmp3 = PWMCMP_MASK; 134 pwm->pwm_map->pwmcfg = (scale & PWMSCALE_MASK) | PWMZEROCMP | PWMSTICKY; 135 136 return 0; 137} 138