// SPDX-License-Identifier: (GPL-2.0+ OR MIT) /* * Copyright (c) 2023 Rockchip Electronics Co., Ltd. * Author: Lingsong Ding */ #include #include #include #include #include #include #include #include #include #include #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 "); MODULE_DESCRIPTION("ROCKCHIP PWM TEST Driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("pwm:pwm_test");