This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

AM4378: How to configure DM Timer4 as PWM output?

Part Number: AM4378

Hi,

I want to generate PWM output using the Timer4 pin.  When I was going through the TRM, I found timer can be configured as PWM output. I am working in u-boot, whether you have any source code or example to make timer4 as PWM in u-boot?

Regards

Monish P

  • Hello Monish,

    Before I pass you over to our Uboot owner, we do have a Linux driver for using the PWM subsystem to output PWM signals. Is there a reason you wanted to use Timer4 in particular, and u-boot instead of Linux?

    Regards,

    Nick

  • Hi Nick,

    In our custom board, we need to validate the hardware in u-boot only.

    We are not using PWMSS pin to generate PWM, we need to generate PWM using timer4 pin only.

    Regards

    Monish P

  • Hello Monish,

    Understood. I have reassigned this to our uboot owner. They will reply soon.

    Regards,

    Nick

  • Hi Monish,
    I'm not aware of PWM test code in u-boot.
    In addition to Nick's early comments, there's timer based PWM kernel driver in kernel for your reference:
    www.kernel.org/.../pwm-omap-dmtimer.txt
    "/drivers/pwm/pwm-omap-dmtimer.c"
    Best,
    -Hong

  • Hello Monish,

    You could also do this manually. i.e., set pinmux settings in uboot files, and then directly program the timer's registers based on the register information in the AM437x Technical Reference Manual (TRM). But I do not believe we provide any examples for manually setting up timer registers outside of the TRM.

    Regards,

    Nick

  • Hi Nick,

    I tried to program directly timer registers for PWM output, by following the steps in TRM. But observation from oscilloscope is the pin is in the HIGH state only. I have enabled the timer4 clock in clock.c. Please see the below configurations.

    #define DMTIMER4_BASE (0x48044000)                             /*Timer4 Base*/
    #define DMTIMER4_TCLR (DMTIMER4_BASE + 0x38)      /* TCLR register */
    #define DMTIMER4_TLDR (DMTIMER4_BASE + 0x40)      /* TLDR register */
    #define DMTIMER4_TMAR (DMTIMER4_BASE + 0x4C)     /* TMAR register */
    
    
    #define COUNTER4_PERIOD (0xFFFFFFE0)              /* Period : 1KHz */
    #define COUNTER4_DUTY (0xFFFFFFEF)                 /* Duty cycle : 50% */
    #define PWM_CONFIG_TCLR (0x00001842)              /* Setting in TCLR register : PT, TRG, TCM, SCPWM, CE, PRE, PTV, AR */
    #define PWM_ENABLE_TCLR (0x00000001)             /* Enable PWM timer : Setting ST bit */
    #define PWM_IRQEN_SET (0x00000001)                 /*Enable MAT_EN_FLAG */
    
     
    
    void timer4_enable(void)
    {
    
    __raw_writel(COUNTER4_PERIOD ,DMTIMER4_TLDR);        /* Setting period : 1KHz */
    
    __raw_writel(COUNTER4_DUTY ,DMTIMER4_TMAR);          /* Setting compare match value, dutycycle is 50% */
    
    __raw_writel(PWM_IRQEN_SET, DMTIMER4_IRQEN_SET);    /* Interrupt when the compare matched */
    
    __raw_writel((PWM_CONFIG_TCLR | PWM_ENABLE_TCLR) ,DMTIMER4_TCLR);       /* Start timer and configuring PWM output */
    
    }

    In dts file I have included the following for timer4 in dts file.

    timer4_pins_default: timer4_pins_default {
    pinctrl-single,pins = <
    0x090 (PIN_OUTPUT_PULLUP | MUX_MODE2) /* TIMER4 -> timer4 -> A9 */
    >;
    };
    
    
    &timer4 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&timer4_pins_default>;
    };
    Whether I have configured everything. Can u please help me in solving the issue?
  • Hi Monish,
    Depending on your schematic, have you tried configuring pinmux for timer4 PWM output pin (A9) as listed below?
    PIN_OUTPUT | MUX_MODE2 | PULL_DISABLE.
    For timer based PWM configuration, in addition to "19.1.3.5 Pulse-Width Modulation" in TRM, another reference is the kernel driver in my previous reply

    "/drivers/pwm/pwm-omap-dmtimer.c"
    pwm_omap_dmtimer_config()
    {
    	/*
    	 * Calculate the appropriate load and match values based on the
    	 * specified period and duty cycle. The load value determines the
    	 * period time and the match value determines the duty time.
    	 *
    	 * The period lasts for (DM_TIMER_MAX-load_value+1) clock cycles.
    	 * Similarly, the active time lasts (match_value-load_value+1) cycles.
    	 * The non-active time is the remainder: (DM_TIMER_MAX-match_value)
    	 * clock cycles.
    	 *
    	 * NOTE: It is required that: load_value <= match_value < DM_TIMER_MAX
    	 *
    	 * References:
    	 *   OMAP4430/60/70 TRM sections 22.2.4.10 and 22.2.4.11
    	 *   AM335x Sitara TRM sections 20.1.3.5 and 20.1.3.6
    	 */
    }

    Best,

    -Hong

  • Hi Hong,

    Thank you for the information. I made the changes whatever you said above, now the pin output is only low. Can you confirm once our settings are correct for PWM output using timer4?

    1. In mux.c I made the following changes, in custom board schematics, we didn't use any pull-up or pull-down resistors.

    static struct module_pin_mux timer4_pin_mux[] = {
    {OFFSET(gpmc_advn_ale), (MODE(2) | PULLUDDIS)}, /* TIMER4 (A9)*/
    {-1},
    };

    void enable_board_pin_mux(void)
    {
    configure_module_pin_mux(mmc1_pin_mux);
    configure_module_pin_mux(i2c0_pin_mux);
    configure_module_pin_mux(mdio_pin_mux);
    configure_module_pin_mux(i2c1_pin_mux);
    configure_module_pin_mux(i2c2_pin_mux);
    configure_module_pin_mux(gpio0_7_pin_mux);
    configure_module_pin_mux(spi2_pin_mux);
    configure_module_pin_mux(mii1_pin_mux);
    configure_module_pin_mux(mii_pruss1_pin_mux);
    configure_module_pin_mux(lcd_pin_mux);
    configure_module_pin_mux(timer4_pin_mux);
    #if defined(CONFIG_NAND)
    printf("Error: NAND flash not present on this board\n");
    #endif
    }

    2. In dts file, I made the following changes,


    timer4_pins_default: timer4_pins_default {
    pinctrl-single,pins = <
    0x090 (PIN_OUTPUT | MUX_MODE2 ) /* TIMER4 -> timer4 -> A9 */
    >;
    };

    &timer4 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&timer4_pins_default>;
    };

    In timer4.c and timer. h, we implemented the code in the register level.

    The following steps followed,

    Selecting CLK_M_OSC clock for timer4.
    ST=0, AR=1, PRE =1, PTV =0, SCPWM =0, TRG = 2 and PT =1.
    TLDR = 0xFFFFD120, value for 1KHz Period
    TMAR= 0xFFFFE88F, set 50% duty cycle
    Start the timer.
    Please find the below code.

    #define DMTIMER4_BASE (0x48044000) // Timer4 base
    #define DMTIMER4_TCLR (DMTIMER4_BASE + 0x38)
    #define DMTIMER4_TLDR (DMTIMER4_BASE + 0x40)
    #define DMTIMER4_TMAR (DMTIMER4_BASE + 0x4C)

    #define COUNTER4_PERIOD (0xFFFFD120) /* Period : 1KHz */
    #define COUNTER4_DUTY (0xFFFFE88F) /* Duty cycle : 50% */
    #define PWM_CONFIG_TCLR (0x00001862) /* Setting in TCLR register : PT, TRG, TCM, SCPWM,CE, PRE, PTV, AR */
    #define PWM_ENABLE_TCLR (0x00001863) /* Enable PWM timer : Setting ST bit */

    void timer4_config(void)
    {

    __raw_writel(0X0000000 ,DMTIMER4_TCLR); // stop timer

    while((__raw_readl(DMTIMER4_TCLR) & 0x00000001) !=0);

    printf("Timer stop\n");

    __raw_writel(PWM_CONFIG_TCLR ,DMTIMER4_TCLR); // Configuting the timer

    __raw_writel(COUNTER4_PERIOD ,DMTIMER4_TLDR); // Setting period

    __raw_writel(COUNTER4_DUTY ,DMTIMER4_TMAR); // setting the duty cycle
    }

    void timer4_enable(void)
    {
    timer4_config(); // Configuring the timer

    __raw_writel(PWM_ENABLE_TCLR ,DMTIMER4_TCLR); // Start the timer

    while((__raw_readl(DMTIMER4_TCLR) & 0x00000001) ==0); // Check timer enabled

    printf("Timer start\n");

    }

    In clock.c we enabled the input clock for the timer4.

    void enable_basic_clocks(void)
    {
    u32 *const clk_domains[] = {
    &cmper->l3clkstctrl,
    &cmper->l3sclkstctrl,
    &cmper->l4lsclkstctrl,
    &cmwkup->wkclkstctrl,
    &cmper->emifclkstctrl,
    0
    };

    u32 *const clk_modules_explicit_en[] = {
    &cmper->l3clkctrl,
    &cmper->l4lsclkctrl,
    &cmper->l4fwclkctrl,
    &cmwkup->wkl4wkclkctrl,
    &cmper->l3instrclkctrl,
    &cmper->l4hsclkctrl,
    &cmwkup->wkgpio0clkctrl,
    &cmwkup->wkctrlclkctrl,
    &cmper->gpmcclkctrl,
    &cmper->elmclkctrl,
    &cmper->mmc0clkctrl,
    &cmper->mmc1clkctrl,
    &cmwkup->wkup_i2c0ctrl,
    &cmwkup->wkup_adctscclkctrl,
    &cmper->gpio1clkctrl,
    &cmper->gpio2clkctrl,
    &cmper->gpio3clkctrl,
    &cmper->gpio4clkctrl,
    &cmper->gpio5clkctrl,
    &cmper->i2c1clkctrl,
    &cmper->i2c2clkctrl,
    &cmper->adc1clkctrl,
    &cmper->cpgmac0clkctrl,
    &cmper->emiffwclkctrl,
    &cmper->emifclkctrl,
    &cmper->otfaemifclkctrl,
    &cmper->lcdclkctrl,
    &cmper-> spi0clkctrl,
    &cmper-> spi1clkctrl,
    &cmper->timer7clkctrl,
    &cmper->timer4clkctrl,
    &cmper->uart1clkctrl,
    &cmper->uart2clkctrl,
    &cmper->uart4clkctrl,
    0
    };

    do_enable_clocks(clk_domains, clk_modules_explicit_en, 1);

    /* Select the Master osc clk as Timer2 clock source */
    writel(0x1, &cmdpll->clktimer2clk);


    /* Select the Master osc clk as Timer4 clock source */
    writel(0x1, &cmdpll->clktimer4clk);


    /* For OPP100 the mac clock should be /5. */
    writel(0x4, &cmdpll->clkselmacclk);

    }

    When I tested the code, the output was only low, I am not getting PWM pulse. Kindly give me the solution.

  • Hi Monish,
    There's an earlier e2e thread on using timer to generating PWM on AM335x for your reference
    e2e.ti.com/.../am335x-pwm-on-dmtimer
    Best,
    -Hong

  • Hi Hong,

    Thank you for your reply. The issue got resolved.

    I included the following in the code.

    1. I didn't set the initial counter value in DMTIMER_TCRR register. (__raw_writel(COUNTER4_PERIOD ,DMTIMER4_TCRR);

     

    2. Also I didn't force the timer to be in smart idle mode using DMTIMER_TIOCP_CFG Register. ( __raw_writel(0x08 ,DMTIMER4_TIOCP_CFG);

    Thanks and Regards

    Monish