Refactor to use new protocol module and sipo.
[bertos.git] / bertos / cpu / arm / drv / pwm_at91.c
index a95e60613fa6d0e5e8140735f24d468eec0ecc79..c5780c3baa36421780529fea6287ca1f8e685f9f 100644 (file)
  * invalidate any other reasons why the executable file might be covered by
  * the GNU General Public License.
  *
- * Copyright 2008 Develer S.r.l. (http://www.develer.com/)
+ * Copyright 2011 Develer S.r.l. (http://www.develer.com/)
  *
  * -->
  *
  *
  * \brief PWM hardware-specific implementation
  *
- * \version $Id$
  * \author Daniele Basile <asterix@develer.com>
+ * \author Francesco Sacchi <batt@develer.com>
  */
 
+#include <drv/pwm.h>
 #include "pwm_at91.h"
-#include "hw/pwm_map.h"
 #include <hw/hw_cpufreq.h>
 #include "cfg/cfg_pwm.h"
 
 #include <cfg/debug.h>
 
 #include <io/arm.h>
+#include <cpu/irq.h>
 
+#define PWM_HW_MAX_PRESCALER_STEP         10
+#define PWM_HW_MAX_PERIOD             0xFFFF
 
-/**
- * Register structure for pwm driver.
- * This array content all data and register pointer
- * to manage pwm peripheral device.
- */
-static PwmChannel pwm_map[PWM_CNT] =
-{
-       {//PWM Channel 0
-               .duty_zero = false,
-               .pol = false,
-               .pwm_pin = BV(PWM0),
-               .mode_reg = &PWM_CMR0,
-               .duty_reg = &PWM_CDTY0,
-               .period_reg = &PWM_CPRD0,
-               .update_reg = &PWM_CUPD0,
-       },
-       {//PWM Channel 1
-               .duty_zero = false,
-               .pol = false,
-               .pwm_pin = BV(PWM1),
-               .mode_reg = &PWM_CMR1,
-               .duty_reg = &PWM_CDTY1,
-               .period_reg = &PWM_CPRD1,
-               .update_reg = &PWM_CUPD1,
-       },
-       {//PWM Channel 2
-               .duty_zero = false,
-        .pol = false,
-               .pwm_pin = BV(PWM2),
-               .mode_reg = &PWM_CMR2,
-               .duty_reg = &PWM_CDTY2,
-               .period_reg = &PWM_CPRD2,
-               .update_reg = &PWM_CUPD2,
-       },
-       {//PWM Channel 3
-               .duty_zero = false,
-               .pol = false,
-               .pwm_pin = BV(PWM3),
-               .mode_reg = &PWM_CMR3,
-               .duty_reg = &PWM_CDTY3,
-               .period_reg = &PWM_CPRD3,
-               .update_reg = &PWM_CUPD3,
-       }
-};
+#if CFG_PWM_ENABLE_OLD_API
+       #include "hw/pwm_map.h"
 
+       /**
+        * Register structure for pwm driver.
+        * This array content all data and register pointer
+        * to manage pwm peripheral device.
+        */
+       static PwmChannel pwm_map[PWM_CNT] =
+       {
+               {//PWM Channel 0
+                       .duty_zero = false,
+                       .pol = false,
+                       .pwm_pin = BV(PWM0),
+                       .mode_reg = &PWM_CMR0,
+                       .duty_reg = &PWM_CDTY0,
+                       .period_reg = &PWM_CPRD0,
+                       .update_reg = &PWM_CUPD0,
+               },
+               {//PWM Channel 1
+                       .duty_zero = false,
+                       .pol = false,
+                       .pwm_pin = BV(PWM1),
+                       .mode_reg = &PWM_CMR1,
+                       .duty_reg = &PWM_CDTY1,
+                       .period_reg = &PWM_CPRD1,
+                       .update_reg = &PWM_CUPD1,
+               },
+               {//PWM Channel 2
+                       .duty_zero = false,
+                       .pol = false,
+                       .pwm_pin = BV(PWM2),
+                       .mode_reg = &PWM_CMR2,
+                       .duty_reg = &PWM_CDTY2,
+                       .period_reg = &PWM_CPRD2,
+                       .update_reg = &PWM_CUPD2,
+               },
+               {//PWM Channel 3
+                       .duty_zero = false,
+                       .pol = false,
+                       .pwm_pin = BV(PWM3),
+                       .mode_reg = &PWM_CMR3,
+                       .duty_reg = &PWM_CDTY3,
+                       .period_reg = &PWM_CPRD3,
+                       .update_reg = &PWM_CUPD3,
+               }
+       };
 
-/**
- * Get preiod from select channel
- *
- * \a dev channel
- */
-pwm_period_t pwm_hw_getPeriod(PwmDev dev)
-{
-       return *pwm_map[dev].period_reg;
-}
 
-/**
- * Set pwm waveform frequecy.
- *
- * \a freq in Hz
- */
-void pwm_hw_setFrequency(PwmDev dev, uint32_t freq)
-{
-       uint32_t period = 0;
+       /**
+        * Get preiod from select channel
+        *
+        * \a dev channel
+        */
+       pwm_period_t pwm_hw_getPeriod(PwmDev dev)
+       {
+               return *pwm_map[dev].period_reg;
+       }
 
-       for(int i = 0; i <= PWM_HW_MAX_PRESCALER_STEP; i++)
+       /**
+        * Set pwm waveform frequecy.
+        *
+        * \a freq in Hz
+        */
+       void pwm_hw_setFrequency(PwmDev dev, uint32_t freq)
        {
-               period = CPU_FREQ / (BV(i) * freq);
-//             LOG_INFO("period[%ld], prescale[%d]\n", period, i);
-               if ((period < PWM_HW_MAX_PERIOD) && (period != 0))
+               uint32_t period = 0;
+
+               for(int i = 0; i <= PWM_HW_MAX_PRESCALER_STEP; i++)
                {
-                       //Clean previous channel prescaler, and set new
-                       *pwm_map[dev].mode_reg &= ~PWM_CPRE_MCK_MASK;
-                       *pwm_map[dev].mode_reg |= i;
-                       //Set pwm period
-                       *pwm_map[dev].period_reg = period;
-                       break;
+                       period = CPU_FREQ / (BV(i) * freq);
+                       LOG_INFO("period[%ld], prescale[%d]\n", period, i);
+                       if ((period < PWM_HW_MAX_PERIOD) && (period != 0))
+                       {
+                               //Clean previous channel prescaler, and set new
+                               *pwm_map[dev].mode_reg &= ~PWM_CPRE_MCK_MASK;
+                               *pwm_map[dev].mode_reg |= i;
+                               //Set pwm period
+                               *pwm_map[dev].period_reg = period;
+                               break;
+                       }
                }
+
+               LOG_INFO("PWM ch[%d] period[%ld]\n", dev, period);
        }
 
-       LOG_INFO("PWM ch[%d] period[%ld]\n", dev, period);
-}
+       /**
+        * Set pwm duty cycle.
+        *
+        * \a duty value 0 - 2^16
+        */
+       void pwm_hw_setDutyUnlock(PwmDev dev, uint16_t duty)
+       {
+               ASSERT(duty <= (uint16_t)*pwm_map[dev].period_reg);
 
-/**
- * Set pwm duty cycle.
- *
- * \a duty value 0 - 2^16
- */
-void pwm_hw_setDutyUnlock(PwmDev dev, uint16_t duty)
-{
-       ASSERT(duty <= (uint16_t)*pwm_map[dev].period_reg);
 
+               /*
+                * If polarity flag is true we must invert
+                * PWM polarity.
+                */
+               if (pwm_map[dev].pol)
+               {
+                       duty = (uint16_t)*pwm_map[dev].period_reg - duty;
+                       LOG_INFO("Inverted duty[%d], pol[%d]\n", duty, pwm_map[dev].pol);
+               }
 
-       /*
-        * WARNING: is forbidden to write 0 to duty cycle value,
-        * and so for duty = 0 we must enable PIO and clear output!
+               /*
+                * WARNING: is forbidden to write 0 to duty cycle value,
+                * and so for duty = 0 we must enable PIO and clear output!
+                */
+               if (!duty)
+               {
+                       PWM_PIO_CODR = pwm_map[dev].pwm_pin;
+                       PWM_PIO_PER  = pwm_map[dev].pwm_pin;
+                       pwm_map[dev].duty_zero = true;
+               }
+               else
+               {
+                       PWM_PIO_PDR = pwm_map[dev].pwm_pin;
+                       PWM_PIO_ABSR = pwm_map[dev].pwm_pin;
+
+                       *pwm_map[dev].update_reg = duty;
+                       pwm_map[dev].duty_zero = false;
+               }
+
+               PWM_ENA = BV(dev);
+               LOG_INFO("PWM ch[%d] duty[%d], period[%ld]\n", dev, duty, *pwm_map[dev].period_reg);
+       }
+
+
+       /**
+        * Enable select pwm channel
+        */
+       void pwm_hw_enable(PwmDev dev)
+       {
+               if (!pwm_map[dev].duty_zero)
+               {
+                       PWM_PIO_PDR  = pwm_map[dev].pwm_pin;
+                       PWM_PIO_ABSR = pwm_map[dev].pwm_pin;
+               }
+       }
+
+       /**
+        * Disable select pwm channel
         */
-       if (!duty)
+       void pwm_hw_disable(PwmDev dev)
        {
                PWM_PIO_PER = pwm_map[dev].pwm_pin;
-               pwm_map[dev].duty_zero = true;
        }
-       else
+
+       /**
+        * Set PWM polarity to select pwm channel
+        */
+       void pwm_hw_setPolarity(PwmDev dev, bool pol)
        {
-       /*
-         * If polarity flag is true we must invert
-         * PWM polarity.
-         */
-        if (pwm_map[dev].pol)
-        {
-                duty = (uint16_t)*pwm_map[dev].period_reg - duty;
-                               LOG_INFO("Inverted duty[%d], pol[%d]\n", duty, pwm_map[dev].pol);
-        }
-
-               PWM_PIO_PDR = pwm_map[dev].pwm_pin;
-               *pwm_map[dev].update_reg = duty;
-               pwm_map[dev].duty_zero = false;
+                       pwm_map[dev].pol = pol;
+                       LOG_INFO("Set pol[%d]\n", pwm_map[dev].pol);
        }
 
-       PWM_ENA = BV(dev);
+       /**
+        * Init pwm.
+        */
+       void pwm_hw_init(void)
+       {
 
-       LOG_INFO("PWM ch[%d] duty[%d], period[%ld]\n", dev, duty, *pwm_map[dev].period_reg);
-}
+               /*
+                * Init pwm:
+                * WARNING: is forbidden to write 0 to duty cycle value,
+                * and so for duty = 0 we must enable PIO and clear output!
+                * - clear PIO outputs
+                * - enable PIO outputs
+                * - Disable PIO and enable PWM functions
+                * - Power on PWM
+                */
+               PWM_PIO_CODR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
+               PWM_PIO_OER  = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
+               PWM_PIO_PDR  = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
+               PWM_PIO_ABSR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
+               PMC_PCER |= BV(PWMC_ID);
 
+               /* Disable all channels. */
+               PWM_DIS = 0xFFFFFFFF;
+               /* Disable prescalers A and B */
+               PWM_MR = 0;
 
-/**
- * Enable select pwm channel
- */
-void pwm_hw_enable(PwmDev dev)
-{
-       if (!pwm_map[dev].duty_zero)
-               PWM_PIO_PDR = pwm_map[dev].pwm_pin;
-}
+               /*
+                * Set pwm mode:
+                * - set period alidned to left
+                * - set output waveform to start at high level
+                * - allow duty cycle modify at next period event
+                */
+               for (int ch = 0; ch < PWM_CNT; ch++)
+               {
+                       *pwm_map[ch].mode_reg = 0;
+                       *pwm_map[ch].mode_reg = BV(PWM_CPOL);
+               }
 
-/**
- * Disable select pwm channel
- */
-void pwm_hw_disable(PwmDev dev)
-{
-       PWM_PIO_PER = pwm_map[dev].pwm_pin;
-}
+       }
 
-/**
- * Set PWM polarity to select pwm channel
- */
-void pwm_hw_setPolarity(PwmDev dev, bool pol)
-{
-        pwm_map[dev].pol = pol;
-               LOG_INFO("Set pol[%d]\n", pwm_map[dev].pol);
-}
+#else
+
+       typedef struct PwmChannelRegs
+       {
+               reg32_t CMR;
+               reg32_t CDTY;
+               reg32_t CPRD;
+               reg32_t CCNT;
+               reg32_t CUPD;
+       } PwmChannelRegs;
 
-/**
- * Init pwm.
- */
-void pwm_hw_init(void)
-{
 
        /*
-        * Init pwm:
-        * WARNING: is forbidden to write 0 to duty cycle value,
-        * and so for duty = 0 we must enable PIO and clear output!
-        * - clear PIO outputs
-        * - enable PIO outputs
-        * - Disable PIO and enable PWM functions
-        * - Power on PWM
+        * Set pwm waveform frequecy.
         */
-       PWM_PIO_CODR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
-       PWM_PIO_OER = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
-       PWM_PIO_PDR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
-       PWM_PIO_ABSR = BV(PWM0) | BV(PWM1) | BV(PWM2) | BV(PWM3);
-       PMC_PCER |= BV(PWMC_ID);
+       void pwm_hw_setFrequency(Pwm *ctx, pwm_freq_t freq)
+       {
+               uint32_t period = 0;
 
-       /* Disable all channels. */
-       PWM_DIS = 0xFFFFFFFF;
-       /* Disable prescalers A and B */
-       PWM_MR = 0;
+               for(int i = 0; i <= PWM_HW_MAX_PRESCALER_STEP; i++)
+               {
+                       period = CPU_FREQ / (BV(i) * freq);
+                       LOG_INFO("period[%ld], prescale[%d]\n", period, i);
+                       if ((period < PWM_HW_MAX_PERIOD) && (period != 0))
+                       {
+                               //Clear previous channel prescaler, and set new
+                               ctx->hw->base->CMR &= ~PWM_CPRE_MCK_MASK;
+                               ctx->hw->base->CMR |= i;
+                               //Set pwm period
+                               ATOMIC(
+                                       ctx->hw->base->CPRD = period;
+                                       ctx->hw->base->CDTY = period;
+                               );
+                               break;
+                       }
+               }
+
+               LOG_INFO("PWM ch[%d] period[%ld]\n", ctx->ch, period);
+       }
+
+       pwm_hwreg_t pwm_hw_getPeriod(Pwm *ctx)
+       {
+               return ctx->hw->base->CPRD;
+       }
 
        /*
-        * Set pwm mode:
-        * - set period alidned to left
-        * - set output waveform to start at high level
-        * - allow duty cycle modify at next period event
+        * Set pwm duty cycle.
+        *
+        * duty value 0 - (2^16 - 1)
         */
-       for (int ch = 0; ch < PWM_CNT; ch++)
+       void pwm_hw_setDuty(Pwm *ctx, pwm_hwreg_t hw_duty)
        {
-               *pwm_map[ch].mode_reg = 0;
-               *pwm_map[ch].mode_reg = BV(PWM_CPOL);
+               ASSERT(hw_duty <= ctx->hw->base->CPRD);
+
+               /*
+                * WARNING: is forbidden to write 0 or 1 to duty cycle value,
+                * and so for duty < 2 we must enable PIO and clear output!
+                */
+               if (hw_duty < 2)
+               {
+                       hw_duty = 2;
+                       PWM_PIO_PER = ctx->hw->pwm_pin;
+               }
+               else
+                       PWM_PIO_PDR = ctx->hw->pwm_pin;
+
+               ctx->hw->base->CUPD = hw_duty;
+               LOG_INFO("PWM ch[%d] duty[%d], period[%ld]\n", ctx->ch, hw_duty, ctx->hw->base->CPRD);
        }
 
-}
+       static PwmHardware pwm_channels[] =
+       {
+               {//PWM Channel 0
+                       .pwm_pin = BV(PWM0),
+                       .base = (volatile PwmChannelRegs *)&PWM_CMR0,
+               },
+               {//PWM Channel 1
+                       .pwm_pin = BV(PWM1),
+                       .base = (volatile PwmChannelRegs *)&PWM_CMR1,
+               },
+               {//PWM Channel 2
+                       .pwm_pin = BV(PWM2),
+                       .base = (volatile PwmChannelRegs *)&PWM_CMR2,
+               },
+               {//PWM Channel 3
+                       .pwm_pin = BV(PWM3),
+                       .base = (volatile PwmChannelRegs *)&PWM_CMR3,
+               },
+       };
+
+       /*
+        * Init pwm.
+        */
+       void pwm_hw_init(Pwm *ctx, unsigned ch)
+       {
+
+               ctx->hw = &pwm_channels[ch];
+
+               /*
+                * Init pwm:
+                * - clear PIO outputs
+                * - enable PIO outputs
+                * - Enable PWM functions
+                * - Power on PWM
+                */
+               PWM_PIO_CODR = ctx->hw->pwm_pin;
+               PWM_PIO_OER  = ctx->hw->pwm_pin;
+               PWM_PIO_PER  = ctx->hw->pwm_pin;
+               PWM_PIO_ABSR = ctx->hw->pwm_pin;
+
+               PMC_PCER |= BV(PWMC_ID);
+
+               /* Disable prescalers A and B */
+               PWM_MR = 0;
+
+               /*
+                * Set pwm mode:
+                * WARNING: is forbidden to write 0 or 1 to duty cycle value,
+                * and so for start we set duty to 2.
+                * Also:
+                * - set period aligned to left
+                * - set output waveform to start at high level
+                * - allow duty cycle modify at next period event
+                */
+               ctx->hw->base->CDTY = 2;
+               ctx->hw->base->CMR = BV(PWM_CPOL);
+               PWM_ENA = BV(ch);
+       }
 
+#endif