rt-thread/bsp/gkipc/drivers/drv_pwm.c

513 lines
12 KiB
C

/*
* File : drv_pwm.c
* This file is part of GK710X BSP for RT-Thread distribution.
*
* Copyright (c) 2017 GOKE Microelectronics Co., Ltd.
* All rights reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Visit http://www.goke.com to get contact with Goke.
*
* Change Logs:
* Date Author Notes
*/
#include <rtdevice.h>
//#include "gpio.h"
#include "drv_pwm.h"
#include <rtdef.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <drivers/watchdog.h>
#include "gtypes.h"
#include "gd_pwm.h"
#include "platform.h"
#define GK_TEST_PWM
static struct gk_pwm_obj *pwm_drv = NULL;
static struct pwm_driver pwm_drv_table;
static rt_err_t pwm_get_status(rt_uint32_t *status)
{
rt_uint32_t channel_enable;
struct gk_pwm_obj *pwm_obj = (struct gk_pwm_obj *)pwm_drv;
if(status == NULL || pwm_obj->id >= PWM_MAX_CHANNEL)
{
rt_kprintf("PWM: > max channel \n");
return RT_ERROR;
}
if (GD_OK != GD_PWM_Get_Status(pwm_obj->id, &channel_enable))
{
rt_kprintf("get pwm status failed!\n");
return RT_ERROR;
}
*status = channel_enable;
return RT_EOK;
}
static rt_err_t pwm_enable(struct gk_pwm_obj *pwm_obj)
{
rt_uint32_t ret = RT_EOK;
if (pwm_obj->id >= PWM_MAX_CHANNEL)
{
rt_kprintf("PWM: > max channel \n");
return RT_ERROR;
}
ret = GD_PwmOnOff(pwm_obj->id, GTRUE);
if (ret != GD_OK)
{
rt_kprintf("enable pwm device failed!\n");
return RT_ERROR;
}
return RT_EOK;
}
static rt_err_t pwm_disable(struct gk_pwm_obj *pwm_obj)
{
rt_uint32_t ret = RT_EOK;
if (pwm_obj->id >= PWM_MAX_CHANNEL)
{
rt_kprintf("PWM: > max channel \n");
return RT_ERROR;
}
ret = GD_PwmOnOff(pwm_obj->id, GFALSE);
if (ret != GD_OK)
{
rt_kprintf("disable pwm device failed!\n");
return RT_ERROR;
}
return ret;
}
static int pwm_get_duty_cycle_ns(struct pwm_device *pwm)
{
struct gk_pwm_obj *pwm_obj = (struct gk_pwm_obj *)pwm_drv;
rt_uint32_t freq, duty;
if (pwm_obj->id >= PWM_MAX_CHANNEL)
{
rt_kprintf("PWM: > max channel \n");
return -1;
}
if (GD_PWM_Get_Param(pwm_obj->id, &freq, &duty))
{
rt_kprintf("GD_PWM_Get_Param error.\n");
return -1;
}
rt_kprintf("get duty: %lu%%, freq: %lu\n", duty, freq);
return 0;
}
static int pwm_set_duty_cycle_ns(struct pwm_device *pwm)
{
struct gk_pwm_obj *pwm_obj = (struct gk_pwm_obj *)pwm_drv;
rt_uint32_t range, duty, freq;
GERR ret = 0;
if(pwm_obj->id >= PWM_MAX_CHANNEL)
{
rt_kprintf("PWM: > max channel \n");
return -1;
}
freq = pwm->freq;
range = pwm->range;
duty = pwm->duty;
//param mode The PWM mode: 0 - Normal Speed Mode; 1 - Sync Speed Mode.
ret = GD_PWM_Set_Mode(pwm_obj->id, 0);
if(ret != GD_OK)
{
rt_kprintf("Set pwm mode 0 failed!\n");
return -1;
}
ret = GD_PWM_Set_Param(pwm_obj->id, freq, range, duty);
if (GD_PWM_NO_ERR != ret)
{
if (GD_PWM_ERR_NOT_SUPPORTED_CHANNEL == ret)
{
rt_kprintf("PWM Set_Param:set[%d %d %d]error *not support channel.\n",
(int)freq, (int)range, (int)duty);
}
else if(GD_PWM_ERR_NOT_SUPPORTED_FREQUENCY == ret)
{
rt_kprintf("PWM Set_Param:set[%d %d %d]error *not support Freq.\n",
(int)freq, (int)range, (int)duty);
}
else if(GD_PWM_ERR_NOT_SUPPORTED_RANGE == ret)
{
rt_kprintf("PWM Set_Param:set[%d %d %d]error *not support Range.\n",
(int)freq, (int)range, (int)duty);
}
else if(GD_PWM_ERR_WRONG_DUTY_CONFIGURATION == ret)
{
rt_kprintf("PWM Set_Param:set[%d %d %d]error *wrong duty.\n",
(int)freq, (int)range, (int)duty);
}
else if(GD_PWM_ERR_RANGE_EXCEED_LIMIT == ret)
{
rt_kprintf("PWM Set_Param:set[%d %d %d]error *exceed Range limit.\n",
(int)freq, (int)range, (int)duty);
}
return -1;
}
return 0;
}
static int pwm_get_vsync_mode_param(rt_uint16_t *speed)
{
struct gk_pwm_obj *pwm_obj = (struct gk_pwm_obj *)pwm_drv;
GERR ret = GD_OK;
if (pwm_obj->id >= PWM_MAX_CHANNEL)
{
rt_kprintf("PWM: > max channel \n");
return -1;
}
ret = GD_PWM_Get_Speed(pwm_obj->id, speed);
if(ret != GD_OK)
{
rt_kprintf("Get pwm speed failed!\n");
return -1;
}
rt_kprintf("get speed: %u\n", *speed);
return 0;
}
static int pwm_set_vsync_mode_param(struct pwm_param_vsync_mode *param)
{
struct gk_pwm_obj *pwm_obj = (struct gk_pwm_obj *)pwm_drv;
GERR ret = GD_OK;
if(pwm_obj->id >= PWM_MAX_CHANNEL)
{
rt_kprintf("PWM: > max channel \n");
return -1;
}
//param mode The PWM mode: 0 - Normal Speed Mode; 1 - Sync Speed Mode.
ret = GD_PWM_Set_Mode(pwm_obj->id, 1);
if(ret != GD_OK)
{
rt_kprintf("Set pwm mode 1 failed!\n");
return -1;
}
ret = GD_PWM_Set_Speed(pwm_obj->id, param->speed);
if(ret != GD_OK)
{
rt_kprintf("Set pwm speed failed!\n");
return -1;
}
ret = GD_PWM_Cycle(pwm_obj->id, param->highLevelCnt, param->lowLevelCnt);
if(ret != GD_OK)
{
rt_kprintf("Set pwm cycle failed!\n");
return -1;
}
return 0;
}
static int pwm_set_clock_divider(int ratio)
{
struct gk_pwm_obj *pwm_obj = (struct gk_pwm_obj *)pwm_drv;
rt_uint32_t range, duty, freq;
GERR ret = 0;
if (pwm_obj->id >= PWM_MAX_CHANNEL)
{
rt_kprintf("PWM: > max channel \n");
return -1;
}
if ((ratio != 1) && (ratio != 2) && (ratio != 3))
{
rt_kprintf("pwm_set_clock_divider:%d wrong param.\n", ratio);
return -1;
}
if (GD_OK != GD_PWM_Set_Clock_Divider(pwm_obj->id, ratio))
{
rt_kprintf("gadi_pwm_set_clock_divider:%d set error.\n", ratio);
return -1;
}
return 0;
}
static int pwm_set_active_channel(int id)
{
GERR ret = 0;
if (id >= PWM_MAX_CHANNEL)
{
rt_kprintf("PWM: > max channel \n");
return -1;
}
if (pwm_drv_table.pwm[id].gpio_id == 0xff)
{
rt_kprintf("channel not open \n");
return -1;
}
pwm_drv->id = id;
return 0;
}
static rt_err_t pwm_start(struct gk_pwm_obj *pwm_obj)
{
rt_uint32_t pwmStatus = 0;
rt_uint32_t ret = GD_OK;
ret = pwm_get_status(&pwmStatus);
if(ret != RT_EOK)
{
return ret;
}
if(pwmStatus)
{
ret = pwm_disable(pwm_obj);
if(ret != RT_EOK)
{
return ret;
}
}
return pwm_enable(pwm_obj);
}
static rt_err_t gk_pwm_open(rt_device_t dev, rt_uint16_t oflag)
{
rt_err_t ret = RT_EOK;
int index;
for (index=0; index<PWM_MAX_CHANNEL; index++)
{
if ((pwm_drv_table.pwm[index].id != 0xff) && (pwm_drv_table.pwm[index].gpio_id != 0xff))
{
rt_kprintf("pwm_drv_table.pwm[%d] id= %d, gpio_id = %d\n",
index,
pwm_drv_table.pwm[index].id,
pwm_drv_table.pwm[index].gpio_id);
ret = GD_PWM_Open(pwm_drv_table.pwm[index].id, pwm_drv_table.pwm[index].gpio_id);
if(ret != GD_OK)
{
rt_kprintf("open pwm device %d channel failed!\n", pwm_drv_table.pwm[index].id);
continue;
}
}
}
if (pwm_drv_table.pwm[0].gpio_id != 0xff)
{
pwm_drv->id = pwm_drv_table.pwm[0].id;
}
return ret;
}
static rt_err_t gk_pwm_close(rt_device_t dev)
{
struct gk_pwm_obj *pwm_obj = (struct gk_pwm_obj *)pwm_drv;
if(pwm_obj->id >= PWM_MAX_CHANNEL)
{
rt_kprintf("PWM: > max channel \n");
return RT_ERROR;
}
GD_PWM_Close(pwm_obj->id);
return RT_EOK;
}
static rt_err_t gk_pwm_ioctl(rt_device_t dev, int cmd, void *arg)
{
int ratio, ret = 0;
rt_uint16_t *speed = NULL;
struct pwm_device *pwm = NULL;
struct pwm_param_vsync_mode *vsync_param = NULL;
struct gk_pwm_obj *pwm_obj = (struct gk_pwm_obj *)pwm_drv;
switch (cmd)
{
case ENABLE_PWM:
pwm_start(pwm_obj);
break;
case DISABLE_PWM:
pwm_disable(pwm_obj);
break;
case SET_PWM_DUTY_CYCLE:
pwm = (struct pwm_device *)arg;
pwm_set_duty_cycle_ns(pwm);
break;
case GET_PWM_DUTY_CYCLE:
pwm = (struct pwm_device *)arg;
pwm_get_duty_cycle_ns(pwm);
break;
case SET_PWM_VSYNC_MODE:
vsync_param = (struct pwm_param_vsync_mode *)arg;
pwm_set_vsync_mode_param(vsync_param);
break;
case GET_PWM_VSYNC_MODE:
speed = (rt_uint16_t *)arg;
pwm_get_vsync_mode_param(speed);
break;
case SET_PWM_CLOCK_DIV:
ratio = *((int*)arg);
pwm_set_clock_divider(ratio);
break;
case SET_PWM_ACT_CHANNEL:
ratio = *((int*)arg);
pwm_set_active_channel(ratio);
break;
default:
break;
}
return ret;
}
static rt_err_t gk_pwm_init(rt_device_t dev)
{
rt_err_t ret = RT_EOK;
ret = (rt_err_t)GD_PWM_Init();
return ret;
}
int gk_pwm_probe(void *priv_data)
{
rt_device_t pwm_dev;
int index;
for (index=0; index < PWM_MAX_CHANNEL; index++)
{
pwm_drv_table.pwm[index].id = 0xff;
pwm_drv_table.pwm[index].gpio_id = 0xff;
}
for (index=0; index < PWM_MAX_CHANNEL; index++)
{
if (((struct pwm_driver *)priv_data)->pwm[index].gpio_id > 0)
{
pwm_drv_table.pwm[index].id = ((struct pwm_driver *)priv_data)->pwm[index].id;
pwm_drv_table.pwm[index].gpio_id = ((struct pwm_driver *)priv_data)->pwm[index].gpio_id;
}
}
pwm_drv = rt_malloc(sizeof(struct gk_pwm_obj));
if (pwm_drv == RT_NULL)
{
rt_kprintf("ERROR: %s pwm_drv malloc failed\n", __func__);
}
rt_memset(pwm_drv, 0, sizeof(struct gk_pwm_obj));
pwm_dev = rt_malloc(sizeof(struct rt_device));
if (pwm_dev == RT_NULL)
{
rt_kprintf("ERROR: %s rt_device malloc failed\n", __func__);
}
rt_memset(pwm_dev, 0, sizeof(struct rt_device));
pwm_dev->user_data = &pwm_drv_table;
pwm_dev->open = gk_pwm_open;
pwm_dev->close = gk_pwm_close;
pwm_dev->control = gk_pwm_ioctl;
pwm_dev->init = gk_pwm_init;
pwm_dev->type = RT_Device_Class_Miscellaneous;
rt_device_register(pwm_dev, "pwm", RT_DEVICE_FLAG_RDWR);
return 0;
}
int gk_pwm_exit(void *priv_data) { return 0; }
struct gk_platform_driver pwm_driver_ops = {
.name = "pwm", .probe = gk_pwm_probe, .remove = gk_pwm_exit,
};
void rt_hw_pwm_init(void)
{
gk_platform_driver_init(&pwm_driver_ops);
}
#ifdef GK_TEST_PWM
int gk_pwm_test(void)
{
rt_device_t pwm_dev;
struct pwm_device pwm;
pwm.duty = 50;
pwm.range = 100;
pwm.freq = 1000;
pwm_dev = rt_device_find("pwm");
if (!pwm_dev)
{
rt_kprintf("cann't find the pwm dev\n");
}
pwm_dev->init(pwm_dev);
pwm_dev->open(pwm_dev, 0);
pwm_dev->control(pwm_dev, SET_PWM_DUTY_CYCLE, &pwm);
pwm_dev->control(pwm_dev, ENABLE_PWM, NULL);
return 0;
}
#endif
#ifdef RT_USING_FINSH
#include <finsh.h>
#ifdef GK_TEST_PWM
FINSH_FUNCTION_EXPORT(gk_pwm_test, gk_pwm_test);
#endif
#endif