pwm: rockchip: add pwm demos for test

Signed-off-by: Damon Ding <damon.ding@rock-chips.com>
Change-Id: I6a9061c255748f32e10511346ec7095c883580db
This commit is contained in:
Damon Ding
2023-07-25 16:43:58 +08:00
committed by Tao Huang
parent 5bbb11788c
commit 304cff0f62
3 changed files with 866 additions and 0 deletions

View File

@@ -502,6 +502,15 @@ config PWM_ROCKCHIP_ONESHOT
help
Support Rockchip pwm oneshot mode for specified number of cycles.
config PWM_ROCKCHIP_TEST
tristate "Rockchip PWM test demo support"
depends on PWM_ROCKCHIP
help
Rockchip PWM test demo driver.
Say yes here if you want to get some demos for how to use PWM,
whether it is about the generic framework or the functions
supported by Rockchip PWM.
config PWM_SAMSUNG
tristate "Samsung PWM support"
depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST

View File

@@ -45,6 +45,7 @@ obj-$(CONFIG_PWM_RASPBERRYPI_POE) += pwm-raspberrypi-poe.o
obj-$(CONFIG_PWM_RCAR) += pwm-rcar.o
obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
obj-$(CONFIG_PWM_ROCKCHIP_TEST) += pwm-rockchip-test.o
obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o

View File

