1 /*
2  * Copyright (c) 2015-2018, Texas Instruments Incorporated
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * *  Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * *  Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * *  Neither the name of Texas Instruments Incorporated nor the names of
17  *    its contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  * By default disable both asserts and log for this module.
35  * This must be done before DebugP.h is included.
36  */
37 #ifndef DebugP_ASSERT_ENABLED
38 #define DebugP_ASSERT_ENABLED 0
39 #endif
40 #ifndef DebugP_LOG_ENABLED
41 #define DebugP_LOG_ENABLED 0
42 #endif
43 
44 #include <stdbool.h>
45 
46 #include <ti/drivers/dpl/ClockP.h>
47 #include <ti/drivers/dpl/DebugP.h>
48 #include <ti/drivers/dpl/HwiP.h>
49 
50 #include <ti/drivers/Power.h>
51 #include <ti/drivers/power/PowerCC32XX.h>
52 #include <ti/drivers/pwm/PWMTimerCC32XX.h>
53 #include <ti/drivers/timer/TimerCC32XX.h>
54 
55 #include <ti/devices/cc32xx/inc/hw_apps_config.h>
56 #include <ti/devices/cc32xx/inc/hw_memmap.h>
57 #include <ti/devices/cc32xx/inc/hw_ocp_shared.h>
58 #include <ti/devices/cc32xx/inc/hw_types.h>
59 #include <ti/devices/cc32xx/inc/hw_timer.h>
60 #include <ti/devices/cc32xx/driverlib/rom.h>
61 #include <ti/devices/cc32xx/driverlib/rom_map.h>
62 #include <ti/devices/cc32xx/driverlib/gpio.h>
63 #include <ti/devices/cc32xx/driverlib/pin.h>
64 #include <ti/devices/cc32xx/driverlib/timer.h>
65 
66 #define PAD_CONFIG_BASE (OCP_SHARED_BASE + OCP_SHARED_O_GPIO_PAD_CONFIG_0)
67 #define PAD_RESET_STATE 0xC61
68 
69 /*!
70  *  @brief If the PWM period is lower than this value, setDutyAndPeriod
71  *  will briefly disable the PWM channel to set the new values.
72  *
73  *  This is to prevent the case where the period, but not the duty, is
74  *  applied before the timeout and the next cycle is in an undetermined state.
75  */
76 #define PWM_PERIOD_FOR_GLITCH_PROTECTION 0xF
77 
78 void PWMTimerCC32XX_close(PWM_Handle handle);
79 int_fast16_t PWMTimerCC32XX_control(PWM_Handle handle, uint_fast16_t cmd,
80         void *arg);
81 void PWMTimerCC32XX_init(PWM_Handle handle);
82 PWM_Handle PWMTimerCC32XX_open(PWM_Handle handle, PWM_Params *params);
83 int_fast16_t PWMTimerCC32XX_setDuty(PWM_Handle handle, uint32_t dutyValue);
84 int_fast16_t PWMTimerCC32XX_setPeriod(PWM_Handle handle, uint32_t periodValue);
85 int_fast16_t PWMTimerCC32XX_setDutyAndPeriod(PWM_Handle handle, uint32_t dutyValue, uint32_t periodValue);
86 void PWMTimerCC32XX_start(PWM_Handle handle);
87 void PWMTimerCC32XX_stop(PWM_Handle handle);
88 
89 /* PWM function table for PWMTimerCC32XX implementation */
90 const PWM_FxnTable PWMTimerCC32XX_fxnTable = {
91     PWMTimerCC32XX_close,
92     PWMTimerCC32XX_control,
93     PWMTimerCC32XX_init,
94     PWMTimerCC32XX_open,
95     PWMTimerCC32XX_setDuty,
96     PWMTimerCC32XX_setPeriod,
97     PWMTimerCC32XX_setDutyAndPeriod,
98     PWMTimerCC32XX_start,
99     PWMTimerCC32XX_stop
100 };
101 
102 /*
103  * Internal value to notify an error has occurred while calculating a duty
104  * or period.
105  */
106 static const uint32_t PWM_INVALID_VALUE = (~0);
107 
108 /*
109  * GPT peripheral load & match registers are 16 bits wide.  Max value which
110  * can be set is 65535.
111  */
112 static const uint16_t PWM_MAX_MATCH_REG_VALUE = (~0);
113 
114 /*
115  * GPT peripherals have 24 bit resolution.  The max period value which be
116  * set is 16777215.
117  */
118 static const uint32_t PWM_MAX_PERIOD_COUNT = (0xFFFFFF);
119 
120 /*
121  *  The following fields are used by CC32XX driverlib APIs and therefore
122  *  must be populated by driverlib macro definitions. For CC32XX driverlib
123  *  these definitions are found in:
124  *      - inc/hw_memmap.h
125  *      - driverlib/gpio.h
126  *      - driverlib/pin.h
127  *      - driverlib/timer.h
128  */
129 static const uint32_t timerBaseAddresses[4] = {
130     TIMERA0_BASE,
131     TIMERA1_BASE,
132     TIMERA2_BASE,
133     TIMERA3_BASE,
134 };
135 
136 static const uint32_t timerHalves[2] = {
137     TIMER_A,
138     TIMER_B,
139 };
140 
141 #define NUMGPIOPORTS 4
142 static const uint32_t gpioBaseAddresses[NUMGPIOPORTS] = {
143     GPIOA0_BASE,
144     GPIOA1_BASE,
145     GPIOA2_BASE,
146     GPIOA3_BASE,
147 };
148 
149 #define NUMGPIOPINS 8
150 static const uint32_t gpioPinIndexes[NUMGPIOPINS] = {
151     GPIO_PIN_0,
152     GPIO_PIN_1,
153     GPIO_PIN_2,
154     GPIO_PIN_3,
155     GPIO_PIN_4,
156     GPIO_PIN_5,
157     GPIO_PIN_6,
158     GPIO_PIN_7,
159 };
160 
161 #define PinConfigTimerPort(config)     (((config) >> 28) & 0xF)
162 #define PinConfigTimerHalf(config)     (((config) >> 24) & 0xF)
163 #define PinConfigGPIOPort(config)      (((config) >> 20) & 0xF)
164 #define PinConfigGPIOPinIndex(config)  (((config) >> 16) & 0xF)
165 #define PinConfigPinMode(config)       (((config) >> 8) & 0xF)
166 #define PinConfigPin(config)           (((config) >> 0) & 0x3F)
167 
168 /*
169  *  ======== getDutyCounts ========
170  */
getDutyCounts(PWM_Duty_Units dutyUnits,uint32_t dutyValue,uint32_t periodCounts)171 static uint32_t getDutyCounts(PWM_Duty_Units dutyUnits, uint32_t dutyValue,
172     uint32_t periodCounts)
173 {
174     uint32_t      duty = 0;
175     ClockP_FreqHz freq;
176 
177     ClockP_getCpuFreq(&freq);
178 
179     switch (dutyUnits) {
180         case PWM_DUTY_COUNTS:
181             duty = dutyValue;
182             break;
183 
184         case PWM_DUTY_FRACTION:
185             duty = (((uint64_t) dutyValue) * ((uint64_t) periodCounts)) /
186                 PWM_DUTY_FRACTION_MAX;
187             break;
188 
189         case PWM_DUTY_US:
190             duty = (dutyValue != 0) ? (dutyValue * (freq.lo/1000000)) - 1 : 0;
191             break;
192 
193         default:
194             /* Unsupported duty units return an invalid duty */
195             duty = PWM_INVALID_VALUE;
196     }
197 
198     return (duty);
199 }
200 
201 /*
202  *  ======== getPeriodCounts ========
203  */
getPeriodCounts(PWM_Period_Units periodUnits,uint32_t periodValue)204 static uint32_t getPeriodCounts(PWM_Period_Units periodUnits,
205     uint32_t periodValue)
206 {
207     uint32_t      period = 0;
208     ClockP_FreqHz freq;
209 
210     ClockP_getCpuFreq(&freq);
211 
212     switch (periodUnits) {
213         case PWM_PERIOD_COUNTS:
214             period = periodValue;
215             break;
216 
217         case PWM_PERIOD_HZ:
218             if (periodValue && periodValue <= freq.lo) {
219                 period = (freq.lo / periodValue) - 1;
220             }
221             break;
222 
223         case PWM_PERIOD_US:
224             period = (periodValue * (freq.lo/1000000)) - 1;
225             break;
226 
227         default:
228             /* Unsupported period units return an invalid period */
229             period = PWM_INVALID_VALUE;
230     }
231 
232     return (period);
233 }
234 
235 /*
236  *  ======== getPowerMgrId ========
237  */
getPowerMgrId(uint32_t baseAddr)238 static uint_fast16_t getPowerMgrId(uint32_t baseAddr)
239 {
240     switch (baseAddr) {
241         case GPIOA0_BASE:
242             return (PowerCC32XX_PERIPH_GPIOA0);
243         case GPIOA1_BASE:
244             return (PowerCC32XX_PERIPH_GPIOA1);
245         case GPIOA2_BASE:
246             return (PowerCC32XX_PERIPH_GPIOA2);
247         case GPIOA3_BASE:
248             return (PowerCC32XX_PERIPH_GPIOA3);
249         case GPIOA4_BASE:
250             return (PowerCC32XX_PERIPH_GPIOA4);
251         default:
252             /* Should never get here */
253             return ((unsigned int) -1);
254     }
255 }
256 
257 /*
258  *  ======== initHw ========
259  */
initHw(PWM_Handle handle,uint32_t period,uint32_t duty)260 static int initHw(PWM_Handle handle, uint32_t period, uint32_t duty)
261 {
262     uintptr_t                       key;
263     int32_t                         result;
264     uint32_t                        timerConfigVal;
265     PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
266     uint32_t timerBaseAddr;
267     uint16_t halfTimer;
268 
269     timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
270     halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
271 
272     key = HwiP_disable();
273 
274     MAP_TimerDisable(timerBaseAddr, halfTimer);
275 
276     /*
277      * The CC32XX SDK TimerConfigure API halts both timers when it is
278      * used to configure a single half timer.  The code below performs
279      * the register operations necessary to configure each half timer
280      * individually.
281      */
282     /* Enable CCP to IO path */
283     HWREG(APPS_CONFIG_BASE + APPS_CONFIG_O_GPT_TRIG_SEL) = 0xFF;
284 
285     /* Split the timer and configure it as a PWM */
286     timerConfigVal = ((halfTimer & (TIMER_CFG_A_PWM | TIMER_CFG_B_PWM)) |
287         TIMER_CFG_SPLIT_PAIR);
288     HWREG(timerBaseAddr + TIMER_O_CFG) |= (timerConfigVal >> 24);
289     if (halfTimer & TIMER_A) {
290         HWREG(timerBaseAddr + TIMER_O_TAMR) = timerConfigVal & 255;
291     }
292     else {
293         HWREG(timerBaseAddr + TIMER_O_TBMR) = (timerConfigVal >> 8) & 255;
294     }
295 
296     /* Set the peripheral output to active-high */
297     MAP_TimerControlLevel(timerBaseAddr, halfTimer, true);
298 
299     HwiP_restore(key);
300 
301     result = PWMTimerCC32XX_setPeriod(handle, period);
302     if (result != PWM_STATUS_SUCCESS) {
303         return (result);
304     }
305 
306     result = PWMTimerCC32XX_setDuty(handle, duty);
307     if (result != PWM_STATUS_SUCCESS) {
308         return (result);
309     }
310 
311     return (PWM_STATUS_SUCCESS);
312 }
313 
314 /*
315  *  ======== postNotifyFxn ========
316  *  Called by Power module when waking up from LPDS.
317  */
postNotifyFxn(unsigned int eventType,uintptr_t eventArg,uintptr_t clientArg)318 static int postNotifyFxn(unsigned int eventType, uintptr_t eventArg,
319     uintptr_t clientArg)
320 {
321     PWM_Handle             handle = (PWM_Handle) clientArg;
322     PWMTimerCC32XX_Object *object = handle->object;
323 
324     initHw(handle, object->period, object->duty);
325 
326     return (Power_NOTIFYDONE);
327 }
328 
329 /*
330  *  ======== PWMTimerCC32XX_close ========
331  *  @pre    Function assumes that the handle is not NULL
332  */
PWMTimerCC32XX_close(PWM_Handle handle)333 void PWMTimerCC32XX_close(PWM_Handle handle)
334 {
335     PWMTimerCC32XX_Object          *object  = handle->object;
336     PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
337     TimerCC32XX_SubTimer            subTimer;
338     uint32_t                        timerBaseAddr;
339     uint32_t                        gpioBaseAddr;
340     uint32_t                        padRegister;
341     uintptr_t                       key;
342 
343     timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
344 
345     subTimer = (TimerCC32XX_SubTimer) (TimerCC32XX_timer16A +
346         PinConfigTimerHalf(hwAttrs->pwmPin));
347 
348     /*
349      * Some PWM pins may not have GPIO capability; in these cases gpioBaseAddr
350      * is set to 0 & the GPIO power dependencies are not released.
351      */
352     gpioBaseAddr = (PinConfigGPIOPort(hwAttrs->pwmPin) >= NUMGPIOPORTS) ?
353         0 : gpioBaseAddresses[PinConfigGPIOPort(hwAttrs->pwmPin)];
354 
355     PWMTimerCC32XX_stop(handle);
356 
357     key = HwiP_disable();
358 
359     TimerCC32XX_freeTimerResource(timerBaseAddr, subTimer);
360 
361     /* Remove GPIO power dependency if pin is GPIO capable */
362     if (gpioBaseAddr) {
363         Power_releaseDependency(getPowerMgrId(gpioBaseAddr));
364     }
365 
366     Power_unregisterNotify(&object->postNotify);
367 
368     padRegister = (PinToPadGet((hwAttrs->pwmPin) & 0x3f)<<2) + PAD_CONFIG_BASE;
369     HWREG(padRegister) = PAD_RESET_STATE;
370 
371     object->isOpen = false;
372 
373     HwiP_restore(key);
374 
375     DebugP_log1("PWM:(%p) is closed", (uintptr_t) handle);
376 }
377 
378 /*
379  *  ======== PWMTimerCC32XX_control ========
380  *  @pre    Function assumes that the handle is not NULL
381  */
PWMTimerCC32XX_control(PWM_Handle handle,uint_fast16_t cmd,void * arg)382 int_fast16_t PWMTimerCC32XX_control(PWM_Handle handle, uint_fast16_t cmd,
383     void *arg)
384 {
385     /* No implementation yet */
386     return (PWM_STATUS_UNDEFINEDCMD);
387 }
388 
389 /*
390  *  ======== PWMTimerCC32XX_init ========
391  *  @pre    Function assumes that the handle is not NULL
392  */
PWMTimerCC32XX_init(PWM_Handle handle)393 void PWMTimerCC32XX_init(PWM_Handle handle)
394 {
395 }
396 
397 /*
398  *  ======== PWMTimerCC32XX_open ========
399  *  @pre    Function assumes that the handle is not NULL
400  */
PWMTimerCC32XX_open(PWM_Handle handle,PWM_Params * params)401 PWM_Handle PWMTimerCC32XX_open(PWM_Handle handle, PWM_Params *params)
402 {
403     uintptr_t                       key;
404     PWMTimerCC32XX_Object          *object = handle->object;
405     PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
406     TimerCC32XX_SubTimer            subTimer;
407     uint32_t                        timerBaseAddr;
408     uint32_t                        gpioBaseAddr;
409     uint16_t                        pin;
410 
411     timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
412     pin = PinConfigPin(hwAttrs->pwmPin);
413 
414     subTimer = (TimerCC32XX_SubTimer) (TimerCC32XX_timer16A +
415         PinConfigTimerHalf(hwAttrs->pwmPin));
416 
417     key = HwiP_disable();
418 
419     if (object->isOpen) {
420         HwiP_restore(key);
421 
422         DebugP_log1("PWM:(%p) already opened.", (uintptr_t) handle);
423 
424         return (NULL);
425     }
426 
427     if (!TimerCC32XX_allocateTimerResource(timerBaseAddr, subTimer)) {
428         HwiP_restore(key);
429 
430         DebugP_log1("Timer: 0x%X unavailable.", timerBaseAddr);
431 
432         return (NULL);
433     }
434 
435     object->isOpen = true;
436 
437     HwiP_restore(key);
438 
439     /*
440      * Some PWM pins may not have GPIO capability; in these cases gpioBaseAddr
441      * is set to 0 & the GPIO power dependencies are not set.
442      */
443     gpioBaseAddr = (PinConfigGPIOPort(hwAttrs->pwmPin) >= NUMGPIOPORTS) ?
444         0 : gpioBaseAddresses[PinConfigGPIOPort(hwAttrs->pwmPin)];
445 
446     /* Set GPIO power dependency if pin is GPIO capable */
447     if (gpioBaseAddr) {
448         /* Check GPIO power resource Id */
449         if (getPowerMgrId(gpioBaseAddr) == ((unsigned int) -1)) {
450             TimerCC32XX_freeTimerResource(timerBaseAddr, subTimer);
451 
452             object->isOpen = false;
453 
454             DebugP_log1("PWM:(%p) Failed to determine GPIO power resource ID.",
455                 (uintptr_t) handle);
456 
457             return (NULL);
458         }
459 
460         /* Register power dependency for GPIO port */
461         Power_setDependency(getPowerMgrId(gpioBaseAddr));
462     }
463 
464     Power_registerNotify(&object->postNotify, PowerCC32XX_AWAKE_LPDS,
465         postNotifyFxn, (uintptr_t) handle);
466 
467     /*
468      * Set PWM duty to initial value (not 0) - required when inverting
469      * output polarity to generate a duty equal to 0 or period.  See comments in
470      * PWMTimerCC32XX_setDuty for more information.
471      */
472     object->duty = 0;
473     object->period = 0;
474     object->dutyUnits = params->dutyUnits;
475     object->idleLevel = params->idleLevel;
476     object->periodUnits = params->periodUnits;
477     object->pwmStarted = 0;
478 
479     /* Initialize the peripheral & set the period & duty */
480     if (initHw(handle, params->periodValue, params->dutyValue) !=
481         PWM_STATUS_SUCCESS) {
482         PWMTimerCC32XX_close(handle);
483 
484         DebugP_log1("PWM:(%p) Failed set initial PWM configuration.",
485             (uintptr_t) handle);
486 
487         return (NULL);
488     }
489 
490     /* Configure the Power_pinParkState based on idleLevel param */
491     PowerCC32XX_setParkState((PowerCC32XX_Pin) pin,
492         (object->idleLevel == PWM_IDLE_HIGH));
493 
494     /* Called to set the initial idleLevel */
495     PWMTimerCC32XX_stop(handle);
496 
497     DebugP_log3("PWM:(%p) opened; period set to: %d; duty set to: %d",
498         (uintptr_t) handle, params->periodValue, params->dutyValue);
499 
500     return (handle);
501 }
502 
503 /*
504  *  ======== PWMTimerCC32XX_setDuty ========
505  *  @pre    Function assumes that handle is not NULL
506  */
PWMTimerCC32XX_setDuty(PWM_Handle handle,uint32_t dutyValue)507 int_fast16_t PWMTimerCC32XX_setDuty(PWM_Handle handle, uint32_t dutyValue)
508 {
509     uintptr_t                       key;
510     uint32_t                        duty;
511     uint32_t                        period;
512     PWMTimerCC32XX_Object          *object = handle->object;
513     PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
514     uint32_t timerBaseAddr;
515     uint16_t halfTimer;
516 
517     timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
518     halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
519 
520     key = HwiP_disable();
521 
522     period = object->period;
523     duty = getDutyCounts(object->dutyUnits, dutyValue, period);
524 
525     if (duty == PWM_INVALID_VALUE) {
526         HwiP_restore(key);
527 
528         DebugP_log1("PWM:(%p) duty units could not be determined.",
529             (uintptr_t) handle);
530 
531         return (PWM_STATUS_ERROR);
532     }
533 
534     if (duty > period) {
535         HwiP_restore(key);
536 
537         DebugP_log1("PWM:(%p) duty is out of range.", (uintptr_t) handle);
538 
539         return (PWM_STATUS_INVALID_DUTY);
540     }
541 
542     /*
543      * The timer peripheral cannot generate a duty equal to the period when
544      * the timer is counting down.  In these cases the PWM duty is set to the
545      * period value (output remains low) and output polarity is inverted.
546      * Additionally, if the output is changed from the period the PWM output
547      * polarity must be inverted again.
548      *
549      * The code below uses the previous duty (object->duty) and the new duty to
550      * determine if the polarity should be inverted.
551      * For more details refer to the device specific datasheet and the following
552      * E2E post:
553      *  http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/354826.aspx
554      */
555     if (((duty == period) && (object->duty != period)) ||
556         ((duty != period) && (object->duty == period))) {
557         HWREG(timerBaseAddr + TIMER_O_CTL) ^=
558             (halfTimer & (TIMER_CTL_TAPWML | TIMER_CTL_TBPWML));
559     }
560 
561     /*
562      * Set & store the new duty.  IMPORTANT: this must be saved after output
563      * inversion is determined and before the duty = 0 corner case.
564      */
565     object->duty = duty;
566 
567     /*
568      * Special corner case, if duty is 0 we set it to the period without
569      * inverting output
570      */
571     if (duty == 0) {
572         duty = period;
573     }
574 
575     MAP_TimerPrescaleMatchSet(timerBaseAddr, halfTimer,
576         duty / PWM_MAX_MATCH_REG_VALUE);
577     MAP_TimerMatchSet(timerBaseAddr, halfTimer,
578         duty % PWM_MAX_MATCH_REG_VALUE);
579 
580     HwiP_restore(key);
581 
582     DebugP_log2("PWM:(%p) duty set to: %d", (uintptr_t) handle, dutyValue);
583 
584     return (PWM_STATUS_SUCCESS);
585 }
586 
587 /*
588  *  ======== PWMTimerCC32XX_setPeriod ========
589  *  @pre    Function assumes that handle is not NULL
590  */
PWMTimerCC32XX_setPeriod(PWM_Handle handle,uint32_t periodValue)591 int_fast16_t PWMTimerCC32XX_setPeriod(PWM_Handle handle, uint32_t periodValue)
592 {
593     uintptr_t                       key;
594     uint32_t                        duty;
595     uint32_t                        period;
596     PWMTimerCC32XX_Object          *object = handle->object;
597     PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
598     uint32_t timerBaseAddr;
599     uint16_t halfTimer;
600 
601     timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
602     halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
603 
604     key = HwiP_disable();
605 
606     duty = object->duty;
607     period = getPeriodCounts(object->periodUnits, periodValue);
608 
609     if (period == PWM_INVALID_VALUE) {
610         HwiP_restore(key);
611 
612         DebugP_log1("PWM:(%p) period units could not be determined.",
613             (uintptr_t) handle);
614 
615         return (PWM_STATUS_ERROR);
616     }
617 
618     if ((period == 0) || (period <= duty) || (period > PWM_MAX_PERIOD_COUNT)) {
619         HwiP_restore(key);
620 
621         DebugP_log1("PWM:(%p) period is out of range.", (uintptr_t) handle);
622 
623         return (PWM_STATUS_INVALID_PERIOD);
624     }
625 
626     /* Set the new period */
627     object->period = period;
628     MAP_TimerPrescaleSet(timerBaseAddr, halfTimer,
629         period / PWM_MAX_MATCH_REG_VALUE);
630     MAP_TimerLoadSet(timerBaseAddr, halfTimer,
631         period % PWM_MAX_MATCH_REG_VALUE);
632 
633     HwiP_restore(key);
634 
635     DebugP_log2("PWM:(%p) period set to: %d", (uintptr_t) handle, periodValue);
636 
637     return (PWM_STATUS_SUCCESS);
638 }
639 
640 /*
641  *  ======== PWMTimerCC32XX_setDutyAndPeriod ========
642  *  @pre    Function assumes that handle is not NULL
643  */
PWMTimerCC32XX_setDutyAndPeriod(PWM_Handle handle,uint32_t dutyValue,uint32_t periodValue)644 int_fast16_t PWMTimerCC32XX_setDutyAndPeriod(PWM_Handle handle, uint32_t dutyValue, uint32_t periodValue)
645 {
646     uintptr_t                       key;
647     uint32_t                        duty;
648     uint32_t                        period;
649     bool                            stopped = false;
650     PWMTimerCC32XX_Object          *object = handle->object;
651     PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
652     uint32_t                        oldPeriod;
653     uint32_t timerBaseAddr;
654     uint16_t halfTimer;
655 
656     timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
657     halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
658 
659     key = HwiP_disable();
660 
661     oldPeriod = getPeriodCounts(object->periodUnits, object->period);
662     period = getPeriodCounts(object->periodUnits, periodValue);
663     duty = getDutyCounts(object->dutyUnits, dutyValue, period);
664 
665     if (period == PWM_INVALID_VALUE) {
666         HwiP_restore(key);
667 
668         DebugP_log1("PWM:(%p) period units could not be determined.", (uintptr_t) handle);
669 
670         return (PWM_STATUS_ERROR);
671     }
672 
673     if ((period == 0) || (period < duty) || (period > PWM_MAX_PERIOD_COUNT)) {
674         HwiP_restore(key);
675 
676         DebugP_log1("PWM:(%p) period is out of range.", (uintptr_t) handle);
677 
678         return (PWM_STATUS_INVALID_PERIOD);
679     }
680 
681     /* Set the new period */
682     object->period = period;
683 
684     /*
685      * The timer peripheral cannot generate a duty equal to the period when
686      * the timer is counting down.  In these cases the PWM duty is set to the
687      * period value (output remains low) and output polarity is inverted.
688      * Additionally, if the output is changed from the period the PWM output
689      * polarity must be inverted again.
690      *
691      * The code below uses the previous duty (object->duty) and the new duty to
692      * determine if the polarity should be inverted.
693      * For more details refer to the device specific datasheet and the following
694      * E2E post:
695      *  http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/354826.aspx
696      */
697     if (((duty == period) && (object->duty != period)) ||
698         ((duty != period) && (object->duty == period))) {
699         HWREG(timerBaseAddr + TIMER_O_CTL) ^=
700             (halfTimer & (TIMER_CTL_TAPWML | TIMER_CTL_TBPWML));
701     }
702 
703     /*
704      * Set & store the new duty.  IMPORTANT: this must be saved after output
705      * inversion is determined and before the duty = 0 corner case.
706      */
707     object->duty = duty;
708 
709     /*
710      * Special corner case, if duty is 0 we set it to the period without
711      * inverting output
712      */
713     if (duty == 0) {
714         duty = period;
715     }
716 
717     if (object->pwmStarted && oldPeriod <= PWM_PERIOD_FOR_GLITCH_PROTECTION) {
718         stopped = true;
719         MAP_TimerDisable(timerBaseAddr, halfTimer);
720     }
721 
722     MAP_TimerPrescaleSet(timerBaseAddr, halfTimer, period / PWM_MAX_MATCH_REG_VALUE);
723     MAP_TimerPrescaleMatchSet(timerBaseAddr, halfTimer, duty / PWM_MAX_MATCH_REG_VALUE);
724 
725     MAP_TimerLoadSet(timerBaseAddr, halfTimer, period % PWM_MAX_MATCH_REG_VALUE);
726     MAP_TimerMatchSet(timerBaseAddr, halfTimer, duty % PWM_MAX_MATCH_REG_VALUE);
727 
728     if (stopped) {
729         MAP_TimerEnable(timerBaseAddr, halfTimer);
730     }
731 
732     HwiP_restore(key);
733     return (PWM_STATUS_SUCCESS);
734 }
735 
736 /*
737  *  ======== PWMTimerCC32XX_start ========
738  *  @pre    Function assumes that handle is not NULL
739  */
PWMTimerCC32XX_start(PWM_Handle handle)740 void PWMTimerCC32XX_start(PWM_Handle handle)
741 {
742     uintptr_t                       key;
743     PWMTimerCC32XX_Object          *object = handle->object;
744     PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
745     uint32_t timerBaseAddr;
746     uint16_t halfTimer;
747     uint16_t pin;
748     uint16_t mode;
749 
750     timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
751     halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
752     pin = PinConfigPin(hwAttrs->pwmPin);
753     mode = PinConfigPinMode(hwAttrs->pwmPin);
754 
755     key = HwiP_disable();
756 
757     /*
758      * GP timer ticks only in Active mode.  Cannot be used in HIB or LPDS.
759      * Set constraint to disallow LPDS.
760      */
761     if (!(object->pwmStarted)) {
762         Power_setConstraint(PowerCC32XX_DISALLOW_LPDS);
763         object->pwmStarted = true;
764     }
765 
766     /* Start the timer & set pinmux to PWM mode */
767     MAP_TimerEnable(timerBaseAddr, halfTimer);
768     MAP_PinTypeTimer((unsigned long)pin, (unsigned long)mode);
769 
770     HwiP_restore(key);
771 }
772 
773 /*
774  *  ======== PWMTimerCC32XX_stop ========
775  *  @pre    Function assumes that handle is not NULL
776  */
PWMTimerCC32XX_stop(PWM_Handle handle)777 void PWMTimerCC32XX_stop(PWM_Handle handle)
778 {
779     uintptr_t                       key;
780     uint8_t                         output;
781     PWMTimerCC32XX_Object          *object = handle->object;
782     PWMTimerCC32XX_HWAttrsV2 const *hwAttrs = handle->hwAttrs;
783     uint32_t timerBaseAddr;
784     uint16_t halfTimer;
785     uint32_t gpioBaseAddr;
786     uint8_t gpioPinIndex;
787     uint16_t pin;
788 
789     timerBaseAddr = timerBaseAddresses[PinConfigTimerPort(hwAttrs->pwmPin)];
790     halfTimer = timerHalves[PinConfigTimerHalf(hwAttrs->pwmPin)];
791     pin = PinConfigPin(hwAttrs->pwmPin);
792 
793     /*
794      * Some PWM pins may not have GPIO capability; in these cases gpioBaseAddr
795      * is set to 0 & the GPIO power dependencies are not set.
796      */
797     gpioBaseAddr = (PinConfigGPIOPort(hwAttrs->pwmPin) >= NUMGPIOPORTS) ?
798         0 : gpioBaseAddresses[PinConfigGPIOPort(hwAttrs->pwmPin)];
799     gpioPinIndex = (PinConfigGPIOPinIndex(hwAttrs->pwmPin) >= NUMGPIOPINS) ?
800         0 : gpioPinIndexes[PinConfigGPIOPinIndex(hwAttrs->pwmPin)];
801 
802     key = HwiP_disable();
803 
804     /* Remove the dependency to allow LPDS */
805     if (object->pwmStarted) {
806         Power_releaseConstraint(PowerCC32XX_DISALLOW_LPDS);
807         object->pwmStarted = false;
808     }
809 
810     /* Set pin as GPIO with IdleLevel value & stop the timer */
811     output = (object->idleLevel) ? gpioPinIndex : 0;
812     MAP_PinTypeGPIO((unsigned long)pin, PIN_MODE_0, false);
813 
814     /* Only configure the pin as GPIO if the pin is GPIO capable */
815     if (gpioBaseAddr) {
816         MAP_GPIODirModeSet(gpioBaseAddr, gpioPinIndex, GPIO_DIR_MODE_OUT);
817         MAP_GPIOPinWrite(gpioBaseAddr, gpioPinIndex, output);
818     }
819 
820     /* Stop the Timer */
821     MAP_TimerDisable(timerBaseAddr, halfTimer);
822     HwiP_restore(key);
823 }
824