857 lines
25 KiB
C

// 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_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;
}
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;
}
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");