Files
banban/ql-bootloader/common/src/pwm_drv.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);
}