diff options
37 files changed, 606 insertions, 319 deletions
diff --git a/board/bds/board.h b/board/bds/board.h index b35cf0445b..f2e7c3d75b 100644 --- a/board/bds/board.h +++ b/board/bds/board.h @@ -40,6 +40,10 @@ enum adc_channel { ADC_CH_COUNT }; +enum pwm_channel { + PWM_CH_COUNT +}; + /* I2C ports */ #define I2C_PORT_LIGHTBAR 5 // port 5 / PA6:7 on link, but PG6:7 on badger /* Number of I2C ports used */ diff --git a/board/bolt/board.c b/board/bolt/board.c index 55c22f2f7b..4fc4b2fc49 100644 --- a/board/bolt/board.c +++ b/board/bolt/board.c @@ -22,6 +22,7 @@ #include "peci.h" #include "power_button.h" #include "pwm.h" +#include "pwm_data.h" #include "registers.h" #include "switch.h" #include "temp_sensor.h" @@ -179,6 +180,13 @@ const struct adc_t adc_channels[] = { }; BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT); +/* PWM channels */ +const struct pwm_t pwm_channels[] = { + [PWM_CH_FAN] = {FAN_CH_CPU, PWM_CONFIG_HAS_RPM_MODE}, + [PWM_CH_KBLIGHT] = {FAN_CH_KBLIGHT, 0}, +}; +BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); + /* I2C ports */ const struct i2c_port_t i2c_ports[] = { /* Note: battery and charger share a port. Only include it once in diff --git a/board/bolt/board.h b/board/bolt/board.h index 2133701812..a9c417f73b 100644 --- a/board/bolt/board.h +++ b/board/bolt/board.h @@ -35,10 +35,11 @@ #define CONFIG_CHARGER_SENSE_RESISTOR_AC 10 /* External Charger maximum current. */ #define CONFIG_CHARGER_INPUT_CURRENT 5000 -#define CONFIG_PWM_FAN -#define CONFIG_PWM_FAN_RPM_MIN 1000 -#define CONFIG_PWM_FAN_RPM_MAX 5050 -#define CONFIG_PWM_FAN_POWER_GOOD GPIO_PP5000_PGOOD +#define CONFIG_FAN +#define CONFIG_FAN_RPM_MIN 1000 +#define CONFIG_FAN_RPM_MAX 5050 +#define CONFIG_FAN_POWER_GOOD GPIO_PP5000_PGOOD +#define CONFIG_PWM #define CONFIG_PWM_KBLIGHT #define CONFIG_TEMP_SENSOR #define CONFIG_UART_HOST 2 @@ -177,6 +178,7 @@ enum x86_signal { /* Number of X86 signals */ X86_SIGNAL_COUNT }; + enum adc_channel { /* EC internal die temperature in degrees K. */ ADC_CH_EC_TEMP = 0, @@ -187,6 +189,14 @@ enum adc_channel { ADC_CH_COUNT }; +enum pwm_channel { + PWM_CH_FAN, + PWM_CH_KBLIGHT, + + /* Number of PWM channels */ + PWM_CH_COUNT +}; + enum temp_sensor_id { /* HEY - need two I2C sensor values, and put PECI first */ diff --git a/board/falco/board.c b/board/falco/board.c index e65e68dae0..c43c3d08f5 100644 --- a/board/falco/board.c +++ b/board/falco/board.c @@ -23,6 +23,8 @@ #include "lm4_adc.h" #include "peci.h" #include "power_button.h" +#include "pwm.h" +#include "pwm_data.h" #include "registers.h" #include "switch.h" #include "temp_sensor.h" @@ -182,6 +184,12 @@ const struct adc_t adc_channels[] = { }; BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT); +/* PWM channels */ +const struct pwm_t pwm_channels[] = { + [PWM_CH_FAN] = {FAN_CH_CPU, PWM_CONFIG_HAS_RPM_MODE}, +}; +BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); + /* I2C ports */ const struct i2c_port_t i2c_ports[] = { /* Note: battery and charger share a port. Only include it once in diff --git a/board/falco/board.h b/board/falco/board.h index 1a75037284..63b14b1b3a 100644 --- a/board/falco/board.h +++ b/board/falco/board.h @@ -18,14 +18,15 @@ #define CONFIG_CHIPSET_X86 #define CONFIG_EXTPOWER_FALCO #define CONFIG_EXTPOWER_GPIO +#define CONFIG_FAN +#define CONFIG_FAN_RPM_MIN 1000 +#define CONFIG_FAN_RPM_MAX 5050 +#define CONFIG_FAN_POWER_GOOD GPIO_PP5000_PGOOD #define CONFIG_KEYBOARD_BOARD_CONFIG #define CONFIG_KEYBOARD_PROTOCOL_8042 #define CONFIG_POWER_BUTTON #define CONFIG_POWER_BUTTON_X86 -#define CONFIG_PWM_FAN -#define CONFIG_PWM_FAN_RPM_MIN 1000 -#define CONFIG_PWM_FAN_RPM_MAX 5050 -#define CONFIG_PWM_FAN_POWER_GOOD GPIO_PP5000_PGOOD +#define CONFIG_PWM #define CONFIG_TEMP_SENSOR #define CONFIG_TEMP_SENSOR_G781 #define CONFIG_UART_HOST 2 @@ -177,6 +178,13 @@ enum adc_channel { ADC_CH_COUNT }; +enum pwm_channel { + PWM_CH_FAN, + + /* Number of PWM channels */ + PWM_CH_COUNT +}; + enum temp_sensor_id { /* CPU die temperature via PECI */ TEMP_SENSOR_CPU_PECI = 0, diff --git a/board/host/board.h b/board/host/board.h index 1af8db0c5d..9301d24e00 100644 --- a/board/host/board.h +++ b/board/host/board.h @@ -14,6 +14,7 @@ #define CONFIG_KEYBOARD_PROTOCOL_MKBP #define CONFIG_POWER_BUTTON #undef CONFIG_WATCHDOG +#define CONFIG_PWM #undef CONFIG_CONSOLE_HISTORY #define CONFIG_CONSOLE_HISTORY 4 @@ -53,4 +54,10 @@ enum adc_channel { ADC_CH_COUNT }; +enum pwm_channel { + PWM_CH_FAN, + + PWM_CH_COUNT +}; + #endif /* __BOARD_H */ diff --git a/board/link/board.c b/board/link/board.c index 3a00d16a03..618700f388 100644 --- a/board/link/board.c +++ b/board/link/board.c @@ -20,6 +20,7 @@ #include "peci.h" #include "power_button.h" #include "pwm.h" +#include "pwm_data.h" #include "registers.h" #include "switch.h" #include "temp_sensor.h" @@ -181,6 +182,13 @@ const struct adc_t adc_channels[] = { }; BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT); +/* PWM channels */ +const struct pwm_t pwm_channels[] = { + [PWM_CH_FAN] = {FAN_CH_CPU, PWM_CONFIG_HAS_RPM_MODE}, + [PWM_CH_KBLIGHT] = {FAN_CH_KBLIGHT, 0}, +}; +BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); + /* I2C ports */ const struct i2c_port_t i2c_ports[] = { /* Note: battery and charger share a port. Only include it once in diff --git a/board/link/board.h b/board/link/board.h index b45de1981a..7d0a8ae268 100644 --- a/board/link/board.h +++ b/board/link/board.h @@ -18,6 +18,10 @@ #define CONFIG_CHIPSET_IVYBRIDGE #define CONFIG_CHIPSET_X86 #define CONFIG_EXTPOWER_GPIO +#define CONFIG_FAN +#define CONFIG_FAN_RPM_MIN 1500 +#define CONFIG_FAN_RPM_MAX 9300 +#define CONFIG_FAN_POWER_GOOD GPIO_PGOOD_5VALW #define CONFIG_I2C_PASSTHRU_RESTRICTED #define CONFIG_KEYBOARD_BOARD_CONFIG #define CONFIG_KEYBOARD_PROTOCOL_8042 @@ -25,10 +29,7 @@ #define CONFIG_ONEWIRE #define CONFIG_POWER_BUTTON #define CONFIG_POWER_BUTTON_X86 -#define CONFIG_PWM_FAN -#define CONFIG_PWM_FAN_RPM_MIN 1500 -#define CONFIG_PWM_FAN_RPM_MAX 9300 -#define CONFIG_PWM_FAN_POWER_GOOD GPIO_PGOOD_5VALW +#define CONFIG_PWM #define CONFIG_PWM_KBLIGHT #define CONFIG_TEMP_SENSOR #define CONFIG_TEMP_SENSOR_TMP006 @@ -64,6 +65,14 @@ enum adc_channel { ADC_CH_COUNT }; +enum pwm_channel { + PWM_CH_FAN, + PWM_CH_KBLIGHT, + + /* Number of PWM channels */ + PWM_CH_COUNT +}; + /* Charger module */ #define CONFIG_CHARGER_SENSE_RESISTOR 10 /* Charge sense resistor, mOhm */ #define CONFIG_CHARGER_SENSE_RESISTOR_AC 20 /* Input sensor resistor, mOhm */ diff --git a/board/peppy/board.c b/board/peppy/board.c index 9b30ac8865..0a782230bf 100644 --- a/board/peppy/board.c +++ b/board/peppy/board.c @@ -20,6 +20,8 @@ #include "lm4_adc.h" #include "peci.h" #include "power_button.h" +#include "pwm.h" +#include "pwm_data.h" #include "registers.h" #include "switch.h" #include "temp_sensor.h" @@ -172,6 +174,12 @@ const struct adc_t adc_channels[] = { }; BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT); +/* PWM channels */ +const struct pwm_t pwm_channels[] = { + [PWM_CH_FAN] = {FAN_CH_CPU, PWM_CONFIG_HAS_RPM_MODE}, +}; +BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); + /* I2C ports */ const struct i2c_port_t i2c_ports[] = { /* Note: battery and charger share a port. Only include it once in diff --git a/board/peppy/board.h b/board/peppy/board.h index 7c3a385071..2be2988aa6 100644 --- a/board/peppy/board.h +++ b/board/peppy/board.h @@ -19,15 +19,16 @@ #define CONFIG_CHIPSET_HASWELL #define CONFIG_CHIPSET_X86 #define CONFIG_EXTPOWER_GPIO +#define CONFIG_FAN +#define CONFIG_FAN_EN_GPIO GPIO_PP5000_FAN_EN +#define CONFIG_FAN_RPM_MIN 1000 +#define CONFIG_FAN_RPM_MAX 5050 +#define CONFIG_FAN_POWER_GOOD GPIO_PP5000_PGOOD #define CONFIG_KEYBOARD_BOARD_CONFIG #define CONFIG_KEYBOARD_PROTOCOL_8042 #define CONFIG_POWER_BUTTON #define CONFIG_POWER_BUTTON_X86 -#define CONFIG_PWM_FAN -#define CONFIG_PWM_FAN_EN_GPIO GPIO_PP5000_FAN_EN -#define CONFIG_PWM_FAN_RPM_MIN 1000 -#define CONFIG_PWM_FAN_RPM_MAX 5050 -#define CONFIG_PWM_FAN_POWER_GOOD GPIO_PP5000_PGOOD +#define CONFIG_PWM #define CONFIG_TEMP_SENSOR #define CONFIG_TEMP_SENSOR_G781 #define CONFIG_UART_HOST 2 @@ -181,6 +182,13 @@ enum adc_channel { ADC_CH_COUNT }; +enum pwm_channel { + PWM_CH_FAN, + + /* Number of PWM channels */ + PWM_CH_COUNT +}; + enum temp_sensor_id { /* CPU die temperature via PECI */ TEMP_SENSOR_CPU_PECI = 0, diff --git a/board/pit/board.c b/board/pit/board.c index 6a5dc07c15..da85dafcb8 100644 --- a/board/pit/board.c +++ b/board/pit/board.c @@ -13,6 +13,8 @@ #include "keyboard_raw.h" #include "lid_switch.h" #include "pmu_tpschrome.h" +#include "pwm.h" +#include "pwm_data.h" #include "registers.h" #include "spi.h" #include "task.h" @@ -107,6 +109,13 @@ const struct i2c_port_t i2c_ports[] = { }; BUILD_ASSERT(ARRAY_SIZE(i2c_ports) == I2C_PORTS_USED); +/* PWM channels */ +const struct pwm_t pwm_channels[] = { + [PWM_CH_POWER_LED] = {STM32_TIM(2), STM32_TIM_CH(3), + PWM_CONFIG_ACTIVE_LOW, GPIO_LED_POWER_L}, +}; +BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); + int pmu_board_init(void) { int ver, failure = 0; diff --git a/board/pit/board.h b/board/pit/board.h index c9f4e5d51e..89c54951b9 100644 --- a/board/pit/board.h +++ b/board/pit/board.h @@ -21,6 +21,7 @@ #define CONFIG_PMU_POWERINFO #define CONFIG_PMU_TPS65090 #define CONFIG_SPI +#define CONFIG_PWM #ifndef __ASSEMBLER__ @@ -120,6 +121,12 @@ enum gpio_signal { GPIO_COUNT }; +enum pwm_channel { + PWM_CH_POWER_LED = 0, + /* Number of PWM channels */ + PWM_CH_COUNT +}; + #endif /* !__ASSEMBLER__ */ #endif /* __BOARD_H */ diff --git a/board/puppy/board.c b/board/puppy/board.c index be011561ac..3570789c50 100644 --- a/board/puppy/board.c +++ b/board/puppy/board.c @@ -13,6 +13,8 @@ #include "keyboard_raw.h" #include "lid_switch.h" #include "pmu_tpschrome.h" +#include "pwm.h" +#include "pwm_data.h" #include "registers.h" #include "spi.h" #include "task.h" @@ -107,6 +109,13 @@ const struct i2c_port_t i2c_ports[] = { }; BUILD_ASSERT(ARRAY_SIZE(i2c_ports) == I2C_PORTS_USED); +/* PWM channels */ +const struct pwm_t pwm_channels[] = { + [PWM_CH_POWER_LED] = {STM32_TIM(2), STM32_TIM_CH(3), + PWM_CONFIG_ACTIVE_LOW, GPIO_LED_POWER_L}, +}; +BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); + int pmu_board_init(void) { int ver, failure = 0; diff --git a/board/puppy/board.h b/board/puppy/board.h index d6f6de4480..f3b1135852 100644 --- a/board/puppy/board.h +++ b/board/puppy/board.h @@ -21,6 +21,7 @@ #define CONFIG_PMU_HARD_RESET #define CONFIG_PMU_TPS65090 #define CONFIG_SPI +#define CONFIG_PWM #ifndef __ASSEMBLER__ @@ -116,6 +117,12 @@ enum gpio_signal { GPIO_COUNT }; +enum pwm_channel { + PWM_CH_POWER_LED = 0, + /* Number of PWM channels */ + PWM_CH_COUNT +}; + #endif /* !__ASSEMBLER__ */ #endif /* __BOARD_H */ diff --git a/board/slippy/board.c b/board/slippy/board.c index 12de82d8a6..2ed4320902 100644 --- a/board/slippy/board.c +++ b/board/slippy/board.c @@ -20,6 +20,8 @@ #include "lm4_adc.h" #include "peci.h" #include "power_button.h" +#include "pwm.h" +#include "pwm_data.h" #include "registers.h" #include "switch.h" #include "temp_sensor.h" @@ -172,6 +174,12 @@ const struct adc_t adc_channels[] = { }; BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT); +/* PWM channels */ +const struct pwm_t pwm_channels[] = { + [PWM_CH_FAN] = {FAN_CH_CPU, PWM_CONFIG_HAS_RPM_MODE}, +}; +BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); + /* I2C ports */ const struct i2c_port_t i2c_ports[] = { /* Note: battery and charger share a port. Only include it once in diff --git a/board/slippy/board.h b/board/slippy/board.h index 0047aa58d0..ace4cdd464 100644 --- a/board/slippy/board.h +++ b/board/slippy/board.h @@ -19,15 +19,16 @@ #define CONFIG_CHIPSET_HASWELL #define CONFIG_CHIPSET_X86 #define CONFIG_EXTPOWER_GPIO +#define CONFIG_FAN +#define CONFIG_FAN_RPM_MIN 1000 +#define CONFIG_FAN_RPM_MAX 5050 +#define CONFIG_FAN_POWER_GOOD GPIO_PP5000_PGOOD #define CONFIG_KEYBOARD_BOARD_CONFIG #define CONFIG_KEYBOARD_PROTOCOL_8042 #define CONFIG_LED_SLIPPY #define CONFIG_POWER_BUTTON #define CONFIG_POWER_BUTTON_X86 -#define CONFIG_PWM_FAN -#define CONFIG_PWM_FAN_RPM_MIN 1000 -#define CONFIG_PWM_FAN_RPM_MAX 5050 -#define CONFIG_PWM_FAN_POWER_GOOD GPIO_PP5000_PGOOD +#define CONFIG_PWM #define CONFIG_TEMP_SENSOR #define CONFIG_TEMP_SENSOR_G781 #define CONFIG_UART_HOST 2 @@ -175,6 +176,13 @@ enum adc_channel { ADC_CH_COUNT }; +enum pwm_channel { + PWM_CH_FAN, + + /* Number of PWM channels */ + PWM_CH_COUNT +}; + enum temp_sensor_id { /* CPU die temperature via PECI */ TEMP_SENSOR_CPU_PECI = 0, diff --git a/board/snow/board.c b/board/snow/board.c index 55e8012972..8a4e89667e 100644 --- a/board/snow/board.c +++ b/board/snow/board.c @@ -17,6 +17,8 @@ #include "lid_switch.h" #include "pmu_tpschrome.h" #include "power_led.h" +#include "pwm.h" +#include "pwm_data.h" #include "registers.h" #include "spi.h" #include "task.h" @@ -120,6 +122,13 @@ const struct i2c_port_t i2c_ports[] = { }; BUILD_ASSERT(ARRAY_SIZE(i2c_ports) == I2C_PORTS_USED); +/* PWM channels */ +const struct pwm_t pwm_channels[] = { + [PWM_CH_POWER_LED] = {STM32_TIM(2), STM32_TIM_CH(2), + PWM_CONFIG_ACTIVE_LOW, GPIO_LED_POWER_L}, +}; +BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); + void board_config_pre_init(void) { uint32_t val; diff --git a/board/snow/board.h b/board/snow/board.h index 2c56d9d02a..363a0490a6 100644 --- a/board/snow/board.h +++ b/board/snow/board.h @@ -28,6 +28,7 @@ #define CONFIG_KEYBOARD_SUPPRESS_NOISE #define CONFIG_PMU_HARD_RESET #define CONFIG_PMU_TPS65090 +#define CONFIG_PWM /* use STOP mode when we have nothing to do */ #define CONFIG_LOW_POWER_IDLE @@ -120,6 +121,12 @@ enum gpio_signal { GPIO_COUNT }; +enum pwm_channel { + PWM_CH_POWER_LED = 0, + /* Number of PWM channels */ + PWM_CH_COUNT +}; + #endif /* !__ASSEMBLER__ */ #endif /* __BOARD_H */ diff --git a/board/spring/board.c b/board/spring/board.c index 44c231ad79..31c5484b7e 100644 --- a/board/spring/board.c +++ b/board/spring/board.c @@ -19,6 +19,8 @@ #include "keyboard_raw.h" #include "lid_switch.h" #include "pmu_tpschrome.h" +#include "pwm.h" +#include "pwm_data.h" #include "registers.h" #include "stm32_adc.h" #include "timer.h" @@ -129,6 +131,13 @@ const struct adc_t adc_channels[] = { }; BUILD_ASSERT(ARRAY_SIZE(adc_channels) == ADC_CH_COUNT); +/* PWM channels */ +const struct pwm_t pwm_channels[] = { + [PWM_CH_ILIM] = {STM32_TIM(3), STM32_TIM_CH(1), 0, + GPIO_ILIM}, +}; +BUILD_ASSERT(ARRAY_SIZE(pwm_channels) == PWM_CH_COUNT); + /* I2C ports */ const struct i2c_port_t i2c_ports[] = { {"host", I2C_PORT_HOST, 100}, diff --git a/board/spring/board.h b/board/spring/board.h index 2046ed9fd9..d95bfce81e 100644 --- a/board/spring/board.h +++ b/board/spring/board.h @@ -33,6 +33,7 @@ #define CONFIG_PMU_HARD_RESET #define CONFIG_PMU_TPS65090 #define CONFIG_USB_SWITCH_TSU6721 +#define CONFIG_PWM #ifndef __ASSEMBLER__ @@ -41,6 +42,7 @@ enum module_id { MODULE_I2C, MODULE_UART, + MODULE_EXTPOWER_USB, }; /* By default, enable all console messages except keyboard */ @@ -73,6 +75,13 @@ enum adc_channel { ADC_CH_COUNT }; +/* PWM signal */ +enum pwm_channel { + PWM_CH_ILIM = 0, + /* Number of PWM channels */ + PWM_CH_COUNT +}; + /* GPIO signal list */ enum gpio_signal { /* Inputs with interrupt handlers are first for efficiency */ 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 diff --git a/common/build.mk b/common/build.mk index 3594cbf780..7b5415153f 100644 --- a/common/build.mk +++ b/common/build.mk @@ -54,7 +54,7 @@ common-$(CONFIG_ONEWIRE)+=onewire.o common-$(CONFIG_POWER_BUTTON)+=power_button.o common-$(CONFIG_POWER_BUTTON_X86)+=power_button_x86.o common-$(CONFIG_PSTORE)+=pstore_commands.o -common-$(CONFIG_PWM_FAN)+=pwm_fan.o +common-$(CONFIG_FAN)+=pwm_fan.o common-$(CONFIG_REGULATOR_IR357X)+=regulator_ir357x.o common-$(CONFIG_SWITCH)+=switch.o common-$(CONFIG_WIRELESS)+=wireless.o diff --git a/common/extpower_usb.c b/common/extpower_usb.c index 7a89f37510..465c8ee651 100644 --- a/common/extpower_usb.c +++ b/common/extpower_usb.c @@ -15,6 +15,7 @@ #include "host_command.h" #include "keyboard_protocol.h" #include "pmu_tpschrome.h" +#include "pwm.h" #include "registers.h" #include "smart_battery.h" #include "stm32_adc.h" @@ -181,56 +182,10 @@ static void set_video_power(int enabled) static void ilim_use_gpio(void) { - /* Disable counter */ - STM32_TIM_CR1(3) &= ~0x1; - - /* Disable TIM3 clock */ - STM32_RCC_APB1ENR &= ~0x2; - - /* Switch to GPIO */ + pwm_enable(PWM_CH_ILIM, 0); gpio_set_flags(GPIO_ILIM, GPIO_OUTPUT); } -static void ilim_use_pwm(void) -{ - uint32_t val; - - /* Config alt. function (TIM3/PWM) */ - val = STM32_GPIO_CRL(GPIO_B) & ~0x000f0000; - val |= 0x00090000; - STM32_GPIO_CRL(GPIO_B) = val; - - /* Enable TIM3 clock */ - STM32_RCC_APB1ENR |= 0x2; - - /* Disable counter during setup */ - STM32_TIM_CR1(3) = 0x0000; - - /* - * CPU_CLOCK / (PSC + 1) determines how fast the counter operates. - * ARR determines the wave period, CCRn determines duty cycle. - * Thus, frequency = CPU_CLOCK / (PSC + 1) / ARR. - * - * Assuming 16MHz clock and ARR=100, PSC needed to achieve PWM_FREQUENCY - * is: PSC = CPU_CLOCK / PWM_FREQUENCY / ARR - 1 - */ - STM32_TIM_PSC(3) = CPU_CLOCK / PWM_FREQUENCY / 100 - 1; /* pre-scaler */ - STM32_TIM_ARR(3) = 100; /* auto-reload value */ - STM32_TIM_CCR1(3) = 100; /* duty cycle */ - - /* CC1 configured as output, PWM mode 1, preload enable */ - STM32_TIM_CCMR1(3) = (6 << 4) | (1 << 3); - - /* CC1 output enable, active high */ - STM32_TIM_CCER(3) = (1 << 0); - - /* Generate update event to force loading of shadow registers */ - STM32_TIM_EGR(3) |= 1; - - /* Enable auto-reload preload, start counting */ - STM32_TIM_CR1(3) |= (1 << 7) | (1 << 0); -} - /** * Set ILIM pin control type. */ @@ -248,7 +203,7 @@ static void ilim_config(enum ilim_config config) config == ILIM_CONFIG_MANUAL_ON ? 1 : 0); break; case ILIM_CONFIG_PWM: - ilim_use_pwm(); + pwm_enable(PWM_CH_ILIM, 1); break; default: break; @@ -360,7 +315,7 @@ static void set_pwm_duty_cycle(int percent) percent = 0; if (percent > 100) percent = 100; - STM32_TIM_CCR1(3) = (percent * STM32_TIM_ARR(3)) / 100; + pwm_set_duty(PWM_CH_ILIM, percent); current_pwm_duty = percent; } @@ -936,7 +891,8 @@ static int command_ilim(int argc, char **argv) else if (current_ilim_config == ILIM_CONFIG_MANUAL_OFF) ccprintf("ILIM is GPIO low\n"); else - ccprintf("ILIM is PWM duty cycle %d%%\n", STM32_TIM_CCR1(3)); + ccprintf("ILIM is PWM duty cycle %d%%\n", + pwm_get_duty(PWM_CH_ILIM)); return EC_SUCCESS; } @@ -957,7 +913,7 @@ static int command_batdebug(int argc, char **argv) * 17000 / 1024); ccprintf("IBAT = %d mA\n", pmu_adc_read(ADC_IBAT, 0) * (1000 / R_BATTERY_MOHM) * 40 / 1024); - ccprintf("PWM = %d%%\n", STM32_TIM_CCR1(3)); + ccprintf("PWM = %d%%\n", pwm_get_duty(PWM_CH_ILIM)); battery_current(&val); ccprintf("Battery Current = %d mA\n", val); battery_voltage(&val); diff --git a/common/lightbar.c b/common/lightbar.c index f729318c16..7471e523d7 100644 --- a/common/lightbar.c +++ b/common/lightbar.c @@ -295,8 +295,8 @@ static void get_battery_level(void) * when ambient is bright), use max brightness for lightbar. If * keyboard backlight is ON, use keyboard backlight brightness. */ - if (pwm_get_keyboard_backlight_enabled()) { - pct = pwm_get_keyboard_backlight(); + if (pwm_get_enabled(PWM_CH_KBLIGHT)) { + pct = pwm_get_duty(PWM_CH_KBLIGHT); pct = (255 * pct) / 100; /* 00 - FF */ if (pct > st.p.bright_bl_on_max[st.battery_is_charging]) pct = st.p.bright_bl_on_max[st.battery_is_charging]; diff --git a/common/pwm_fan.c b/common/pwm_fan.c index 758ee94ea7..f9198b1274 100644 --- a/common/pwm_fan.c +++ b/common/pwm_fan.c @@ -6,7 +6,7 @@ #include "common.h" #include "fan.h" -#ifndef CONFIG_PWM_FAN_RPM_CUSTOM +#ifndef CONFIG_FAN_RPM_CUSTOM /* This is the default implementation. It's only called over [0,100]. * Convert the percentage to a target RPM. We can't simply scale all * the way down to zero because most fans won't turn that slowly, so @@ -19,8 +19,8 @@ int pwm_fan_percent_to_rpm(int pct) if (!pct) rpm = 0; else - rpm = ((pct - 1) * CONFIG_PWM_FAN_RPM_MAX + - (100 - pct) * CONFIG_PWM_FAN_RPM_MIN) / 99; + rpm = ((pct - 1) * CONFIG_FAN_RPM_MAX + + (100 - pct) * CONFIG_FAN_RPM_MIN) / 99; return rpm; } diff --git a/include/config.h b/include/config.h index 9bdbc0b57d..c6e6f6512f 100644 --- a/include/config.h +++ b/include/config.h @@ -283,6 +283,30 @@ #undef CONFIG_EXTPOWER_USB /*****************************************************************************/ +/* Compile support for PWM control of cooling fans */ +#undef CONFIG_FAN + +/* Name of active high GPIO to control power to the cooling fan */ +#undef CONFIG_FAN_EN_GPIO + +/* Fan speeds corresponding to 1% and 100% cooling (0% == off). */ +#undef CONFIG_FAN_RPM_MIN +#undef CONFIG_FAN_RPM_MAX + +/* Alternately, define this to replace the default mapping with your own + * board-specific function in board.c: + * + * int pwm_fan_percent_to_rpm(int pct); + * + */ +#undef CONFIG_FAN_RPM_CUSTOM + +/* If you define this, the "faninfo" console command will read the GPIO to + * display the state of the fan's power rail. + */ +#undef CONFIG_FAN_POWER_GOOD + +/*****************************************************************************/ /* Flash configuration */ /* Compile support for programming on-chip flash */ @@ -466,28 +490,8 @@ #undef CONFIG_PSTORE /*****************************************************************************/ -/* Compile support for PWM control of cooling fans */ -#undef CONFIG_PWM_FAN - -/* Name of active high GPIO to control power to the cooling fan */ -#undef CONFIG_PWM_FAN_EN_GPIO - -/* Fan speeds corresponding to 1% and 100% cooling (0% == off). */ -#undef CONFIG_PWM_FAN_RPM_MIN -#undef CONFIG_PWM_FAN_RPM_MAX - -/* Alternately, define this to replace the default mapping with your own - * board-specific function in board.c: - * - * int pwm_fan_percent_to_rpm(int pct); - * - */ -#undef CONFIG_PWM_FAN_RPM_CUSTOM - -/* If you define this, the "faninfo" console command will read the GPIO to - * display the state of the fan's power rail. - */ -#undef CONFIG_PWM_FAN_POWER_GOOD +/* Compile support for PWM control */ +#undef CONFIG_PWM /*****************************************************************************/ /* Compile support for PWM output to keyboard backlight */ diff --git a/include/pwm.h b/include/pwm.h index 4f399832c2..cb0f961713 100644 --- a/include/pwm.h +++ b/include/pwm.h @@ -7,28 +7,36 @@ #define __CROS_EC_PWM_H /** - * Set the fan PWM duty cycle (0-100), disabling the automatic control. + * Enable/disable a PWM channel. */ -void pwm_set_fan_duty(int percent); +void pwm_enable(enum pwm_channel ch, int enabled); /** - * Enable/disable the keyboard backlight. + * Get PWM channel enabled status. */ -void pwm_enable_keyboard_backlight(int enable); +int pwm_get_enabled(enum pwm_channel ch); /** - * Get the keyboard backlight enable/disable status (1=enabled, 0=disabled). + * Set PWM channel duty cycle (0-100). */ -int pwm_get_keyboard_backlight_enabled(void); +void pwm_set_duty(enum pwm_channel ch, int percent); /** - * Get the keyboard backlight percentage (0=off, 100=max). + * Get PWM channel duty cycle. */ -int pwm_get_keyboard_backlight(void); +int pwm_get_duty(enum pwm_channel ch); + +/* Flags for PWM config table */ + +/** + * PWM output signal is inverted, so 100% duty means always low + */ +#define PWM_CONFIG_ACTIVE_LOW (1 << 0) /** - * Set the keyboard backlight percentage (0=off, 100=max). + * PWM channel has a fan controller with a tach input and can auto-adjust + * its duty cycle to produce a given fan RPM. */ -void pwm_set_keyboard_backlight(int percent); +#define PWM_CONFIG_HAS_RPM_MODE (1 << 1) #endif /* __CROS_EC_PWM_H */ |