@@ -0,0 +1,856 @@
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2023 Rockchip Electronics Co., Ltd.
* Author: Lingsong Ding <damon.ding@rock-chips.com>
*/
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/pwm-rockchip.h>
#include <linux/syscalls.h>
#include <linux/delay.h>
#include <linux/types.h>
#define PWM_CONTROLLER_NUM_MAX 4
#define PWM_CHANNEL_NUM_MAX 8
#define PWM_WAVE_8BIT_TEST 1
/* 400k pwm_dclk src */
#ifdef PWM_WAVE_8BIT_TEST
#define PWM_TABLE_MAX 256
#define PWM_WIDTH_MODE PWM_WAVE_TABLE_8BITS_WIDTH
/* in nanoseconds */
#define PWM_WAVE_STEP 2500
#define PWM_WAVE_RPT 30
#else
#define PWM_TABLE_MAX 100
#define PWM_WIDTH_MODE PWM_WAVE_TABLE_16BITS_WIDTH
/* in nanoseconds */
#define PWM_WAVE_STEP 10000
#define PWM_WAVE_RPT 10
#endif
struct pwm_test_data {
struct pwm_device *pwm_dev;
struct device *dev;
};
static struct pwm_test_data *g_pwm_test_data[PWM_CONTROLLER_NUM_MAX][PWM_CHANNEL_NUM_MAX] = {};
struct pwm_device *global_pdev;
static int global_channel_id = -1;
static u64 table[256] = {};
enum pwm_cmd_type {
UNSUPPORTED_TYPE = -1,
PWM_SET_ENABLE = 0,
PWM_SET_CONTINOUS,
PWM_SET_ONESHOT,
PWM_SET_CAPTURE,
PWM_SET_COUNTER,
PWM_SET_FREQ_METER,
PWM_SET_BIPHASIC,
PWM_SET_WAVE,
PWM_GLOBAL_CTRL,
PWM_TEST_HELP,
};
static enum pwm_cmd_type pwm_rockchip_test_parse_cmd(char *cmd_type)
{
if (!strcmp(cmd_type, "enable"))
return PWM_SET_ENABLE;
else if (!strcmp(cmd_type, "continuous"))
return PWM_SET_CONTINOUS;
#ifdef CONFIG_PWM_ROCKCHIP_ONESHOT
else if (!strcmp(cmd_type, "oneshot"))
return PWM_SET_ONESHOT;
#endif
else if (!strcmp(cmd_type, "capture"))
return PWM_SET_CAPTURE;
else if (!strcmp(cmd_type, "counter"))
return PWM_SET_COUNTER;
else if (!strcmp(cmd_type, "frequency"))
return PWM_SET_FREQ_METER;
else if (!strcmp(cmd_type, "biphasic"))
return PWM_SET_BIPHASIC;
else if (!strcmp(cmd_type, "wave"))
return PWM_SET_WAVE;
else if (!strcmp(cmd_type, "global"))
return PWM_GLOBAL_CTRL;
else if (!strcmp(cmd_type, "help"))
return PWM_TEST_HELP;
else
return UNSUPPORTED_TYPE;
}
static int get_biphasic_mode(char *mode)
{
if (!strcmp(mode, "mode0"))
return PWM_BIPHASIC_COUNTER_MODE0;
else if (!strcmp(mode, "mode1"))
return PWM_BIPHASIC_COUNTER_MODE1;
else if (!strcmp(mode, "mode2"))
return PWM_BIPHASIC_COUNTER_MODE2;
else if (!strcmp(mode, "mode3"))
return PWM_BIPHASIC_COUNTER_MODE3;
else if (!strcmp(mode, "mode4"))
return PWM_BIPHASIC_COUNTER_MODE4;
else
return -EINVAL;
}
static void pwm_rockchip_test_help_info(void)
{
pr_info("------------------------------------------------------------------------------------\n");
pr_info(" HELP INFO\n");
pr_info("------------------------------------------------------------------------------------\n");
pr_info("pwm test commands format:\n");
pr_info("echo cmd_type controller_id channel_id para0 para1 ... > /dev/pwm_rockchip_misc_test\n");
pr_info("echo global cmd_type controller_id channel_id > /dev/pwm_rockchip_misc_test\n");
pr_info("\n");
pr_info("continuous mode demo:\n");
pr_info("echo continuous 0 0 10000 5000 normal > /dev/pwm_rockchip_misc_test\n");
pr_info("echo enable 0 0 true > /dev/pwm_rockchip_misc_test\n");
pr_info("\n");
pr_info("oneshot mode demo:\n");
pr_info("echo oneshot 0 1 10000 5000 0 normal 10 0 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo enable 0 1 true > /dev/pwm_rockchip_misc_test\n");
pr_info("\n");
pr_info("capture mode demo:\n");
pr_info("echo capture 1 0 1000 > /dev/pwm_rockchip_misc_test\n");
pr_info("\n");
pr_info("counter mode demo:\n");
pr_info("echo counter 1 0 io 1000 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo counter 1 1 cru 1000 > /dev/pwm_rockchip_misc_test\n");
pr_info("\n");
pr_info("frequency meter mode demo:\n");
pr_info("echo frequency 1 2 io 1000 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo frequency 1 3 cru 1000 > /dev/pwm_rockchip_misc_test\n");
pr_info("\n");
pr_info("biphasic counter mode demo(for mode0 as frequency meter):\n");
pr_info("echo biphasic 1 4 freq 1000 > /dev/pwm_rockchip_misc_test\n");
pr_info("biphasic counter mode demo:\n");
pr_info("echo biphasic 1 5 mode0 1000 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo biphasic 1 5 mode1 1000 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo biphasic 1 5 mode2 1000 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo biphasic 1 5 mode3 1000 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo biphasic 1 5 mode4 1000 > /dev/pwm_rockchip_misc_test\n");
pr_info("\n");
pr_info("global control demo:\n");
pr_info("echo global join 0 0 1 2 3 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo global grant 0 0 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo oneshot 0 0 10000 1000 2000 normal 10 0 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo oneshot 0 1 10000 1000 4000 normal 10 0 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo oneshot 0 2 10000 1000 6000 normal 10 0 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo oneshot 0 3 10000 1000 8000 normal 10 0 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo global update 0 0 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo global enable 0 0 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo global reclaim 0 0 > /dev/pwm_rockchip_misc_test\n");
pr_info("echo global exit 0 0 1 2 3 > /dev/pwm_rockchip_misc_test\n");
pr_info("\n");
pr_info("wave generator demo:\n");
pr_info("echo wave 0 1 true > /dev/pwm_rockchip_misc_test\n");
pr_info("echo continuous 0 1 640000 320000 normal > /dev/pwm_rockchip_misc_test\n");
pr_info("echo continuous 0 1 1000000 500000 normal > /dev/pwm_rockchip_misc_test\n");
pr_info("echo enable 0 1 true > /dev/pwm_rockchip_misc_test\n");
pr_info("------------------------------------------------------------------------------------\n");
}
static struct pwm_device *pwm_rockchip_test_get_pwm_dev(char *controller, char *channel,
int *controller_id, int *channel_id)
{
struct pwm_device *pdev = NULL;
int ret = 0;
if (!controller || !channel || !controller_id || !channel_id)
return NULL;
ret = kstrtoint(controller, 10, controller_id);
if (ret || *controller_id >= PWM_CONTROLLER_NUM_MAX) {
pr_err("pwm controller id should be 0 to %d\n", PWM_CONTROLLER_NUM_MAX - 1);
return NULL;
}
ret = kstrtoint(channel, 10, channel_id);
if (ret || *channel_id >= PWM_CHANNEL_NUM_MAX) {
pr_err("pwm channel id should be 0 to %d\n", PWM_CHANNEL_NUM_MAX - 1);
return NULL;
}
if (!g_pwm_test_data[*controller_id][*channel_id]) {
pr_err("pwm%d_%d should be bound first\n", *controller_id, *channel_id);
return NULL;
}
if (g_pwm_test_data[*controller_id][*channel_id]->pwm_dev)
pdev = g_pwm_test_data[*controller_id][*channel_id]->pwm_dev;
return pdev;
}
static ssize_t pwm_rockchip_test_write(struct file *file, const char __user *buf,
size_t n, loff_t *offset)
{
struct pwm_device *pdev;
struct pwm_state state;
struct pwm_capture cap_res;
struct rockchip_pwm_wave_table duty_table;
struct rockchip_pwm_wave_config wave_config;
struct rockchip_pwm_biphasic_config biphasic_config;
enum rockchip_pwm_freq_meter_input_sel freq_input_sel;
enum rockchip_pwm_counter_input_sel counter_input_sel;
enum rockchip_pwm_biphasic_mode biphasic_mode;
enum pwm_cmd_type cmd_type;
enum pwm_polarity polarity;
unsigned long timeout_ms;
unsigned long freq_hz;
unsigned long counter_res;
unsigned long biphasic_res;
int controller_id, channel_id;
int period, duty;
#ifdef CONFIG_PWM_ROCKCHIP_ONESHOT
int duty_offset, rpt_first, rpt_second;
#endif
int argc = 0;
int i = 0;
int ret = 0;
char tmp[256] = {};
char *argv[16] = {};
char *cmd, *data;
bool enable;
memset(tmp, 0, sizeof(tmp));
if (copy_from_user(tmp, buf, n))
return -EFAULT;
cmd = tmp;
data = tmp;
while (data < (tmp + n)) {
data = strstr(data, " ");
if (!data)
break;
*data = 0;
argv[argc] = ++data;
argc++;
if (argc >= 16)
break;
}
tmp[n - 1] = 0;
if (!cmd) {
ret = -EINVAL;
goto exit;
}
cmd_type = pwm_rockchip_test_parse_cmd(cmd);
switch (cmd_type) {
case PWM_SET_ENABLE:
pdev = pwm_rockchip_test_get_pwm_dev(argv[0], argv[1],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
if (argv[2]) {
if (!strcmp(argv[2], "true")) {
enable = true;
} else if (!strcmp(argv[2], "false")) {
enable = false;
} else {
pr_err("enable state should be true or false\n");
ret = -EINVAL;
goto exit;
}
} else {
pr_err("failed to parse enable for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
goto exit;
}
pwm_get_state(pdev, &state);
state.enabled = enable;
ret = pwm_apply_state(pdev, &state);
if (ret) {
pr_err("failed to %s pwm%d_%d\n",
enable ? "enable" : "disable", controller_id, channel_id);
ret = -EINVAL;
goto exit;
}
pr_info("%s pwm%d_%d\n", enable ? "enable" : "disable", controller_id, channel_id);
break;
case PWM_SET_CONTINOUS:
pdev = pwm_rockchip_test_get_pwm_dev(argv[0], argv[1],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
ret = kstrtoint(argv[2], 10, &period);
if (ret) {
pr_err("failed to parse period for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
ret = kstrtoint(argv[3], 10, &duty);
if (ret) {
pr_err("failed to parse duty for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
if (argv[4]) {
if (!strcmp(argv[4], "inversed")) {
polarity = PWM_POLARITY_INVERSED;
} else if (!strcmp(argv[4], "normal")) {
polarity = PWM_POLARITY_NORMAL;
} else {
pr_err("polarity should be inversed or normal\n");
ret = -EINVAL;
goto exit;
}
} else {
pr_err("failed to parse polarity for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
pwm_get_state(pdev, &state);
state.period = period;
state.duty_cycle = duty;
state.polarity = polarity;
ret = pwm_apply_state(pdev, &state);
if (ret) {
pr_err("failed to set %s mode for pwm%d_%d\n",
cmd, controller_id, channel_id);
return -EINVAL;
}
pr_info("set %s mode for pwm%d_%d: period = %dns, duty = %dns, polarity = %s\n",
cmd, controller_id, channel_id,
period, duty, polarity ? "inversed" : "normal");
break;
#ifdef CONFIG_PWM_ROCKCHIP_ONESHOT
case PWM_SET_ONESHOT:
pdev = pwm_rockchip_test_get_pwm_dev(argv[0], argv[1],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
ret = kstrtoint(argv[2], 10, &period);
if (ret) {
pr_err("failed to parse period for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
ret = kstrtoint(argv[3], 10, &duty);
if (ret) {
pr_err("failed to parse duty for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
ret = kstrtoint(argv[4], 10, &duty_offset);
if (ret) {
pr_err("failed to parse offset for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
if (argv[5]) {
if (!strcmp(argv[5], "inversed")) {
polarity = PWM_POLARITY_INVERSED;
} else if (!strcmp(argv[5], "normal")) {
polarity = PWM_POLARITY_NORMAL;
} else {
pr_err("polarity should be inversed or normal\n");
ret = -EINVAL;
goto exit;
}
} else {
pr_err("failed to parse polarity for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
ret = kstrtoint(argv[6], 10, &rpt_first);
if (ret) {
pr_err("failed to parse rpt_first for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
ret = kstrtoint(argv[7], 10, &rpt_second);
if (ret) {
pr_err("failed to parse rpt_second for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
pwm_get_state(pdev, &state);
state.period = period;
state.duty_cycle = duty;
state.duty_offset = duty_offset;
state.polarity = polarity;
state.oneshot_count = rpt_first;
state.oneshot_repeat = rpt_second;
ret = pwm_apply_state(pdev, &state);
if (ret) {
pr_err("failed to set %s mode for pwm%d_%d\n",
cmd, controller_id, channel_id);
return -EINVAL;
}
pr_info("set %s mode for pwm%d_%d: period = %dns, duty = %dns, offset = %dns, polarity = %s rpt = (%d, %d)\n",
cmd, controller_id, channel_id, period, duty, duty_offset, polarity ? "inversed" : "normal",
rpt_first, rpt_second);
break;
#endif
case PWM_SET_CAPTURE:
pdev = pwm_rockchip_test_get_pwm_dev(argv[0], argv[1],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
ret = kstrtoul(argv[2], 10, &timeout_ms);
if (ret) {
pr_err("failed to parse timeout_ms for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
ret = pwm_capture(pdev, &cap_res, timeout_ms);
if (ret) {
pr_err("failed to set %s mode for pwm%d_%d\n",
cmd, controller_id, channel_id);
return -EINVAL;
}
pr_info("set %s mode for pwm%d_%d: timeout_ms = %ld, result: period = %dns, duty = %dns\n",
cmd, controller_id, channel_id, timeout_ms, cap_res.period, cap_res.duty_cycle);
break;
case PWM_SET_COUNTER:
pdev = pwm_rockchip_test_get_pwm_dev(argv[0], argv[1],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
if (argv[2]) {
if (!strcmp(argv[2], "cru")) {
counter_input_sel = PWM_COUNTER_INPUT_FROM_CRU;
} else if (!strcmp(argv[2], "io")) {
counter_input_sel = PWM_COUNTER_INPUT_FROM_IO;
} else {
pr_err("input_sel should be cru or io\n");
ret = -EINVAL;
goto exit;
}
} else {
pr_err("failed to parse input_sel for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
ret = kstrtoul(argv[3], 10, &timeout_ms);
if (ret) {
pr_err("failed to parse timeout_ms for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
ret = rockchip_pwm_set_counter(pdev, counter_input_sel, true);
if (ret) {
pr_err("failed to enable %s mode for pwm%d_%d\n",
cmd, controller_id, channel_id);
return -EINVAL;
}
msleep(timeout_ms);
ret = rockchip_pwm_set_counter(pdev, 0, false);
if (ret) {
pr_err("failed to disable %s mode for pwm%d_%d\n",
cmd, controller_id, channel_id);
return -EINVAL;
}
ret = rockchip_pwm_get_counter_result(pdev, &counter_res, true);
if (ret) {
pr_err("failed to get %s mode result for pwm%d_%d\n",
cmd, controller_id, channel_id);
return -EINVAL;
}
pr_info("set %s mode for pwm%d_%d: timeout_ms = %ld, result: counter_res = %ld\n",
cmd, controller_id, channel_id, timeout_ms, counter_res);
break;
case PWM_SET_FREQ_METER:
pdev = pwm_rockchip_test_get_pwm_dev(argv[0], argv[1],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
if (argv[2]) {
if (!strcmp(argv[2], "cru")) {
freq_input_sel = PWM_FREQ_METER_INPUT_FROM_CRU;
} else if (!strcmp(argv[2], "io")) {
freq_input_sel = PWM_FREQ_METER_INPUT_FROM_IO;
} else {
pr_err("input_sel should be cru or io\n");
ret = -EINVAL;
goto exit;
}
} else {
pr_err("failed to parse input_sel for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
ret = kstrtoul(argv[3], 10, &timeout_ms);
if (ret) {
pr_err("failed to parse timeout_ms for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
ret = rockchip_pwm_set_freq_meter(pdev, timeout_ms, freq_input_sel, &freq_hz);
if (ret) {
pr_err("failed to enable %s mode for pwm%d_%d\n",
cmd, controller_id, channel_id);
return -EINVAL;
}
pr_info("set %s mode for pwm%d_%d: timeout_ms = %ld, result: frequency = %ldHz\n",
cmd, controller_id, channel_id, timeout_ms, freq_hz);
break;
case PWM_SET_BIPHASIC:
pdev = pwm_rockchip_test_get_pwm_dev(argv[0], argv[1],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
if (argv[2]) {
if (!strcmp(argv[2], "freq")) {
biphasic_mode = PWM_BIPHASIC_COUNTER_MODE0_FREQ;
} else {
ret = get_biphasic_mode(argv[2]);
if (ret < 0) {
pr_err("unsupported biphasic counter mode\n");
ret = -EINVAL;
goto exit;
}
biphasic_mode = ret;
}
} else {
pr_err("failed to parse biphasic_mode for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
ret = kstrtoul(argv[3], 10, &timeout_ms);
if (ret) {
pr_err("failed to parse timeout_ms for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
ret = -EINVAL;
goto exit;
}
biphasic_config.enable = true;
biphasic_config.is_continuous = false;
biphasic_config.mode = biphasic_mode;
biphasic_config.delay_ms = timeout_ms;
if (biphasic_mode != PWM_BIPHASIC_COUNTER_MODE0_FREQ && timeout_ms == 0)
biphasic_config.is_continuous = true;
ret = rockchip_pwm_set_biphasic(pdev, &biphasic_config, &biphasic_res);
if (ret) {
pr_err("failed to enable %s mode for pwm%d_%d\n",
cmd, controller_id, channel_id);
return -EINVAL;
}
pr_info("set %s mode for pwm%d_%d: timeout_ms = %ld, result%s = %ld%s\n",
cmd, controller_id, channel_id, timeout_ms,
biphasic_mode == PWM_BIPHASIC_COUNTER_MODE0_FREQ ? "(frequency)" : "",
biphasic_res,
biphasic_mode == PWM_BIPHASIC_COUNTER_MODE0_FREQ ? "Hz" : "");
break;
case PWM_SET_WAVE:
pdev = pwm_rockchip_test_get_pwm_dev(argv[0], argv[1],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
if (argv[2]) {
if (!strcmp(argv[2], "true")) {
enable = true;
} else if (!strcmp(argv[2], "false")) {
enable = false;
} else {
pr_err("enable state should be true or false\n");
ret = -EINVAL;
goto exit;
}
} else {
pr_err("failed to parse enable for pwm%d_%d in %s mode\n",
controller_id, channel_id, cmd);
goto exit;
}
for (i = 0; i < PWM_TABLE_MAX; i++)
table[i] = i * PWM_WAVE_STEP;
duty_table.table = table;
duty_table.offset = (channel_id % 3) * PWM_TABLE_MAX;
duty_table.len = PWM_TABLE_MAX;
wave_config.rpt = PWM_WAVE_RPT;
/* select the 400k clk src */
wave_config.clk_rate = 400000;
wave_config.duty_table = &duty_table;
wave_config.period_table = NULL;
wave_config.enable = enable;
wave_config.duty_en = true;
wave_config.period_en = false;
wave_config.width_mode = PWM_WIDTH_MODE;
wave_config.update_mode = PWM_WAVE_INCREASING_THEN_DECREASING;
wave_config.duty_max = (channel_id % 3 + 1) * PWM_TABLE_MAX - 1;
wave_config.duty_min = (channel_id % 3) * PWM_TABLE_MAX;
wave_config.period_max = 0;
wave_config.period_min = 0;
wave_config.offset = 0;
wave_config.middle = PWM_TABLE_MAX / 2;
wave_config.max_hold = 3;
wave_config.min_hold = 0;
wave_config.middle_hold = 2;
ret = rockchip_pwm_set_wave(pdev, &wave_config);
if (ret) {
pr_err("failed to %s pwm%d_%d\n",
enable ? "enable" : "disable", controller_id, channel_id);
ret = -EINVAL;
goto exit;
}
pr_info("%s %s mode for pwm%d_%d: table_len = %d, table_step = %d\n",
argv[2], cmd, controller_id, channel_id, PWM_TABLE_MAX, PWM_WAVE_STEP);
break;
case PWM_GLOBAL_CTRL:
if (!argv[0]) {
ret = -EINVAL;
goto exit;
}
if (!strcmp(argv[0], "join")) {
i = 2;
while (argv[i]) {
pdev = pwm_rockchip_test_get_pwm_dev(argv[1], argv[i],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
rockchip_pwm_global_ctrl(pdev, PWM_GLOBAL_CTRL_JOIN);
pr_info("pwm%d_%d join global control group\n",
controller_id, channel_id);
i++;
}
} else if (!strcmp(argv[0], "exit")) {
i = 2;
while (argv[i]) {
pdev = pwm_rockchip_test_get_pwm_dev(argv[1], argv[i],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
rockchip_pwm_global_ctrl(pdev, PWM_GLOBAL_CTRL_EXIT);
pr_info("pwm%d_%d exit global control group\n",
controller_id, channel_id);
i++;
}
} else if (!strcmp(argv[0], "grant")) {
pdev = pwm_rockchip_test_get_pwm_dev(argv[1], argv[2],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
ret = rockchip_pwm_global_ctrl(pdev, PWM_GLOBAL_CTRL_GRANT);
if (ret) {
ret = -EINVAL;
goto exit;
}
global_channel_id = channel_id;
global_pdev = pdev;
pr_info("pwm%d_%d is granted global control\n",
controller_id, global_channel_id);
} else if (!strcmp(argv[0], "reclaim")) {
pdev = pwm_rockchip_test_get_pwm_dev(argv[1], argv[2],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
rockchip_pwm_global_ctrl(pdev, PWM_GLOBAL_CTRL_RECLAIM);
pr_info("pwm%d_%d is reclaimed global control\n",
controller_id, global_channel_id);
global_pdev = NULL;
global_channel_id = -1;
} else if (!strcmp(argv[0], "update")) {
pdev = pwm_rockchip_test_get_pwm_dev(argv[1], argv[2],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
rockchip_pwm_global_ctrl(pdev, PWM_GLOBAL_CTRL_UPDATE);
pr_info("pwm%d_%d executes global update\n",
controller_id, global_channel_id);
} else if (!strcmp(argv[0], "enable")) {
pdev = pwm_rockchip_test_get_pwm_dev(argv[1], argv[2],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
rockchip_pwm_global_ctrl(pdev, PWM_GLOBAL_CTRL_ENABLE);
pr_info("pwm%d_%d executes global enable\n",
controller_id, global_channel_id);
} else if (!strcmp(argv[0], "disable")) {
pdev = pwm_rockchip_test_get_pwm_dev(argv[1], argv[2],
&controller_id, &channel_id);
if (!pdev) {
pr_err("failed to get pwm device\n");
ret = -EINVAL;
goto exit;
}
rockchip_pwm_global_ctrl(pdev, PWM_GLOBAL_CTRL_DISABLE);
pr_info("pwm%d_%d executes global disable\n",
controller_id, global_channel_id);
} else {
pr_err("unsupported global control command\n");
ret = -EINVAL;
goto exit;
}
break;
case PWM_TEST_HELP:
default:
pwm_rockchip_test_help_info();
break;
}
return n;
exit:
pwm_rockchip_test_help_info();
return ret;
}
static const struct file_operations pwm_rockchip_test_fops = {
.write = pwm_rockchip_test_write,
};
static struct miscdevice pwm_rockchip_test_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "pwm_rockchip_misc_test",
.fops = &pwm_rockchip_test_fops,
};
static int pwm_rockchip_test_probe(struct platform_device *pdev)
{
struct pwm_test_data *test_data = NULL;
struct pwm_device *pwm_dev = NULL;
char pwm_name[64] = {};
int i, j;
for (i = 0; i < PWM_CONTROLLER_NUM_MAX; i++) {
for (j = 0; j < PWM_CHANNEL_NUM_MAX; j++) {
sprintf(pwm_name, "%s%d_%d", "pwm", i, j);
pwm_dev = devm_pwm_get(&pdev->dev, pwm_name);
if (IS_ERR(pwm_dev)) {
dev_dbg(&pdev->dev, "failed to bind %s\n", pwm_name);
continue;
}
test_data = devm_kzalloc(&pdev->dev, sizeof(*test_data), GFP_KERNEL);
if (!test_data)
return -ENOMEM;
test_data->pwm_dev = pwm_dev;
g_pwm_test_data[i][j] = test_data;
g_pwm_test_data[i][j]->pwm_dev = pwm_dev;
dev_warn(&pdev->dev, "%s bind %s\n",
pwm_name,
g_pwm_test_data[i][j]->pwm_dev->chip->dev->of_node->full_name);
}
}
misc_register(&pwm_rockchip_test_misc);
return 0;
}
static int pwm_rockchip_test_remove(struct platform_device *pdev)
{
misc_deregister(&pwm_rockchip_test_misc);
return 0;
}
static const struct of_device_id pwm_rockchip_test_of_match[] = {
{ .compatible = "pwm-rockchip-test" },
{ }
};
MODULE_DEVICE_TABLE(of, pwm_rockchip_test_of_match);
static struct platform_driver pwm_rockchip_test_driver = {
.driver = {
.name = "pwm-rockchip-test",
.of_match_table = of_match_ptr(pwm_rockchip_test_of_match),
},
.probe = pwm_rockchip_test_probe,
.remove = pwm_rockchip_test_remove,
};
module_platform_driver(pwm_rockchip_test_driver);
MODULE_AUTHOR("Lingsong Ding <damon.ding@rock-chips.com>");
MODULE_DESCRIPTION("ROCKCHIP PWM TEST Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("pwm:pwm_test");