diff options
author | Vic Yang <victoryang@chromium.org> | 2013-08-05 11:17:35 +0800 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2013-08-27 23:20:33 +0000 |
commit | 5d014fd2dd5e92ad35a4f2dd1b58e0b1baebb65e (patch) | |
tree | 2078f9831177392f22ba202cbea4b09d69a9a710 /chip | |
parent | 99f06c39aabd262653c96c589c5cd26fc1fb8389 (diff) | |
download | chrome-ec-5d014fd2dd5e92ad35a4f2dd1b58e0b1baebb65e.tar.gz |
Refactor PWM module
This unifies the PWM module interface for LM4 and STM32. Now PWM
channels are defined in board.h/board.c. Instead of calling functions
named pwm_set_fan_duty(x), one can now use pwm_set_duty(PWM_CH_FAN, x),
which prevents additional functions added when we have a new PWM
channel.
BUG=chrome-os-partner:18343
TEST=Limit input current on Spring.
TEST=Check power LED in S0/S3/S5 on Snow.
TEST=Check keyboard backlight functionality on Link.
TEST=Check fan speed control/detecting on Link.
BRANCH=None
Change-Id: Ibac4d79f72e65c94776d503558a7592f7db859dc
Signed-off-by: Vic Yang <victoryang@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/64450
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Diffstat (limited to 'chip')
-rw-r--r-- | chip/lm4/build.mk | 3 | ||||
-rw-r--r-- | chip/lm4/fan.c (renamed from chip/lm4/pwm_fan.c) | 64 | ||||
-rw-r--r-- | chip/lm4/lpc.c | 4 | ||||
-rw-r--r-- | chip/lm4/pwm.c | 112 | ||||
-rw-r--r-- | chip/lm4/pwm_data.h | 21 | ||||
-rw-r--r-- | chip/lm4/pwm_kblight.c | 79 | ||||
-rw-r--r-- | chip/stm32/build.mk | 1 | ||||
-rw-r--r-- | chip/stm32/power_led.c | 91 | ||||
-rw-r--r-- | chip/stm32/pwm.c | 154 | ||||
-rw-r--r-- | chip/stm32/pwm_data.h | 37 | ||||
-rw-r--r-- | chip/stm32/registers.h | 11 |
11 files changed, 368 insertions, 209 deletions
diff --git a/chip/lm4/build.mk b/chip/lm4/build.mk index 0125ada892..26532d347a 100644 --- a/chip/lm4/build.mk +++ b/chip/lm4/build.mk @@ -19,7 +19,8 @@ chip-$(CONFIG_FLASH)+=flash.o chip-$(CONFIG_I2C)+=i2c.o chip-$(CONFIG_LPC)+=lpc.o chip-$(CONFIG_PECI)+=peci.o -chip-$(CONFIG_PWM_FAN)+=pwm_fan.o +chip-$(CONFIG_PWM)+=pwm.o +chip-$(CONFIG_FAN)+=fan.o chip-$(CONFIG_PWM_KBLIGHT)+=pwm_kblight.o chip-$(CONFIG_SPI)+=spi.o chip-$(CONFIG_WATCHDOG)+=watchdog.o diff --git a/chip/lm4/pwm_fan.c b/chip/lm4/fan.c index ac5b7579c7..cf109573ff 100644 --- a/chip/lm4/pwm_fan.c +++ b/chip/lm4/fan.c @@ -12,6 +12,7 @@ #include "gpio.h" #include "hooks.h" #include "host_command.h" +#include "pwm.h" #include "registers.h" #include "system.h" #include "task.h" @@ -37,15 +38,12 @@ static int fan_get_enabled(void) { - return (LM4_FAN_FANCTL & (1 << FAN_CH_CPU)) ? 1 : 0; + return pwm_get_enabled(PWM_CH_FAN); } static void fan_set_enabled(int enable) { - if (enable) - LM4_FAN_FANCTL |= (1 << FAN_CH_CPU); - else - LM4_FAN_FANCTL &= ~(1 << FAN_CH_CPU); + pwm_enable(PWM_CH_FAN, enable); #ifdef CONFIG_PWM_FAN_EN_GPIO gpio_set_level(CONFIG_PWM_FAN_EN_GPIO, enable); @@ -98,16 +96,6 @@ static void fan_set_rpm_target(int rpm) LM4_FAN_FANCMD(FAN_CH_CPU) = rpm; } -static int fan_get_duty_raw(void) -{ - return (LM4_FAN_FANCMD(FAN_CH_CPU) >> 16) & MAX_PWM; -} - -static void fan_set_duty_raw(int pwm) -{ - LM4_FAN_FANCMD(FAN_CH_CPU) = pwm << 16; -} - static int fan_get_status(void) { return (LM4_FAN_FANSTS >> (2 * FAN_CH_CPU)) & 0x03; @@ -158,22 +146,8 @@ void pwm_fan_set_percent_needed(int pct) fan_set_rpm_target(rpm); } -static int fan_get_duty_cycle(void) -{ - return fan_get_duty_raw() * 100 / MAX_PWM; -} - static void fan_set_duty_cycle(int percent) { - int pwm; - - if (percent < 0) - percent = 0; - else if (percent > 100) - percent = 100; - - pwm = (MAX_PWM * percent) / 100; - /* Move the fan to manual control */ fan_set_rpm_mode(0); @@ -184,7 +158,12 @@ static void fan_set_duty_cycle(int percent) fan_set_thermal_control_enabled(0); /* Set the duty cycle */ - fan_set_duty_raw(pwm); + pwm_set_duty(PWM_CH_FAN, percent); +} + +static int fan_get_duty_cycle(void) +{ + return pwm_get_duty(PWM_CH_FAN); } /*****************************************************************************/ @@ -360,30 +339,8 @@ static void pwm_fan_init(void) int version, size; int i; - /* Enable the fan module and delay a few clocks */ - LM4_SYSTEM_RCGCFAN = 1; - clock_wait_cycles(3); - - /* Configure GPIOs */ gpio_config_module(MODULE_PWM_FAN, 1); - /* Disable all fans */ - LM4_FAN_FANCTL = 0; - - /* - * Configure CPU fan: - * 0x8000 = bit 15 = auto-restart - * 0x0000 = bit 14 = slow acceleration - * 0x0000 = bits 13:11 = no hysteresis - * 0x0000 = bits 10:8 = start period (2<<0) edges - * 0x0000 = bits 7:6 = no fast start - * 0x0020 = bits 5:4 = average 4 edges when calculating RPM - * 0x000c = bits 3:2 = 8 pulses per revolution - * (see note at top of file) - * 0x0000 = bit 0 = automatic control - */ - LM4_FAN_FANCH(FAN_CH_CPU) = 0x802c; - prev = (const struct pwm_fan_state *) system_get_jump_tag(PWMFAN_SYSJUMP_TAG, &version, &size); if (prev && version == PWM_HOOK_VERSION && size == sizeof(*prev)) { @@ -402,7 +359,7 @@ static void pwm_fan_init(void) for (i = 0; i < EC_FAN_SPEED_ENTRIES; i++) mapped[i] = EC_FAN_SPEED_NOT_PRESENT; } -DECLARE_HOOK(HOOK_INIT, pwm_fan_init, HOOK_PRIO_DEFAULT); +DECLARE_HOOK(HOOK_INIT, pwm_fan_init, HOOK_PRIO_DEFAULT + 1); static void pwm_fan_second(void) { @@ -449,7 +406,6 @@ static void pwm_fan_S3_S5(void) */ fan_set_rpm_target(0); fan_set_enabled(0); /* crosbug.com/p/8097 */ - } DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pwm_fan_S3_S5, HOOK_PRIO_DEFAULT); DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pwm_fan_S3_S5, HOOK_PRIO_DEFAULT); diff --git a/chip/lm4/lpc.c b/chip/lm4/lpc.c index 04563caf66..71938409d8 100644 --- a/chip/lm4/lpc.c +++ b/chip/lm4/lpc.c @@ -401,7 +401,7 @@ static void handle_acpi_write(int is_cmd) * good enough and less code than defining a new * console command interface just for ACPI read/write. */ - result = pwm_get_keyboard_backlight(); + result = pwm_get_duty(PWM_CH_KBLIGHT); break; #endif default: @@ -427,7 +427,7 @@ static void handle_acpi_write(int is_cmd) * debug console. */ CPRINTF("\r[%T ACPI kblight %d]", data); - pwm_set_keyboard_backlight(data); + pwm_set_duty(PWM_CH_KBLIGHT, data); break; #endif default: diff --git a/chip/lm4/pwm.c b/chip/lm4/pwm.c new file mode 100644 index 0000000000..5d797ded3a --- /dev/null +++ b/chip/lm4/pwm.c @@ -0,0 +1,112 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* PWM control module for LM4 */ + +#include "clock.h" +#include "gpio.h" +#include "hooks.h" +#include "pwm.h" +#include "pwm_data.h" +#include "registers.h" +#include "thermal.h" +#include "util.h" + +/* Maximum RPM for PWM controller */ +#define MAX_RPM 0x1fff + +/* Maximum PWM for PWM controller */ +#define MAX_PWM 0x1ff + +#define RPM_SCALE 2 + +void pwm_enable(enum pwm_channel ch, int enabled) +{ + const struct pwm_t *pwm = pwm_channels + ch; + if (enabled) + LM4_FAN_FANCTL |= (1 << pwm->channel); + else + LM4_FAN_FANCTL &= ~(1 << pwm->channel); +} + +int pwm_get_enabled(enum pwm_channel ch) +{ + const struct pwm_t *pwm = pwm_channels + ch; + return (LM4_FAN_FANCTL & (1 << pwm->channel)) ? 1 : 0; +} + +void pwm_set_duty(enum pwm_channel ch, int percent) +{ + const struct pwm_t *pwm = pwm_channels + ch; + int duty; + + if (percent < 0) + percent = 0; + else if (percent > 100) + percent = 100; + + duty = (MAX_PWM * percent) / 100; + + /* Always enable the channel */ + pwm_enable(ch, 1); + + /* Set the duty cycle */ + LM4_FAN_FANCMD(pwm->channel) = duty << 16; +} + +int pwm_get_duty(enum pwm_channel ch) +{ + const struct pwm_t *pwm = pwm_channels + ch; + + return (LM4_FAN_FANCMD(pwm->channel) >> 16) * 100 / MAX_PWM; +} + +static void pwm_init(void) +{ + int i; + const struct pwm_t *pwm; + + /* Enable the fan module and delay a few clocks */ + LM4_SYSTEM_RCGCFAN = 1; + clock_wait_cycles(3); + + /* Disable all fans */ + LM4_FAN_FANCTL = 0; + + for (i = 0; i < PWM_CH_COUNT; ++i) { + pwm = pwm_channels + i; + + if (pwm->flags & PWM_CONFIG_HAS_RPM_MODE) { + /* + * Configure PWM: + * 0x8000 = bit 15 = auto-restart + * 0x0000 = bit 14 = slow acceleration + * 0x0000 = bits 13:11 = no hysteresis + * 0x0000 = bits 10:8 = start period (2<<0) edges + * 0x0000 = bits 7:6 = no fast start + * 0x0020 = bits 5:4 = average 4 edges when + * calculating RPM + * 0x000c = bits 3:2 = 8 pulses per revolution + * (see note at top of file) + * 0x0000 = bit 0 = automatic control + */ + LM4_FAN_FANCH(pwm->channel) = 0x802c; + } else { + /* + * Configure keyboard backlight: + * 0x0000 = bit 15 = no auto-restart + * 0x0000 = bit 14 = slow acceleration + * 0x0000 = bits 13:11 = no hysteresis + * 0x0000 = bits 10:8 = start period (2<<0) edges + * 0x0000 = bits 7:6 = no fast start + * 0x0000 = bits 5:4 = no RPM averaging + * 0x0000 = bits 3:2 = 1 pulses per revolution + * 0x0001 = bit 0 = manual control + */ + LM4_FAN_FANCH(pwm->channel) = 0x0001; + } + } +} +DECLARE_HOOK(HOOK_INIT, pwm_init, HOOK_PRIO_DEFAULT); diff --git a/chip/lm4/pwm_data.h b/chip/lm4/pwm_data.h new file mode 100644 index 0000000000..ac83fcf7da --- /dev/null +++ b/chip/lm4/pwm_data.h @@ -0,0 +1,21 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* LM4-specific PWM module for Chrome EC */ + +#ifndef __CROS_EC_LM4_PWM_H +#define __CROS_EC_LM4_PWM_H + +/* Data structure to define PWM channels. */ +struct pwm_t { + /* PWM channel ID */ + int channel; + /* PWM channel flags. See include/pwm.h */ + uint32_t flags; +}; + +extern const struct pwm_t pwm_channels[]; + +#endif /* __CROS_EC_LM4_PWM_H */ diff --git a/chip/lm4/pwm_kblight.c b/chip/lm4/pwm_kblight.c index 25ad3dc45b..f76f0399e1 100644 --- a/chip/lm4/pwm_kblight.c +++ b/chip/lm4/pwm_kblight.c @@ -31,35 +31,6 @@ struct pwm_kbd_state { uint8_t pad0, pad1; /* Pad to multiple of 4 bytes. */ }; -void pwm_enable_keyboard_backlight(int enable) -{ - if (enable) - LM4_FAN_FANCTL |= (1 << FAN_CH_KBLIGHT); - else - LM4_FAN_FANCTL &= ~(1 << FAN_CH_KBLIGHT); -} - -int pwm_get_keyboard_backlight_enabled(void) -{ - return (LM4_FAN_FANCTL & (1 << FAN_CH_KBLIGHT)) ? 1 : 0; -} - -int pwm_get_keyboard_backlight(void) -{ - return ((LM4_FAN_FANCMD(FAN_CH_KBLIGHT) >> 16) * 100 + - MAX_PWM / 2) / MAX_PWM; -} - -void pwm_set_keyboard_backlight(int percent) -{ - if (percent < 0) - percent = 0; - else if (percent > 100) - percent = 100; - - LM4_FAN_FANCMD(FAN_CH_KBLIGHT) = ((percent * MAX_PWM + 50) / 100) << 16; -} - /*****************************************************************************/ /* Console commands */ @@ -70,10 +41,10 @@ static int command_kblight(int argc, char **argv) int i = strtoi(argv[1], &e, 0); if (*e) return EC_ERROR_PARAM1; - pwm_set_keyboard_backlight(i); + pwm_set_duty(PWM_CH_KBLIGHT, i); } - ccprintf("Keyboard backlight: %d%%\n", pwm_get_keyboard_backlight()); + ccprintf("Keyboard backlight: %d%%\n", pwm_get_duty(PWM_CH_KBLIGHT)); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(kblight, command_kblight, @@ -88,8 +59,8 @@ int pwm_command_get_keyboard_backlight(struct host_cmd_handler_args *args) { struct ec_response_pwm_get_keyboard_backlight *r = args->response; - r->percent = pwm_get_keyboard_backlight(); - r->enabled = pwm_get_keyboard_backlight_enabled(); + r->percent = pwm_get_duty(PWM_CH_KBLIGHT); + r->enabled = pwm_get_enabled(PWM_CH_KBLIGHT); args->response_size = sizeof(*r); return EC_RES_SUCCESS; @@ -102,7 +73,7 @@ int pwm_command_set_keyboard_backlight(struct host_cmd_handler_args *args) { const struct ec_params_pwm_set_keyboard_backlight *p = args->params; - pwm_set_keyboard_backlight(p->percent); + pwm_set_duty(PWM_CH_KBLIGHT, p->percent); return EC_RES_SUCCESS; } @@ -118,39 +89,19 @@ static void pwm_kblight_init(void) const struct pwm_kbd_state *prev; int version, size; - /* Enable the fan module and delay a few clocks */ - LM4_SYSTEM_RCGCFAN = 1; - clock_wait_cycles(3); - - /* Configure GPIOs */ + /* Configure GPIO */ gpio_config_module(MODULE_PWM_KBLIGHT, 1); - /* Disable all fans */ - LM4_FAN_FANCTL = 0; - - /* - * Configure keyboard backlight: - * 0x0000 = bit 15 = auto-restart - * 0x0000 = bit 14 = slow acceleration - * 0x0000 = bits 13:11 = no hysteresis - * 0x0000 = bits 10:8 = start period (2<<0) edges - * 0x0000 = bits 7:6 = no fast start - * 0x0000 = bits 5:4 = average 4 edges when calculating RPM - * 0x0000 = bits 3:2 = 4 pulses per revolution - * 0x0001 = bit 0 = manual control - */ - LM4_FAN_FANCH(FAN_CH_KBLIGHT) = 0x0001; - prev = (const struct pwm_kbd_state *) system_get_jump_tag(PWMKBD_SYSJUMP_TAG, &version, &size); if (prev && version == PWM_HOOK_VERSION && size == sizeof(*prev)) { /* Restore previous state. */ - pwm_enable_keyboard_backlight(prev->kblight_en); - pwm_set_keyboard_backlight(prev->kblight_percent); + pwm_enable(PWM_CH_KBLIGHT, prev->kblight_en); + pwm_set_duty(PWM_CH_KBLIGHT, prev->kblight_percent); } else { /* Enable keyboard backlight control, turned down */ - pwm_set_keyboard_backlight(0); - pwm_enable_keyboard_backlight(1); + pwm_set_duty(PWM_CH_KBLIGHT, 0); + pwm_enable(PWM_CH_KBLIGHT, 1); } } DECLARE_HOOK(HOOK_INIT, pwm_kblight_init, HOOK_PRIO_DEFAULT); @@ -159,8 +110,8 @@ static void pwm_kblight_preserve_state(void) { struct pwm_kbd_state state; - state.kblight_en = pwm_get_keyboard_backlight_enabled(); - state.kblight_percent = pwm_get_keyboard_backlight(); + state.kblight_en = pwm_get_enabled(PWM_CH_KBLIGHT); + state.kblight_percent = pwm_get_duty(PWM_CH_KBLIGHT); system_add_jump_tag(PWMKBD_SYSJUMP_TAG, PWM_HOOK_VERSION, sizeof(state), &state); @@ -169,18 +120,18 @@ DECLARE_HOOK(HOOK_SYSJUMP, pwm_kblight_preserve_state, HOOK_PRIO_DEFAULT); static void pwm_kblight_suspend(void) { - pwm_set_keyboard_backlight(0); + pwm_set_duty(PWM_CH_KBLIGHT, 0); } DECLARE_HOOK(HOOK_CHIPSET_SUSPEND, pwm_kblight_suspend, HOOK_PRIO_DEFAULT); static void pwm_kblight_shutdown(void) { - pwm_set_keyboard_backlight(0); + pwm_set_duty(PWM_CH_KBLIGHT, 0); } DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pwm_kblight_shutdown, HOOK_PRIO_DEFAULT); static void pwm_kblight_lid_change(void) { - pwm_enable_keyboard_backlight(lid_is_open()); + pwm_enable(PWM_CH_KBLIGHT, lid_is_open()); } DECLARE_HOOK(HOOK_LID_CHANGE, pwm_kblight_lid_change, HOOK_PRIO_DEFAULT); diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk index 24ae387ca7..58fce53887 100644 --- a/chip/stm32/build.mk +++ b/chip/stm32/build.mk @@ -18,3 +18,4 @@ chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o chip-$(HAS_TASK_POWERLED)+=power_led.o chip-$(CONFIG_FLASH)+=flash-$(CHIP_FAMILY).o chip-$(CONFIG_ADC)+=adc.o +chip-$(CONFIG_PWM)+=pwm.o diff --git a/chip/stm32/power_led.c b/chip/stm32/power_led.c index 237fd14670..cdfb8a4ff5 100644 --- a/chip/stm32/power_led.c +++ b/chip/stm32/power_led.c @@ -21,6 +21,8 @@ #include "hooks.h" #include "hwtimer.h" #include "power_led.h" +#include "pwm.h" +#include "pwm_data.h" #include "registers.h" #include "task.h" #include "timer.h" @@ -32,7 +34,6 @@ static enum powerled_state led_state = POWERLED_STATE_ON; static int power_led_percent = 100; -static int using_pwm; void powerled_set_state(enum powerled_state new_state) { @@ -45,77 +46,18 @@ static void power_led_set_duty(int percent) { ASSERT((percent >= 0) && (percent <= 100)); power_led_percent = percent; - /* - * Set the duty cycle. CCRx = percent * ARR / 100. Since we set - * ARR=100, this is just percent. - */ -#ifdef BOARD_snow - STM32_TIM_CCR2(TIM_POWER_LED) = percent; -#else - STM32_TIM_CCR3(TIM_POWER_LED) = percent; -#endif + pwm_set_duty(PWM_CH_POWER_LED, percent); } static void power_led_use_pwm(void) { - /* Configure power LED GPIO for TIM2/PWM alternate function */ -#ifdef BOARD_snow - /* PB3 = TIM2_CH2 */ - uint32_t val = STM32_GPIO_CRL(GPIO_B) & ~0x0000f000; - val |= 0x00009000; /* alt. function (TIM2/PWM) */ - STM32_GPIO_CRL(GPIO_B) = val; -#else - gpio_config_module(MODULE_POWER_LED, 1); -#endif - - /* Enable timer */ - __hw_timer_enable_clock(TIM_POWER_LED, 1); - - /* Disable counter during setup */ - STM32_TIM_CR1(TIM_POWER_LED) = 0x0000; - - /* - * CPU clock / PSC determines how fast the counter operates. - * ARR determines the wave period, CCRn determines duty cycle. - * Thus, frequency = cpu_freq / PSC / ARR. so: - * - * frequency = cpu_freq / (cpu_freq/10000) / 100 = 100 Hz. - */ - STM32_TIM_PSC(TIM_POWER_LED) = clock_get_freq() / 10000; - STM32_TIM_ARR(TIM_POWER_LED) = 100; - + pwm_enable(PWM_CH_POWER_LED, 1); power_led_set_duty(100); - -#ifdef BOARD_snow - /* CC2 configured as output, PWM mode 1, preload enable */ - STM32_TIM_CCMR1(TIM_POWER_LED) = (6 << 12) | (1 << 11); - - /* CC2 output enable, active low */ - STM32_TIM_CCER(TIM_POWER_LED) = (1 << 4) | (1 << 5); -#else - /* CC3 configured as output, PWM mode 1, preload enable */ - STM32_TIM_CCMR2(TIM_POWER_LED) = (6 << 4) | (1 << 3); - - /* CC3 output enable, active low */ - STM32_TIM_CCER(TIM_POWER_LED) = (1 << 8) | (1 << 9); -#endif - - /* Generate update event to force loading of shadow registers */ - STM32_TIM_EGR(TIM_POWER_LED) |= 1; - - /* Enable auto-reload preload, start counting */ - STM32_TIM_CR1(TIM_POWER_LED) |= (1 << 7) | (1 << 0); - - using_pwm = 1; } static void power_led_manual_off(void) { - /* Disable counter */ - STM32_TIM_CR1(TIM_POWER_LED) &= ~0x1; - - /* Disable timer clock */ - __hw_timer_enable_clock(TIM_POWER_LED, 0); + pwm_enable(PWM_CH_POWER_LED, 0); /* * Reconfigure GPIO as a floating input. Alternatively we could @@ -128,8 +70,6 @@ static void power_led_manual_off(void) #else gpio_config_module(MODULE_POWER_LED, 0); #endif - - using_pwm = 0; } /** @@ -166,17 +106,6 @@ static int power_led_step(void) return state_timeout; } -/** - * Handle clock frequency change - */ -static void power_led_freq_change(void) -{ - /* If we're using PWM, re-initialize to adjust timer divisor */ - if (using_pwm) - power_led_use_pwm(); -} -DECLARE_HOOK(HOOK_FREQ_CHANGE, power_led_freq_change, HOOK_PRIO_DEFAULT); - void power_led_task(void) { while (1) { @@ -189,21 +118,18 @@ void power_led_task(void) * duty duty cycle of 100%. This produces a softer * brightness than setting the GPIO to solid ON. */ - if (!using_pwm) - power_led_use_pwm(); + power_led_use_pwm(); power_led_set_duty(100); state_timeout = -1; break; case POWERLED_STATE_OFF: /* Reconfigure GPIO to disable the LED */ - if (using_pwm) - power_led_manual_off(); + power_led_manual_off(); state_timeout = -1; break; case POWERLED_STATE_SUSPEND: /* Drive using PWM with variable duty cycle */ - if (!using_pwm) - power_led_use_pwm(); + power_led_use_pwm(); state_timeout = power_led_step(); break; default: @@ -214,6 +140,7 @@ void power_led_task(void) } } +#define CONFIG_CMD_POWERLED #ifdef CONFIG_CMD_POWERLED static int command_powerled(int argc, char **argv) { diff --git a/chip/stm32/pwm.c b/chip/stm32/pwm.c new file mode 100644 index 0000000000..73db55334f --- /dev/null +++ b/chip/stm32/pwm.c @@ -0,0 +1,154 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* PWM control module for STM32 */ + +#include "clock.h" +#include "gpio.h" +#include "hooks.h" +#include "hwtimer.h" +#include "pwm.h" +#include "pwm_data.h" +#include "registers.h" +#include "util.h" + +static int using_pwm[PWM_CH_COUNT]; + +void pwm_set_duty(enum pwm_channel ch, int percent) +{ + const struct pwm_t *pwm = pwm_channels + ch; + timer_ctlr_t *tim = (timer_ctlr_t *)(pwm->tim.base); + + ASSERT((percent >= 0) && (percent <= 100)); + tim->ccr[pwm->channel] = percent; +} + +int pwm_get_duty(enum pwm_channel ch) +{ + const struct pwm_t *pwm = pwm_channels + ch; + timer_ctlr_t *tim = (timer_ctlr_t *)(pwm->tim.base); + return tim->ccr[pwm->channel]; +} + +static void pwm_configure(enum pwm_channel ch) +{ + const struct pwm_t *pwm = pwm_channels + ch; + const struct gpio_info *gpio = gpio_list + pwm->pin; + timer_ctlr_t *tim = (timer_ctlr_t *)(pwm->tim.base); + volatile unsigned *ccmr = NULL; +#ifdef CHIP_FAMILY_stm32f + int mask = gpio->mask; + volatile uint32_t *gpio_cr = NULL; + uint32_t val; +#endif + + if (using_pwm[ch]) + return; + +#ifdef CHIP_FAMILY_stm32f + if (mask < 0x100) { + gpio_cr = &STM32_GPIO_CRL(gpio->port); + } else { + gpio_cr = &STM32_GPIO_CRH(gpio->port); + mask >>= 8; + } + + /* Expand mask from 8-bit to 32-bit */ + mask = mask * mask; + mask = mask * mask; + + /* Set alternate function */ + val = *gpio_cr & ~(mask * 0xf); + val |= mask * 0x9; + *gpio_cr = val; +#else /* stm32l */ + gpio_set_alternate_function(gpio->port, gpio->mask, + GPIO_ALT_TIM(pwm->tim.id)); +#endif + + /* Enable timer */ + __hw_timer_enable_clock(pwm->tim.id, 1); + + /* Disable counter during setup */ + tim->cr1 = 0x0000; + + /* + * CPU clock / PSC determines how fast the counter operates. + * ARR determines the wave period, CCRn determines duty cycle. + * Thus, frequency = cpu_freq / PSC / ARR. so: + * + * frequency = cpu_freq / (cpu_freq/10000) / 100 = 100 Hz. + */ + tim->psc = clock_get_freq() / 10000; + tim->arr = 100; + + if (pwm->channel <= 2) /* Channel ID starts from 1 */ + ccmr = &tim->ccmr1; + else + ccmr = &tim->ccmr2; + + /* Output, PWM mode 1, preload enable */ + if (pwm->channel & 0x1) + *ccmr = (6 << 4) | (1 << 3); + else + *ccmr = (6 << 12) | (1 << 11); + + /* Output enable. Set active high/low. */ + if (pwm->flags & PWM_CONFIG_ACTIVE_LOW) + tim->ccer = 3 << (pwm->channel * 4 - 4); + else + tim->ccer = 1 << (pwm->channel * 4 - 4); + + /* Generate update event to force loading of shadow registers */ + tim->egr |= 1; + + /* Enable auto-reload preload, start counting */ + tim->cr1 |= (1 << 7) | (1 << 0); + + using_pwm[ch] = 1; +} + +static void pwm_disable(enum pwm_channel ch) +{ + const struct pwm_t *pwm = pwm_channels + ch; + timer_ctlr_t *tim = (timer_ctlr_t *)(pwm->tim.base); + + if (using_pwm[ch] == 0) + return; + + /* Disable counter */ + tim->cr1 &= ~0x1; + + /* Disable timer clock */ + __hw_timer_enable_clock(pwm->tim.id, 0); + + using_pwm[ch] = 0; +} + +void pwm_enable(enum pwm_channel ch, int enabled) +{ + if (enabled) + pwm_configure(ch); + else + pwm_disable(ch); +} + +static void pwm_reconfigure(enum pwm_channel ch) +{ + using_pwm[ch] = 0; + pwm_configure(ch); +} + +/** + * Handle clock frequency change + */ +static void pwm_freq_change(void) +{ + int i; + for (i = 0; i < PWM_CH_COUNT; ++i) + if (using_pwm[i]) + pwm_reconfigure(i); +} +DECLARE_HOOK(HOOK_FREQ_CHANGE, pwm_freq_change, HOOK_PRIO_DEFAULT); diff --git a/chip/stm32/pwm_data.h b/chip/stm32/pwm_data.h new file mode 100644 index 0000000000..d588dabeaa --- /dev/null +++ b/chip/stm32/pwm_data.h @@ -0,0 +1,37 @@ +/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +/* STM32-specific PWM module for Chrome EC */ + +#ifndef __CROS_EC_STM32_PWM_H +#define __CROS_EC_STM32_PWM_H + +/* Data structure to define PWM channels. */ +struct pwm_t { + /* + * Timer powering the PWM channel. Must use STM32_TIM(x) to + * initialize + */ + struct { + int id; + uintptr_t base; + } tim; + /* Channel ID within the timer */ + int channel; + /* PWM channel flags. See include/pwm.h */ + uint32_t flags; + /* GPIO pin corresponding to the PWM channel */ + enum gpio_signal pin; +}; + +extern const struct pwm_t pwm_channels[]; + +/* Macro to fill in both timer ID and register base */ +#define STM32_TIM(x) {x, STM32_TIM_BASE(x)} + +/* Plain ID mapping for readability */ +#define STM32_TIM_CH(x) (x) + +#endif /* __CROS_EC_STM32_PWM_H */ diff --git a/chip/stm32/registers.h b/chip/stm32/registers.h index 0d7d14e9a2..3a4934ea71 100644 --- a/chip/stm32/registers.h +++ b/chip/stm32/registers.h @@ -145,8 +145,10 @@ #define STM32_TIM16_BASE 0x40014400 /* STM32F100 only */ #define STM32_TIM17_BASE 0x40014800 /* STM32F100 only */ +#define STM32_TIM_BASE(n) CONCAT3(STM32_TIM, n, _BASE) + #define STM32_TIM_REG(n, offset) \ - REG16(CONCAT3(STM32_TIM, n, _BASE) + (offset)) + REG16(STM32_TIM_BASE(n) + (offset)) #define STM32_TIM_CR1(n) STM32_TIM_REG(n, 0x00) #define STM32_TIM_CR2(n) STM32_TIM_REG(n, 0x04) @@ -186,12 +188,8 @@ struct timer_ctlr { unsigned psc; unsigned arr; - unsigned reserved30; - unsigned ccr1; - unsigned ccr2; - unsigned ccr3; + unsigned ccr[5]; /* ccr[0] = reserved30 */ - unsigned ccr4; unsigned reserved44; unsigned dcr; unsigned dmar; @@ -235,6 +233,7 @@ typedef volatile struct timer_ctlr timer_ctlr_t; #define GPIO_ALT_TIM2 0x1 #define GPIO_ALT_TIM3_4 0x2 #define GPIO_ALT_TIM9_11 0x3 +#define GPIO_ALT_TIM(x) (((x) > 5) ? 0x3 : ((x) / 3 + 1)) #define GPIO_ALT_I2C 0x4 #define GPIO_ALT_SPI 0x5 #define GPIO_ALT_USART 0x7 |