|  | /* Copyright 2013 The ChromiumOS Authors | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "common.h" | 
|  | #include "console.h" | 
|  | #include "gpio.h" | 
|  | #include "hooks.h" | 
|  | #include "host_command.h" | 
|  | #include "pwm.h" | 
|  | #include "util.h" | 
|  |  | 
|  | #ifdef CONFIG_PWM | 
|  |  | 
|  | /* | 
|  | * Get target channel based on type / index host command parameters. | 
|  | * Returns 0 if a valid channel is selected, -1 on error. | 
|  | */ | 
|  | static int get_target_channel(enum pwm_channel *channel, int type, int index) | 
|  | { | 
|  | switch (type) { | 
|  | case EC_PWM_TYPE_GENERIC: | 
|  | *channel = index; | 
|  | break; | 
|  | #ifdef CONFIG_PWM_KBLIGHT | 
|  | case EC_PWM_TYPE_KB_LIGHT: | 
|  | *channel = PWM_CH_KBLIGHT; | 
|  | break; | 
|  | #endif | 
|  | #ifdef CONFIG_PWM_DISPLIGHT | 
|  | case EC_PWM_TYPE_DISPLAY_LIGHT: | 
|  | *channel = PWM_CH_DISPLIGHT; | 
|  | break; | 
|  | #endif | 
|  | default: | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return *channel >= PWM_CH_COUNT; | 
|  | } | 
|  |  | 
|  | __attribute__((weak)) void pwm_set_raw_duty(enum pwm_channel ch, uint16_t duty) | 
|  | { | 
|  | int percent; | 
|  |  | 
|  | /* Convert 16 bit duty to percent on [0, 100] */ | 
|  | percent = PWM_RAW_TO_PERCENT(duty); | 
|  | pwm_set_duty(ch, percent); | 
|  | } | 
|  |  | 
|  | __attribute__((weak)) uint16_t pwm_get_raw_duty(enum pwm_channel ch) | 
|  | { | 
|  | return PWM_PERCENT_TO_RAW(pwm_get_duty(ch)); | 
|  | } | 
|  |  | 
|  | static enum ec_status | 
|  | host_command_pwm_set_duty(struct host_cmd_handler_args *args) | 
|  | { | 
|  | const struct ec_params_pwm_set_duty *p = args->params; | 
|  | enum pwm_channel channel; | 
|  |  | 
|  | if (get_target_channel(&channel, p->pwm_type, p->index)) | 
|  | return EC_RES_INVALID_PARAM; | 
|  | pwm_set_raw_duty(channel, p->duty); | 
|  | pwm_enable(channel, p->duty > 0); | 
|  |  | 
|  | return EC_RES_SUCCESS; | 
|  | } | 
|  | DECLARE_HOST_COMMAND(EC_CMD_PWM_SET_DUTY, host_command_pwm_set_duty, | 
|  | EC_VER_MASK(0)); | 
|  |  | 
|  | static enum ec_status | 
|  | host_command_pwm_get_duty(struct host_cmd_handler_args *args) | 
|  | { | 
|  | const struct ec_params_pwm_get_duty *p = args->params; | 
|  | struct ec_response_pwm_get_duty *r = args->response; | 
|  |  | 
|  | enum pwm_channel channel; | 
|  |  | 
|  | if (get_target_channel(&channel, p->pwm_type, p->index)) | 
|  | return EC_RES_INVALID_PARAM; | 
|  |  | 
|  | r->duty = pwm_get_raw_duty(channel); | 
|  |  | 
|  | args->response_size = sizeof(*r); | 
|  |  | 
|  | return EC_RES_SUCCESS; | 
|  | } | 
|  | DECLARE_HOST_COMMAND(EC_CMD_PWM_GET_DUTY, host_command_pwm_get_duty, | 
|  | EC_VER_MASK(0)); | 
|  |  | 
|  | /** | 
|  | * Print status of a PWM channel. | 
|  | * | 
|  | * @param ch		Channel to print. | 
|  | */ | 
|  | static void print_channel(enum pwm_channel ch, int max_duty) | 
|  | { | 
|  | if (pwm_get_enabled(ch)) | 
|  | if (max_duty == 100) | 
|  | ccprintf("  %d: %d%%\n", ch, pwm_get_duty(ch)); | 
|  | else | 
|  | ccprintf("  %d: %d\n", ch, pwm_get_raw_duty(ch)); | 
|  | else | 
|  | ccprintf("  %d: disabled\n", ch); | 
|  | } | 
|  |  | 
|  | static int cc_pwm_duty(int argc, const char **argv) | 
|  | { | 
|  | int value = 0; | 
|  | int max_duty = 100; | 
|  | int ch; | 
|  | char *e; | 
|  | const char *raw; | 
|  |  | 
|  | if (argc < 2) { | 
|  | ccprintf("PWM channels:\n"); | 
|  | for (ch = 0; ch < PWM_CH_COUNT; ch++) | 
|  | print_channel(ch, max_duty); | 
|  | return EC_SUCCESS; | 
|  | } | 
|  |  | 
|  | ch = strtoi(argv[1], &e, 0); | 
|  | if (*e || ch < 0 || ch >= PWM_CH_COUNT) | 
|  | return EC_ERROR_PARAM1; | 
|  |  | 
|  | if (argc > 2) { | 
|  | raw = argv[2]; | 
|  | if (!strcasecmp(raw, "raw")) { | 
|  | /* use raw duty */ | 
|  | value = strtoi(argv[3], &e, 0); | 
|  | max_duty = EC_PWM_MAX_DUTY; | 
|  | } else { | 
|  | /* use percent duty */ | 
|  | value = strtoi(argv[2], &e, 0); | 
|  | max_duty = 100; | 
|  | } | 
|  |  | 
|  | if (*e || value > max_duty) { | 
|  | /* Bad param */ | 
|  | return EC_ERROR_PARAM2; | 
|  | } else if (value < 0) { | 
|  | /* Negative = disable */ | 
|  | pwm_enable(ch, 0); | 
|  | } else { | 
|  | ccprintf("Setting channel %d to %d\n", ch, value); | 
|  | pwm_enable(ch, 1); | 
|  | (max_duty == 100) ? pwm_set_duty(ch, value) : | 
|  | pwm_set_raw_duty(ch, value); | 
|  | } | 
|  | } | 
|  |  | 
|  | print_channel(ch, max_duty); | 
|  |  | 
|  | return EC_SUCCESS; | 
|  | } | 
|  | DECLARE_CONSOLE_COMMAND(pwmduty, cc_pwm_duty, | 
|  | "[channel [<percent> | -1=disable] | [raw <value>]]", | 
|  | "Get/set PWM duty cycles "); | 
|  | #endif /* CONFIG_PWM */ | 
|  |  | 
|  | /* | 
|  | * Initialize all PWM pins as functional.  This is not required under | 
|  | * Zephyr as pin configuration is automatically performed by chip driver | 
|  | */ | 
|  | static void pwm_pin_init(void) | 
|  | { | 
|  | gpio_config_module(MODULE_PWM, 1); | 
|  | } | 
|  | /* HOOK_PRIO_INIT_PWM may be used for chip PWM unit init, so use PRIO + 1 */ | 
|  | DECLARE_HOOK(HOOK_INIT, pwm_pin_init, HOOK_PRIO_INIT_PWM + 1); |