/**************************************************************************** * arch/mips/src/pic32mz/pic32mz_timer_lowerhalf.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "pic32mz_timer.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* One of the type B timers should be defined. */ #if defined(CONFIG_TIMER) && defined(CONFIG_PIC32MZ_TIMER) /**************************************************************************** * Private Types ****************************************************************************/ /* This structure provides the private representation of the "lower-half" * driver state structure. This structure must be cast-compatible with the * timer_lowerhalf_s structure. */ struct pic32mz_lowerhalf_s { FAR const struct timer_ops_s *ops; /* Lower half operations */ FAR struct pic32mz_timer_dev_s *timer; /* pic32mz timer driver */ tccb_t callback; /* Current user interrupt cb */ FAR void *arg; /* Argument to upper half cb */ uint32_t timeout; /* Current timeout value (us) */ uint32_t ticks; /* Timeout converted in ticks */ uint32_t freq; /* Timer's frequency (Hz) */ uint8_t width; /* Timer's width */ uint32_t maxticks; /* Maximum ticks for this timer */ bool started; /* True: Timer has been started */ }; /**************************************************************************** * Private Function Prototypes ****************************************************************************/ /* Helper functions *********************************************************/ static uint32_t pic32mz_usec2ticks(struct pic32mz_lowerhalf_s *priv, uint32_t usecs); static uint32_t pic32mz_ticks2usec(struct pic32mz_lowerhalf_s *priv, uint32_t ticks); /* Interrupt handling *******************************************************/ static int pic32mz_timer_handler(int irq, void * context, void * arg); /* "Lower half" driver methods **********************************************/ static int pic32mz_start(FAR struct timer_lowerhalf_s *lower); static int pic32mz_stop(FAR struct timer_lowerhalf_s *lower); static int pic32mz_getstatus(struct timer_lowerhalf_s *lower, struct timer_status_s *status); static int pic32mz_settimeout(FAR struct timer_lowerhalf_s *lower, uint32_t timeout); static void pic32mz_setcallback(FAR struct timer_lowerhalf_s *lower, tccb_t callback, FAR void *arg); static int pic32mz_ioctl(struct timer_lowerhalf_s *lower, int cmd, unsigned long arg); static int pic32mz_maxtimeout(FAR struct timer_lowerhalf_s *lower, FAR uint32_t *maxtimeout); /**************************************************************************** * Private Data ****************************************************************************/ /* "Lower half" driver methods */ static const struct timer_ops_s g_timer_ops = { .start = pic32mz_start, .stop = pic32mz_stop, .getstatus = pic32mz_getstatus, .settimeout = pic32mz_settimeout, .setcallback = pic32mz_setcallback, .ioctl = pic32mz_ioctl, .maxtimeout = pic32mz_maxtimeout, }; #ifdef CONFIG_PIC32MZ_T2 static struct pic32mz_lowerhalf_s g_t2_lowerhalf = { .ops = &g_timer_ops, }; #endif #ifdef CONFIG_PIC32MZ_T3 static struct pic32mz_lowerhalf_s g_t3_lowerhalf = { .ops = &g_timer_ops, }; #endif #ifdef CONFIG_PIC32MZ_T4 static struct pic32mz_lowerhalf_s g_t4_lowerhalf = { .ops = &g_timer_ops, }; #endif #ifdef CONFIG_PIC32MZ_T5 static struct pic32mz_lowerhalf_s g_t5_lowerhalf = { .ops = &g_timer_ops, }; #endif #ifdef CONFIG_PIC32MZ_T6 static struct pic32mz_lowerhalf_s g_t6_lowerhalf = { .ops = &g_timer_ops, }; #endif #ifdef CONFIG_PIC32MZ_T7 static struct pic32mz_lowerhalf_s g_t7_lowerhalf = { .ops = &g_timer_ops, }; #endif #ifdef CONFIG_PIC32MZ_T8 static struct pic32mz_lowerhalf_s g_t8_lowerhalf = { .ops = &g_timer_ops, }; #endif #ifdef CONFIG_PIC32MZ_T9 static struct pic32mz_lowerhalf_s g_t9_lowerhalf = { .ops = &g_timer_ops, }; #endif /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: pic32mz_usec2ticks * * Description: * Convert microseconds to timer clock ticks. * * Input Parameters: * priv A pointer to a private timer driver lower half instance * usecs The number of usecs to convert * * Returned Value: * The time converted to clock ticks. * ****************************************************************************/ static uint32_t pic32mz_usec2ticks(FAR struct pic32mz_lowerhalf_s *priv, uint32_t usecs) { uint64_t bigticks; bigticks = ((uint64_t)usecs * (uint64_t)priv->freq) / 1000000; if (bigticks > UINT32_MAX) { return UINT32_MAX; } return (uint32_t)bigticks; } /**************************************************************************** * Name: pic32mz_ticks2usec * * Description: * Convert timer clock ticks to microseconds. * * Input Parameters: * priv A pointer to a private timer driver lower half instance * usecs The number of ticks to convert * * Returned Value: * The time converted to microseconds. * ****************************************************************************/ static uint32_t pic32mz_ticks2usec(FAR struct pic32mz_lowerhalf_s *priv, uint32_t ticks) { uint64_t bigusec; bigusec = (1000000ull * (uint64_t)ticks) / priv->freq; if (bigusec > UINT32_MAX) { return UINT32_MAX; } return (uint32_t)bigusec; } /**************************************************************************** * Name: pic32mz_timer_handler * * Description: * timer interrupt handler * * Input Parameters: * * Returned Value: * ****************************************************************************/ static int pic32mz_timer_handler(int irq, FAR void *context, FAR void *arg) { FAR struct pic32mz_lowerhalf_s *lower = (struct pic32mz_lowerhalf_s *) arg; uint32_t next_interval_us = 0; PIC32MZ_TIMER_ACKINT(lower->timer); if (lower->callback && lower->callback(&next_interval_us, lower->arg)) { if (next_interval_us > 0) { /* Stop the timer before writing the new period, * to prevent unintended period matches. */ PIC32MZ_TIMER_STOP(lower->timer); PIC32MZ_TIMER_SETPERIOD(lower->timer, next_interval_us); PIC32MZ_TIMER_START(lower->timer); } } else { pic32mz_stop((struct timer_lowerhalf_s *)lower); } return OK; } /**************************************************************************** * Name: pic32mz_start * * Description: * Start the timer, resetting the time to the current timeout, * * Input Parameters: * lower A pointer the publicly visible representation of the * "lower-half" driver state structure. * * Returned Value: * Zero on success; a negated errno value on failure. * ****************************************************************************/ static int pic32mz_start(FAR struct timer_lowerhalf_s *lower) { FAR struct pic32mz_lowerhalf_s *priv = (FAR struct pic32mz_lowerhalf_s *)lower; if (!priv->started) { if (priv->callback != NULL) { PIC32MZ_TIMER_SETISR(priv->timer, pic32mz_timer_handler, priv); } PIC32MZ_TIMER_START(priv->timer); priv->started = true; return OK; } /* Return EBUSY to indicate that the timer was already running */ return -EBUSY; } /**************************************************************************** * Name: pic32mz_stop * * Description: * Stop the timer * * Input Parameters: * lower A pointer the publicly visible representation of the "lower-half" * driver state structure. * * Returned Value: * Zero on success; a negated errno value on failure. * ****************************************************************************/ static int pic32mz_stop(FAR struct timer_lowerhalf_s *lower) { FAR struct pic32mz_lowerhalf_s *priv = (FAR struct pic32mz_lowerhalf_s *)lower; if (priv->started) { PIC32MZ_TIMER_STOP(priv->timer); PIC32MZ_TIMER_SETISR(priv->timer, NULL, NULL); priv->started = false; return OK; } /* Return ENODEV to indicate that the timer was not running */ return -ENODEV; } /**************************************************************************** * Name: pic32mz_getstatus * * Description: * Get the current timer status * * Input Parameters: * lower A pointer the publicly visible representation of the * "lower-half" driver state structure. * status The location to return the status information. * * Returned Value: * Zero on success; a negated errno value on failure. * ****************************************************************************/ static int pic32mz_getstatus(FAR struct timer_lowerhalf_s *lower, FAR struct timer_status_s *status) { FAR struct pic32mz_lowerhalf_s *priv = (FAR struct pic32mz_lowerhalf_s *)lower; uint32_t remainingticks; DEBUGASSERT(priv); /* Return the status bit */ status->flags = 0; if (priv->started) { status->flags |= TCFLAGS_ACTIVE; } if (priv->callback) { status->flags |= TCFLAGS_HANDLER; } /* Return the actual timeout in microseconds */ status->timeout = priv->timeout; /* Get the remaining ticks for the next overflow. */ remainingticks = priv->ticks - PIC32MZ_TIMER_GETCOUNTER(priv->timer); /* Convert the remaining ticks to microseconds. */ status->timeleft = pic32mz_ticks2usec(priv, remainingticks); return OK; } /**************************************************************************** * Name: pic32mz_settimeout * * Description: * Set a new timeout value (and reset the timer) * * Input Parameters: * lower A pointer the publicly visible representation of the * "lower-half" driver state structure. * timeout The new timeout value in microseconds. * * Returned Value: * Zero on success; a negated errno value on failure. * ****************************************************************************/ static int pic32mz_settimeout(FAR struct timer_lowerhalf_s *lower, uint32_t timeout) { FAR struct pic32mz_lowerhalf_s *priv = (FAR struct pic32mz_lowerhalf_s *)lower; DEBUGASSERT(priv); if (priv->started) { return -EPERM; } tmrinfo("Entry: timeout = %d\n", timeout); priv->timeout = timeout; /* Convert the timeout to ticks. */ priv->ticks = pic32mz_usec2ticks(priv, timeout); tmrinfo("Timeout ticks = %ld\n", priv->ticks); if (priv->ticks == 0) { tmrerr("The timeout value can't be represented by " "the current config! Try a different prescaler.\n"); /* REVISIT: This might be done by trying all the prescale values * (there aren't that much) and find the first that can represent * these ticks. */ return -EAGAIN; } else if (priv->ticks > priv->maxticks) { tmrwarn("Timeout value truncated! Consider a different prescaler.\n"); /* REVISIT: Try a different prescale value first. */ PIC32MZ_TIMER_SETPERIOD(priv->timer, priv->maxticks); } else { PIC32MZ_TIMER_SETPERIOD(priv->timer, priv->ticks); } return OK; } /**************************************************************************** * Name: pic32mz_setcallback * * Description: * Call this user provided timeout callback. * * Input Parameters: * lower A pointer the publicly visible representation of the * "lower-half" driver state structure. * callback The new timer expiration function pointer. If this * function pointer is NULL, then the reset-on-expiration * behavior is restored. * arg Argument that will be provided in the callback. * * Returned Value: * The previous timer expiration function pointer or NULL is there was * no previous function pointer. * ****************************************************************************/ static void pic32mz_setcallback(FAR struct timer_lowerhalf_s *lower, tccb_t callback, FAR void *arg) { FAR struct pic32mz_lowerhalf_s *priv = (FAR struct pic32mz_lowerhalf_s *)lower; irqstate_t flags = enter_critical_section(); DEBUGASSERT(priv); /* Save the new callback */ priv->callback = callback; priv->arg = arg; leave_critical_section(flags); } /**************************************************************************** * Name: pic32mz_ioctl * * Description: * Any ioctl commands that are not recognized by the "upper-half" driver * are forwarded to the lower half driver through this method. * * Input Parameters: * lower A pointer the publicly visible representation of the "lower-half" * driver state structure. * cmd The ioctl command value * arg The optional argument that accompanies the 'cmd'. The * interpretation of this argument depends on the particular * command. * * Returned Value: * Zero on success; a negated errno value on failure. * ****************************************************************************/ static int pic32mz_ioctl(FAR struct timer_lowerhalf_s *lower, int cmd, unsigned long arg) { int ret = -ENOTTY; DEBUGASSERT(lower); tmrinfo("Entry: cmd=%d arg=%ld\n", cmd, arg); return ret; } /**************************************************************************** * Name: pic32mz_ioctl * * Description: * Get the maximum supported timeout value * * Input Parameters: * lower A pointer the publicly visible representation of the * "lower-half" driver state structure. * maxtimeout The max value in microseconds will be written here. * * Returned Value: * Zero on success; a negated errno value on failure. * ****************************************************************************/ static int pic32mz_maxtimeout(FAR struct timer_lowerhalf_s *lower, FAR uint32_t *maxtimeout) { FAR struct pic32mz_lowerhalf_s *priv = (FAR struct pic32mz_lowerhalf_s *)lower; DEBUGASSERT(priv); /* Convert ticks to microseconds */ *maxtimeout = pic32mz_ticks2usec(priv, priv->maxticks); return OK; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: pic32mz_timer_initialize * * Description: * Bind the configuration timer to a timer lower half instance and * register the timer drivers at 'devpath' * * Input Parameters: * devpath The full path to the timer device. This should be of the * form /dev/timer0 * timer the timer's number. * * Returned Value: * Zero (OK) is returned on success; A negated errno value is returned * to indicate the nature of any failure. * ****************************************************************************/ int pic32mz_timer_initialize(FAR const char *devpath, int timer) { FAR struct pic32mz_lowerhalf_s *lower; FAR void *drvr; tmrinfo("Initializing %s - timer %d\n", devpath, timer); switch (timer) { #ifdef CONFIG_PIC32MZ_T2 case 2: lower = &g_t2_lowerhalf; break; #endif #ifdef CONFIG_PIC32MZ_T3 case 3: lower = &g_t3_lowerhalf; break; #endif #ifdef CONFIG_PIC32MZ_T4 case 4: lower = &g_t4_lowerhalf; break; #endif #ifdef CONFIG_PIC32MZ_T5 case 5: lower = &g_t5_lowerhalf; break; #endif #ifdef CONFIG_PIC32MZ_T6 case 6: lower = &g_t6_lowerhalf; break; #endif #ifdef CONFIG_PIC32MZ_T7 case 7: lower = &g_t7_lowerhalf; break; #endif #ifdef CONFIG_PIC32MZ_T8 case 8: lower = &g_t8_lowerhalf; break; #endif #ifdef CONFIG_PIC32MZ_T9 case 9: lower = &g_t9_lowerhalf; break; #endif default: return -ENODEV; } /* Initialize the elements of lower half state structure */ lower->started = false; lower->callback = NULL; lower->timer = pic32mz_timer_init(timer); if (lower->timer == NULL) { tmrerr("ERROR: Failed to init timer %d.\n", timer); return -EINVAL; } lower->freq = PIC32MZ_TIMER_GETFREQ(lower->timer); lower->width = PIC32MZ_TIMER_GETWIDTH(lower->timer); lower->maxticks = ((1ull << lower->width) - 1ul); tmrinfo("Freq=%dHz / Width=%d-bit / Max ticks=%lu\n", lower->freq, lower->width, lower->maxticks); /* Register the timer driver as /dev/timerX. The returned value from * timer_register is a handle that could be used with timer_unregister(). * REVISIT: The returned handle is discard here. */ drvr = timer_register(devpath, (struct timer_lowerhalf_s *)lower); if (drvr == NULL) { /* The actual cause of the failure may have been a failure to allocate * perhaps a failure to register the timer driver (such as if the * 'devpath' were not unique). We know here but we return EEXIST to * indicate the failure (implying the non-unique devpath). */ tmrerr("ERROR: Failed to register timer %s\n", devpath); return -EEXIST; } tmrinfo("Timer registered successfully\n"); return OK; } #endif /* CONFIG_TIMER && CONFIG_PIC32MZ_TIMER */