144 lines
3.3 KiB
C
144 lines
3.3 KiB
C
#include "plat_types.h"
|
|
//#include "plat_basic_api.h"
|
|
//#include "ui_log_api.h"
|
|
|
|
#include "__regs_pwm.h"
|
|
|
|
//#include "clock_api.h"
|
|
|
|
struct pwm_ctl {
|
|
unsigned int pwm_cr; /*address*/
|
|
unsigned int pwm_dcr;
|
|
unsigned int pwm_pcr;
|
|
|
|
unsigned long clk_freq;
|
|
};
|
|
|
|
static struct pwm_ctl pwm[4];
|
|
|
|
/**
|
|
How to get the PV(The value of after scaled clock cycles per cycle/T):
|
|
InputClockT * ('PV(The value of before scaled clock cycles per cycle)) = PWM_T
|
|
'PV = PWM_T / InputClockT
|
|
= PWM_T * InputClockF
|
|
PV = 'PV /(prescale + 1) -1
|
|
|
|
How to get the prescale:
|
|
Because the internal clock period counter is 10-bit, to avoid overun. We can use the prescale.
|
|
prescale real value = ('PV - 1(if > 1024, then need prescale)) / 1024
|
|
|
|
How to get the Duty cycle:
|
|
Duty cycle = (PV(the cycles per T) + 1) * ratio(Duty time/PWM_T)
|
|
|
|
*/
|
|
static int __pwm_set_para(struct pwm_ctl *p, int period_ns, int duty_ns)
|
|
{
|
|
unsigned long period_cycles, prescale, pv, dc;
|
|
unsigned long long clk_freq;
|
|
|
|
clk_freq = p->clk_freq;
|
|
clk_freq = clk_freq * period_ns;
|
|
clk_freq = clk_freq / (1*1000*1000*1000);
|
|
|
|
period_cycles = clk_freq;
|
|
|
|
if (period_cycles < 1)
|
|
period_cycles = 1;
|
|
|
|
prescale = (period_cycles - 1) / 1024;
|
|
|
|
pv = period_cycles / (prescale + 1) - 1;
|
|
|
|
if (prescale > 63) {
|
|
raw_uart_log("clock and period are too large, Both prescale is overun!\n\r");
|
|
return -1;
|
|
}
|
|
if (duty_ns == period_ns)
|
|
dc = BIT_FD;
|
|
else
|
|
dc = (pv + 1) * duty_ns / period_ns;
|
|
|
|
/* NOTE: the clock to PWM has to be enabled first
|
|
* before writing to the registers
|
|
*/
|
|
//__pwm_enable(p);
|
|
|
|
raw_uart_log("prescale = 0x%x, dc = 0x%x, pv = 0x%x\n\r", prescale, dc, pv);
|
|
CHIP_REG_OR(p->pwm_cr, prescale);
|
|
write32(p->pwm_dcr, dc);
|
|
write32(p->pwm_pcr, pv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void pwm_shutdow_mode(struct pwm_ctl *p, int is_graceful_down)
|
|
{
|
|
//unsigned int addr;
|
|
if (is_graceful_down) {
|
|
CHIP_REG_AND(p->pwm_cr, ~BIT_SD);
|
|
} else {
|
|
CHIP_REG_OR(p->pwm_cr, BIT_SD);
|
|
}
|
|
}
|
|
|
|
/*Only support port 0, 1, 2, 3*/
|
|
int pwm_start_work(int port, int period_ns, int duty_ns)
|
|
{
|
|
if (port > 3)
|
|
return -1;
|
|
return __pwm_set_para(&pwm[port], period_ns, duty_ns);
|
|
}
|
|
|
|
/*Only support port 0, 1, 2, 3*/
|
|
int pwm_set_shutdown_mode(int port, int is_graceful_down)
|
|
{
|
|
if (port > 3)
|
|
return -1;
|
|
|
|
pwm_shutdow_mode(&pwm[port], is_graceful_down);
|
|
return 0;
|
|
}
|
|
|
|
/*Only support port 0, 1, 2, 3*/
|
|
int pwm_disable(int port)
|
|
{
|
|
if (port > 3)
|
|
return -1;
|
|
|
|
write32((APBC_PWM0_CLK_RST + port * 4), 0);
|
|
return 0;
|
|
}
|
|
|
|
/*Only support port 0, 1, 2, 3*/
|
|
int pwm_enable(int port,unsigned long clk_freq)
|
|
{
|
|
if (port > 3)
|
|
return -1;
|
|
if(clk_freq == 12800000)
|
|
write32((APBC_PWM0_CLK_RST + port * 4),0x3 | (PWM_13M <<4));
|
|
else
|
|
write32((APBC_PWM0_CLK_RST + port * 4),0x3 | (PWM_32K <<4));
|
|
return 0;
|
|
}
|
|
|
|
|
|
void pwm_module_init(int port, unsigned long clk_freq)
|
|
{
|
|
//unsigned long long clk_freq;
|
|
|
|
if (port >= ARR_SIZE(pwm)) {
|
|
raw_uart_log("pwm_module_init wrong: port = %d\n\r",port);
|
|
return;
|
|
}
|
|
|
|
//clk_freq = pwm_clksource_select(clk_id);
|
|
raw_uart_log("pwm clock frequency is %d\n",clk_freq);
|
|
|
|
pwm[port].pwm_cr = REG_PWM0_PWM_CRx + port * 0x400;
|
|
pwm[port].pwm_dcr= REG_PWM0_PWM_DCR + port * 0x400;
|
|
pwm[port].pwm_pcr= REG_PWM0_PWM_PCR + port * 0x400;
|
|
pwm[port].clk_freq = clk_freq;
|
|
pwm_enable(port,clk_freq);
|
|
}
|