2025-10-10 16:07:00 +08:00

908 lines
26 KiB
C

// Copyright 2020-2021 Beken
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <common/bk_include.h>
#include <common/bk_compiler.h>
#include "icu_driver.h"
#include <os/mem.h>
#include <driver/pwm.h>
#include "pwm_hal.h"
#include "pwm_driver.h"
#include "clock_driver.h"
#include "power_driver.h"
#include <driver/int.h>
#include "sys_driver.h"
#include "gpio_driver.h"
#include <driver/gpio.h>
static void pwm_isr(uint32_t unit_id) __BK_SECTION(".itcm");
static void pwm_isr_unit0(void);
static void pwm_isr_unit1(void);
//Info:
/*
* There are 2 units PWM in ASIC chip, each unit has 3 groups PWM, each group has 2 channels.
* Some Chip hasn't some GPIO cause some special PWM channel can't be used.
* As this PWM driver codes have been released to many customers, and can't change API.
* I am applogized that some history codes looks urgly ...
*/
//TODO list
// 1. Check if a PWM channel already in using
// 2. Support PWM timer/counter API
typedef struct {
pwm_chan_t chan1;
pwm_chan_t chan2;
bool is_valid;
} pwm_group_info_t;
#define PWM_GROUP_NUM_IN_ONE_UNIT (SOC_PWM_CHAN_NUM_PER_UNIT >> 1) //one unit has many groups,driver use it
#define PWM_GPIO_MODE_MAX 4
#define PWM_APP_GROUP_MAX (SOC_PWM_UNIT_NUM * PWM_GROUP_NUM_IN_ONE_UNIT)
#define PWM_CHAN_MASK (0x3f) //6 channels
typedef struct {
pwm_hal_t hal[SOC_PWM_UNIT_NUM];
//Important notes: currently no lock for bits
//Concurrently operation is NOT allowed!!!
uint32_t chan_init_bits[SOC_PWM_UNIT_NUM];
pwm_group_info_t groups[SOC_PWM_UNIT_NUM][PWM_GROUP_NUM_IN_ONE_UNIT];
uint8_t init_level[SOC_PWM_UNIT_NUM][PWM_GROUP_NUM_IN_ONE_UNIT];//0:not init 1:init high 2:init low for every channel
} pwm_driver_t;
static pwm_driver_t s_pwm = {0};
static pwm_isr_t s_pwm_isr[SOC_PWM_UNIT_NUM][SOC_PWM_CHAN_NUM_PER_UNIT] = {NULL};
static bool s_pwm_driver_is_init = false;
#define PWM_RETURN_ON_INVALID_CHAN(chan) do {\
if ((chan) >= (SOC_PWM_CHAN_NUM_PER_UNIT * SOC_PWM_UNIT_NUM)) {\
return BK_ERR_PWM_CHAN_ID;\
}\
} while(0)
#define PWM_RETURN_ON_INVALID_CHAN_CLOCK(clock) do {\
if ((clock) >= PWM_CHAN_CLOCK_MAX) {\
return BK_ERR_PWM_CHAN_CLOCK;\
}\
} while(0)
#define PWM_RETURN_ON_NOT_INIT() do {\
if (!s_pwm_driver_is_init) {\
return BK_ERR_PWM_NOT_INIT;\
}\
} while(0)
#define PWM_RETURN_ON_CHAN_NOT_INIT(unit_id, drv_chan) do {\
if (!(s_pwm.chan_init_bits[unit_id] & BIT(drv_chan))) {\
return BK_ERR_PWM_CHAN_NOT_INIT;\
}\
} while(0)
#define PWM_CHAN_ID_TO_UNIT_ID(chan) (chan/SOC_PWM_CHAN_NUM_PER_UNIT)
//APP channel uses 0~11, driver each unit has 6 channels, so uses 0~5,API no need to modify for customers.
#define PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan) (chan % SOC_PWM_CHAN_NUM_PER_UNIT)
//APP group uses 0~5, driver each unit has 3 groups, so uses 0~2,API no need to modify for customers.
#define PWM_APP_GROUP_TO_DRIVER_GROUP(group) (group % SOC_PWM_GROUP_NUM)
#define PWM_APP_GROUP_TO_UNIT_ID(group) (group / SOC_PWM_GROUP_NUM)
#define PWM_RETURN_ON_INVALID_GROUP_ID(group) do{\
if (group >= PWM_APP_GROUP_MAX) {\
return BK_ERR_PWM_GROUP_ID;\
}\
} while(0)
#define PWM_RETURN_ON_INVALID_GROUP(chan) do {\
if (chan >= PWM_GROUP_NUM_IN_ONE_UNIT) {\
return BK_ERR_PWM_GROUP_ID;\
}\
} while(0)
#define PWM_RETURN_ON_INVALID_GPIO_MODE(chan) do {\
if (chan >= PWM_GPIO_MODE_MAX) {\
return BK_ERR_PWM_INVALID_GPIO_MODE;\
}\
} while(0)
#define PWM_RETURN_ON_CHAN_NOT_STOP(chan) do {\
} while(0)
#define PWM_SET_PIN(id) do {\
gpio_dev_unmap(PWM##id##_LL_PIN);\
gpio_dev_map(PWM##id##_LL_PIN, GPIO_DEV_PWM##id);\
bk_gpio_pull_up(PWM##id##_LL_PIN);\
} while(0)
static void pwm_init_gpio(pwm_id_t id)
{
switch(id) {
case PWM_ID_0:
PWM_SET_PIN(0);
break;
case PWM_ID_1:
PWM_SET_PIN(1);
break;
case PWM_ID_2:
PWM_SET_PIN(2);
break;
case PWM_ID_3:
PWM_SET_PIN(3);
break;
case PWM_ID_4:
PWM_SET_PIN(4);
break;
case PWM_ID_5:
PWM_SET_PIN(5);
break;
case PWM_ID_6:
PWM_SET_PIN(6);
break;
case PWM_ID_7:
PWM_SET_PIN(7);
break;
case PWM_ID_8:
PWM_SET_PIN(8);
break;
case PWM_ID_9:
PWM_SET_PIN(9);
break;
case PWM_ID_10:
PWM_SET_PIN(10);
break;
case PWM_ID_11:
PWM_SET_PIN(11);
break;
default:
break;
}
}
//TODO call it via gpio hal
static void pwm_set_gpio(pwm_chan_t chan)
{
pwm_init_gpio(chan);
}
static void pwm_chan_enable_interrupt_common(pwm_chan_t chan)
{
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
pwm_hal_enable_interrupt(&s_pwm.hal[unit_id], drv_ch);
}
/* Once the global PWM interrupt is enabled, we never disable it.
* It should be OK, because if all PWM are stop, there will be no
* PWM interrupt even though the global PWM interrupt is enabled.
*
* We can choose to disable the global PWM interrupt if all PWM
* channels are stopped, but it make the code complex!
**/
static void pwm_chan_disable_interrupt_common(pwm_chan_t chan)
{
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
pwm_hal_clear_chan_interrupt_status(&s_pwm.hal[unit_id], drv_ch);
}
/* Init common to PWM/Capture init
* 1. Set channel init bit
* 2. Power up channel
* 3. Set channel specific clock
*/
static void pwm_chan_init_common(pwm_chan_t chan)
{
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
s_pwm.chan_init_bits[unit_id] |= BIT(drv_ch);
#if (CONFIG_SYSTEM_CTRL)
//all of the channels deinit, then power-down
if ((unit_id == 0) && (s_pwm.chan_init_bits[unit_id] & PWM_CHAN_MASK)) {
sys_drv_dev_clk_pwr_up(CLK_PWR_ID_PWM_1, CLK_PWR_CTRL_PWR_UP);
}
//one of the channels init, then power-up
if ((unit_id == 1) && (s_pwm.chan_init_bits[unit_id] & PWM_CHAN_MASK)) {
sys_drv_dev_clk_pwr_up(CLK_PWR_ID_PWM_2, CLK_PWR_CTRL_PWR_UP);
}
#else
power_pwm_pwr_up(chan);
/* The hardware support to enable LPO clock, however no customer
* currently use LPO clock, here we always disable LPO clock for
* simplicity.
**/
clk_disable_pwm_clk_lpo(chan);
#endif
//TODO call hal
pwm_set_gpio(chan);
pwm_hal_set_init_signal_high(&s_pwm.hal[unit_id], drv_ch);
}
/* chan has switched from APP channel-id to DRIVER channel-id */
static void pwm_chan_deinit_common(uint32_t unit_id, pwm_chan_t drv_chan)
{
s_pwm.chan_init_bits[unit_id] &= ~BIT(drv_chan);
pwm_hal_stop_common(&s_pwm.hal[unit_id], drv_chan);
pwm_hal_reset_config_to_default(&s_pwm.hal[unit_id], drv_chan);
#if (CONFIG_SYSTEM_CTRL)
//all of the channels deinit, then power-down
if ((unit_id == 0) && ((s_pwm.chan_init_bits[unit_id] & PWM_CHAN_MASK)== 0)) {
sys_drv_dev_clk_pwr_up(CLK_PWR_ID_PWM_1, CLK_PWR_CTRL_PWR_DOWN);
}
if((unit_id == 1) && (((s_pwm.chan_init_bits[unit_id]) & PWM_CHAN_MASK)== 0)) {
sys_drv_dev_clk_pwr_up(CLK_PWR_ID_PWM_2, CLK_PWR_CTRL_PWR_DOWN);
}
#else
power_pwm_pwr_down(drv_chan);
#endif
}
//TODO we should remove pwm_init finally
//Implement pwm_init/pwm_exit to make it compitable with existing driver model
bk_err_t bk_pwm_driver_init(void)
{
if (s_pwm_driver_is_init)
return BK_OK;
os_memset(&s_pwm, 0, sizeof(s_pwm));
/*
* Hardware support to configure DCO clock, considering no customers use
* 32k clock, we hard-coded to 26M clock for simplicity.
*/
#if (CONFIG_SYSTEM_CTRL)
sys_drv_pwm_select_clock(SYS_SEL_PWM0, PWM_SCLK_XTAL);
sys_drv_pwm_select_clock(SYS_SEL_PWM1, PWM_SCLK_XTAL);
#else
clk_set_pwms_clk_26m();
#endif
os_memset(&s_pwm, 0, sizeof(s_pwm));
for(uint32_t unit_id = 0; unit_id < SOC_PWM_UNIT_NUM; unit_id++) {
s_pwm.hal[unit_id].id = unit_id;
pwm_hal_init(&s_pwm.hal[unit_id]);
}
os_memset(&s_pwm_isr, 0, sizeof(s_pwm_isr));
bk_int_isr_register(INT_SRC_PWM, pwm_isr_unit0, NULL);
sys_drv_int_enable(PWM_INTERRUPT_CTRL_BIT);
bk_int_isr_register(INT_SRC_PWM1, pwm_isr_unit1, NULL);
sys_drv_int_group2_enable(PWM1_INTERRUPT_CTRL_BIT);
s_pwm_driver_is_init = true;
return BK_OK;
}
bk_err_t bk_pwm_driver_deinit(void)
{
if (!s_pwm_driver_is_init)
return BK_OK;
for(uint32_t unit_id = 0; unit_id < SOC_PWM_UNIT_NUM; unit_id++) {
for (int chan = 0; chan < SOC_PWM_CHAN_NUM_PER_UNIT; chan++) {
pwm_chan_deinit_common(unit_id, chan);
}
}
s_pwm_driver_is_init = false;
return BK_OK;
}
static void pwm_adjust_init_signal_via_duty(pwm_chan_t chan, uint32_t period,
uint32_t duty1, uint32_t duty2, uint32_t duty3)
{
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
if(s_pwm.init_level[unit_id][drv_ch]==2)
{//init low
/** If duty ratio is 100% and initial signal is low,
* then the PWM signal will always be low.
* To avoid that, we manually configure init signal to high if
* duty ratio is 100%.
*/
if (duty1 == period)
pwm_hal_set_init_signal_high(&s_pwm.hal[unit_id], drv_ch);
else
pwm_hal_set_init_signal_low(&s_pwm.hal[unit_id], drv_ch);
}
else
{//init high
/** If duty ratio is 0% and initial signal is high,
* then the PWM signal will always be high.
* To avoid that, we manually configure init signal to low if
* duty ratio is 0%.
*/
if (duty1 == 0)
pwm_hal_set_init_signal_low(&s_pwm.hal[unit_id], drv_ch);
else
pwm_hal_set_init_signal_high(&s_pwm.hal[unit_id], drv_ch);
}
}
bk_err_t bk_pwm_init(pwm_chan_t chan, const pwm_init_config_t *config)
{
PWM_RETURN_ON_NOT_INIT();
BK_RETURN_ON_NULL(config);
PWM_RETURN_ON_INVALID_CHAN(chan);
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
if (!pwm_hal_is_duty_valid(config->period_cycle, config->duty_cycle,
config->duty2_cycle, config->duty3_cycle))
return BK_ERR_PWM_PERIOD_DUTY;
pwm_chan_init_common(chan);
pwm_adjust_init_signal_via_duty(chan, config->period_cycle, config->duty_cycle,
config->duty2_cycle, config->duty3_cycle);
pwm_hal_config_t hal_config = {0};
hal_config.t4 = config->period_cycle;
hal_config.t1 = config->duty_cycle;
if (config->duty2_cycle > 0)
hal_config.t2 = hal_config.t1 + config->duty2_cycle;
if (config->duty3_cycle > 0)
hal_config.t3 = hal_config.t2 + config->duty3_cycle;
PWM_LOGI("init, chan(%d), t1=%x, t2=%x, t3=%x, t4=%x\n",
chan, hal_config.t1, hal_config.t2, hal_config.t3, hal_config.t4);
s_pwm.init_level[unit_id][drv_ch]=0;
return pwm_hal_init_pwm(&s_pwm.hal[unit_id], drv_ch, &hal_config);
}
bk_err_t bk_pwm_deinit(pwm_chan_t chan)
{
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
s_pwm.init_level[unit_id][drv_ch]=0;
pwm_chan_deinit_common(unit_id, drv_ch);
return BK_OK;
}
bk_err_t bk_pwm_start(pwm_chan_t chan)
{
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
PWM_RETURN_ON_CHAN_NOT_INIT(unit_id, drv_ch);
pwm_hal_start_common(&s_pwm.hal[unit_id], drv_ch);
return BK_OK;
}
bk_err_t bk_pwm_stop(pwm_chan_t chan)
{
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
PWM_RETURN_ON_CHAN_NOT_INIT(unit_id, drv_ch);
pwm_hal_stop_common(&s_pwm.hal[unit_id], drv_ch);
return BK_OK;
}
bk_err_t bk_pwm_register_isr(pwm_chan_t chan, pwm_isr_t isr)
{
PWM_RETURN_ON_NOT_INIT();
PWM_RETURN_ON_INVALID_CHAN(chan);
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
GLOBAL_INT_DECLARATION();
GLOBAL_INT_DISABLE();
s_pwm_isr[unit_id][drv_ch] = isr;
GLOBAL_INT_RESTORE();
return BK_OK;
}
/* The PWM channel should be started before interrupt can be enabled.
*/
bk_err_t bk_pwm_enable_interrupt(pwm_chan_t chan)
{
PWM_RETURN_ON_INVALID_CHAN(chan);
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
PWM_RETURN_ON_CHAN_NOT_INIT(unit_id, drv_ch);
//BK_LOGI("TODO:PWM:why enable INT has to check channel started?\r\n");
if (pwm_hal_is_chan_started(&s_pwm.hal[unit_id], drv_ch)) {
pwm_chan_enable_interrupt_common(chan);
return BK_OK;
} else
return BK_ERR_PWM_CHAN_NOT_START;
}
bk_err_t bk_pwm_disable_interrupt(pwm_chan_t chan)
{
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
PWM_RETURN_ON_CHAN_NOT_INIT(unit_id, drv_ch);
pwm_chan_disable_interrupt_common(chan);
return BK_OK;
}
bk_err_t bk_pwm_set_period_duty(pwm_chan_t chan, pwm_period_duty_config_t *config)
{
uint32_t t1 = 0;
uint32_t t2 = 0;
uint32_t t3 = 0;
uint32_t t4 = 0;
BK_RETURN_ON_NULL(config);
PWM_RETURN_ON_INVALID_CHAN(chan);
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
PWM_RETURN_ON_CHAN_NOT_INIT(unit_id, drv_ch);
if (!pwm_hal_is_duty_valid(config->period_cycle, config->duty_cycle,
config->duty2_cycle, config->duty3_cycle))
return BK_ERR_PWM_PERIOD_DUTY;
pwm_adjust_init_signal_via_duty(chan, config->period_cycle, config->duty_cycle,
config->duty2_cycle, config->duty3_cycle);
pwm_hal_set_t1(&s_pwm.hal[unit_id], drv_ch, config->duty_cycle);
t1 = config->duty_cycle;
t2 = config->duty2_cycle;
if (t2 > 0)
t2 += t1;
t3 = config->duty3_cycle;
if (t3 > 0)
t3 += t2;
t4 = config->period_cycle;
pwm_hal_set_t1(&s_pwm.hal[unit_id], drv_ch, t1);
pwm_hal_set_t2(&s_pwm.hal[unit_id], drv_ch, t2);
pwm_hal_set_t3(&s_pwm.hal[unit_id], drv_ch, t3);
pwm_hal_set_t4(&s_pwm.hal[unit_id], drv_ch, t4);
//The new configuration take effect only loading it
pwm_hal_load_new_config(&s_pwm.hal[unit_id], drv_ch);
return BK_OK;
}
static bool pwm_is_0_duty_ratio(pwm_chan_t chan)
{
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
return (pwm_hal_get_t1(&s_pwm.hal[unit_id], drv_ch) == 0);
}
static bool pwm_is_100_duty_ratio(pwm_chan_t chan)
{
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
return (pwm_hal_get_t1(&s_pwm.hal[unit_id], drv_ch) == pwm_hal_get_t4(&s_pwm.hal[unit_id], drv_ch));
}
bk_err_t bk_pwm_set_init_signal_low(pwm_chan_t chan)
{
PWM_RETURN_ON_INVALID_CHAN(chan);
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
PWM_RETURN_ON_CHAN_NOT_INIT(unit_id, drv_ch);
s_pwm.init_level[unit_id][drv_ch]=2;
if (pwm_is_100_duty_ratio(chan))
pwm_hal_set_init_signal_high(&s_pwm.hal[unit_id], drv_ch);
else
pwm_hal_set_init_signal_low(&s_pwm.hal[unit_id], drv_ch);
pwm_hal_load_new_config(&s_pwm.hal[unit_id], drv_ch);
return BK_OK;
}
bk_err_t bk_pwm_set_init_signal_high(pwm_chan_t chan)
{
PWM_RETURN_ON_INVALID_CHAN(chan);
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
PWM_RETURN_ON_CHAN_NOT_INIT(unit_id, drv_ch);
s_pwm.init_level[unit_id][drv_ch]=1;
if (pwm_is_0_duty_ratio(chan))
pwm_hal_set_init_signal_low(&s_pwm.hal[unit_id], drv_ch);
else
pwm_hal_set_init_signal_high(&s_pwm.hal[unit_id], drv_ch);
return BK_OK;
}
bk_err_t bk_pwm_capture_init(pwm_chan_t chan, const pwm_capture_init_config_t *config)
{
PWM_RETURN_ON_NOT_INIT();
BK_RETURN_ON_NULL(config);
PWM_RETURN_ON_INVALID_CHAN(chan);
if (!pwm_hal_is_capture_edge_valid(config->edge))
return BK_ERR_PWM_CAPTURE_EDGE;
pwm_chan_init_common(chan);
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
s_pwm_isr[unit_id][drv_ch] = config->isr;
pwm_hal_capture_config_t hal_config = {
.edge = config->edge,
};
return pwm_hal_init_capture(&s_pwm.hal[unit_id], drv_ch, &hal_config);
}
bk_err_t bk_pwm_capture_deinit(pwm_chan_t chan)
{
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
PWM_RETURN_ON_NOT_INIT();
PWM_RETURN_ON_INVALID_CHAN(chan);
pwm_chan_deinit_common(unit_id, drv_ch);
return BK_OK;
}
bk_err_t bk_pwm_capture_start(pwm_chan_t chan)
{
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
PWM_RETURN_ON_CHAN_NOT_INIT(unit_id, drv_ch);
PWM_RETURN_ON_INVALID_CHAN(chan);
pwm_hal_start_common(&s_pwm.hal[unit_id], drv_ch);
pwm_chan_enable_interrupt_common(chan); //interrupt should be enabled after capture start
return BK_OK;
}
bk_err_t bk_pwm_capture_stop(pwm_chan_t chan)
{
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
PWM_RETURN_ON_CHAN_NOT_INIT(unit_id, drv_ch);
PWM_RETURN_ON_INVALID_CHAN(chan);
pwm_hal_stop_common(&s_pwm.hal[unit_id], drv_ch);
return BK_OK;
}
//It's up the caller to make sure the channel ID is correct
uint32_t bk_pwm_capture_get_value(pwm_chan_t chan)
{
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
return pwm_hal_get_capture_value(&s_pwm.hal[unit_id], drv_ch);
}
static pwm_group_t pwm_group_find_channel(pwm_chan_t chan)
{
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
for (int i = 0; i < PWM_GROUP_NUM_IN_ONE_UNIT; i++) {
if (s_pwm.groups[unit_id][i].is_valid && ((s_pwm.groups[unit_id][i].chan1 == chan) ||
(s_pwm.groups[unit_id][i].chan2 == chan)))
return i;
}
return PWM_GROUP_ID_INVALID;
}
static pwm_group_t pwm_group_find(pwm_chan_t chan1, pwm_chan_t chan2)
{
uint32_t unit_id1 = PWM_CHAN_ID_TO_UNIT_ID(chan1);
uint32_t unit_id2 = PWM_CHAN_ID_TO_UNIT_ID(chan2);
if(unit_id1 != unit_id2)
return PWM_GROUP_ID_INVALID;
for (int i = 0; i < PWM_GROUP_NUM_IN_ONE_UNIT; i++) {
if (s_pwm.groups[unit_id1][i].is_valid) {
if ((s_pwm.groups[unit_id1][i].chan1 == chan1) &&
(s_pwm.groups[unit_id1][i].chan2 == chan2))
return i;
if ((s_pwm.groups[unit_id1][i].chan1 == chan2) &&
(s_pwm.groups[unit_id1][i].chan2 == chan1))
return i;
}
}
return PWM_GROUP_ID_INVALID;
}
static bool pwm_group_is_existed(pwm_group_t group)
{
uint32_t unit_id = PWM_APP_GROUP_TO_UNIT_ID(group);
uint32_t drv_group = PWM_APP_GROUP_TO_DRIVER_GROUP(group);
return (s_pwm.groups[unit_id][drv_group].is_valid == true);
}
static pwm_group_t pwm_group_add(pwm_chan_t chan1, pwm_chan_t chan2)
{
uint32_t unit_id1 = PWM_CHAN_ID_TO_UNIT_ID(chan1);
uint32_t unit_id2 = PWM_CHAN_ID_TO_UNIT_ID(chan2);
if(unit_id1 != unit_id2)
return PWM_GROUP_ID_INVALID;
if((chan1/SOC_PWM_CHAN_NUM_PER_GROUP) != (chan2/SOC_PWM_CHAN_NUM_PER_GROUP))
return PWM_GROUP_ID_INVALID;
/* for(uint32_t unit_id = 0; unit_id < SOC_PWM_UNIT_NUM; unit_id++) */ {
/* for (int i = 0; i < PWM_GROUP_NUM_IN_ONE_UNIT; i++) */ {
uint32_t drv_group = (chan1%SOC_PWM_CHAN_NUM_PER_UNIT)/SOC_PWM_CHAN_NUM_PER_GROUP;
if (!s_pwm.groups[unit_id1][drv_group].is_valid) {
s_pwm.groups[unit_id1][drv_group].is_valid = true;
s_pwm.groups[unit_id1][drv_group].chan1 = chan1;
s_pwm.groups[unit_id1][drv_group].chan2 = chan2;
return ((unit_id1 * PWM_GROUP_NUM_IN_ONE_UNIT) + drv_group);
}
}
}
return PWM_GROUP_ID_INVALID;
}
static bk_err_t pwm_group_delete(pwm_group_t group)
{
PWM_RETURN_ON_INVALID_GROUP_ID(group);
uint32_t unit_id = PWM_APP_GROUP_TO_UNIT_ID(group);
uint32_t drv_group = PWM_APP_GROUP_TO_DRIVER_GROUP(group);
s_pwm.groups[unit_id][drv_group].is_valid = false;
return BK_OK;
}
static bk_err_t pwm_group_validate_param(pwm_chan_t chan1, pwm_chan_t chan2,
uint32_t period_cycle, uint32_t chan1_duty_cycle, uint32_t chan2_duty_cycle)
{
PWM_RETURN_ON_INVALID_CHAN(chan1);
PWM_RETURN_ON_INVALID_CHAN(chan2);
if (chan1 == chan2)
return BK_ERR_PWM_GROUP_SAME_CHAN;
if (PWM_GROUP_ID_INVALID != pwm_group_find(chan1, chan2))
return BK_ERR_PWM_GROUP_EXIST;
if (PWM_GROUP_ID_INVALID != pwm_group_find_channel(chan1))
return BK_ERR_PWM_GROUP_CHAN_USED;
if (PWM_GROUP_ID_INVALID != pwm_group_find_channel(chan2))
return BK_ERR_PWM_GROUP_CHAN_USED;
if (period_cycle < (chan1_duty_cycle + chan2_duty_cycle))
return BK_ERR_PWM_GROUP_DUTY;
//duty is 0 is meanless
if ((chan1_duty_cycle == 0) || (chan2_duty_cycle == 0))
return BK_ERR_PWM_GROUP_DUTY;
return BK_OK;
}
/* NOTES:pwm_group_t group is for APP, driver needs to translate it for DRIVER group in unit. */
bk_err_t bk_pwm_group_init(const pwm_group_init_config_t *config, pwm_group_t *group)
{
pwm_init_config_t pwm_config;
uint32_t dead_cycle = 0;
int ret;
PWM_RETURN_ON_NOT_INIT();
BK_RETURN_ON_NULL(group);
*group = PWM_GROUP_ID_INVALID;
ret = pwm_group_validate_param(config->chan1, config->chan2, config->period_cycle,
config->chan1_duty_cycle, config->chan2_duty_cycle);
if (ret != BK_OK)
return ret;
/* Relationship between period/duty
*
* T14/T24 - Period cycle P, P = period_cycle
* T11 - PWM channel 1 duty cycle, T11 = chan1_duty_cycle
* T21 - PWM channel 2 signal reverse time1
* T22 - PWM cahnnel 2 signal reverse time2
* D2 = (T22 - T21) = chan2_duty_cycle, it's channe 2's high signal duty cycle
* D = (P - chan1_duty_cycle - chan2_duty_cycle)/2, the dead cycle
*
* T21 = T11 + D
* T22 = T21 + chan2_duty_cycle
*
* |<--D----->|<--D2-----|<--D----->|
* ______________
* channe1 | |________________________________|
* 0 T11 T14
* __________
* channel2 |_________________________| |__________|
* 0 T21 T22 T24
*
*/
pwm_config.period_cycle = config->period_cycle;
pwm_config.duty_cycle = config->chan1_duty_cycle;
pwm_config.duty2_cycle = 0;
pwm_config.duty3_cycle = 0;
pwm_config.psc = 25;
ret = bk_pwm_init(config->chan1, &pwm_config);
if (ret != BK_OK)
return ret;
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(config->chan1);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(config->chan1);
pwm_hal_set_init_signal_high(&s_pwm.hal[unit_id], drv_ch);
dead_cycle = (config->period_cycle - config->chan1_duty_cycle - config->chan2_duty_cycle) >> 1;
pwm_config.duty_cycle = config->chan1_duty_cycle + dead_cycle;
pwm_config.duty2_cycle = config->chan2_duty_cycle;
ret = bk_pwm_init(config->chan2, &pwm_config);
if (ret != BK_OK)
return ret;
unit_id = PWM_CHAN_ID_TO_UNIT_ID(config->chan2);
drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(config->chan2);
pwm_hal_set_init_signal_low(&s_pwm.hal[unit_id], drv_ch);
//TODO peter adjust the PWM initial signal according to DUTY value
*group = pwm_group_add(config->chan1, config->chan2);
PWM_LOGI("group period=%x chan1_duty=%x chan2_duty=%x\n",
config->period_cycle, config->chan1_duty_cycle, config->chan2_duty_cycle);
PWM_LOGI("group T21=%x T22=%x\n", pwm_config.duty_cycle, pwm_config.duty2_cycle);
return BK_OK;
}
bk_err_t bk_pwm_group_deinit(pwm_group_t group)
{
PWM_RETURN_ON_INVALID_GROUP_ID(group);
uint32_t unit_id = PWM_APP_GROUP_TO_UNIT_ID(group);
uint32_t drv_group = PWM_APP_GROUP_TO_DRIVER_GROUP(group);
if (!pwm_group_is_existed(group))
return BK_OK;
bk_pwm_group_stop(group);
pwm_chan_deinit_common(unit_id, s_pwm.groups[unit_id][drv_group].chan1);
pwm_chan_deinit_common(unit_id, s_pwm.groups[unit_id][drv_group].chan2);
pwm_group_delete(group);
return BK_OK;
}
bk_err_t bk_pwm_group_start(pwm_group_t group)
{
PWM_RETURN_ON_INVALID_GROUP_ID(group);
pwm_chan_t chan1;
pwm_chan_t chan2;
uint32_t unit_id = PWM_APP_GROUP_TO_UNIT_ID(group);
uint32_t drv_group = PWM_APP_GROUP_TO_DRIVER_GROUP(group);
if (!pwm_group_is_existed(group))
return BK_ERR_PWM_GROUP_NOT_EXIST;
chan1 = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(s_pwm.groups[unit_id][drv_group].chan1);
chan2 = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(s_pwm.groups[unit_id][drv_group].chan2);
if (pwm_hal_is_hardware_group(&s_pwm.hal[unit_id], chan1, chan2))
pwm_hal_start_hardware_group(&s_pwm.hal[unit_id], chan1, chan2);
pwm_hal_start_common(&s_pwm.hal[unit_id], chan1);
pwm_hal_start_common(&s_pwm.hal[unit_id], chan2);
return BK_OK;
}
bk_err_t bk_pwm_group_stop(pwm_group_t group)
{
pwm_chan_t chan1;
pwm_chan_t chan2;
if (!pwm_group_is_existed(group))
return BK_OK;
uint32_t unit_id = PWM_APP_GROUP_TO_UNIT_ID(group);
uint32_t drv_group = PWM_APP_GROUP_TO_DRIVER_GROUP(group);
chan1 = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(s_pwm.groups[unit_id][drv_group].chan1);
chan2 = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(s_pwm.groups[unit_id][drv_group].chan2);
if (pwm_hal_is_hardware_group(&s_pwm.hal[unit_id], chan1, chan2))
pwm_hal_stop_hardware_group(&s_pwm.hal[unit_id], chan1, chan2);
pwm_hal_stop_common(&s_pwm.hal[unit_id], chan1);
pwm_hal_stop_common(&s_pwm.hal[unit_id], chan2);
return BK_OK;
}
bk_err_t bk_pwm_group_set_config(pwm_group_t group, const pwm_group_config_t *config)
{
return BK_OK;
}
bk_err_t bk_pwm_group_set_init_signal(pwm_group_t group, const pwm_group_config_t *config)
{
return BK_OK;
}
bk_err_t bk_pwm_set_mode_timer(pwm_chan_t chan)
{
PWM_RETURN_ON_INVALID_CHAN(chan);
uint32_t drv_ch = PWM_APP_CHAN_ID_TO_DRV_CHAN_ID(chan);
uint32_t unit_id = PWM_CHAN_ID_TO_UNIT_ID(chan);
pwm_hal_set_mode_timer(&s_pwm.hal[unit_id], drv_ch);
return BK_OK;
}
static void pwm_isr(uint32_t unit_id)
{
pwm_hal_t *hal;
uint32_t int_status;
int chan;
/* for(uint32_t unit_id = 0; unit_id < SOC_PWM_UNIT_NUM; unit_id++) */ {
hal = &s_pwm.hal[unit_id];
int_status = pwm_hal_get_interrupt_status(hal);
pwm_hal_clear_interrupt_status(hal, int_status);
for (chan = 0; chan < SOC_PWM_CHAN_NUM_PER_UNIT; chan++) {
if (pwm_hal_is_interrupt_triggered(hal, chan, int_status)) {
if (s_pwm_isr[unit_id][chan])
s_pwm_isr[unit_id][chan](chan + (unit_id * SOC_PWM_CHAN_NUM_PER_UNIT));
}
}
}
}
static void pwm_isr_unit0(void)
{
pwm_isr(0);
}
static void pwm_isr_unit1(void)
{
pwm_isr(1);
}