summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/bds/board.h4
-rw-r--r--board/bolt/board.c8
-rw-r--r--board/bolt/board.h18
-rw-r--r--board/falco/board.c8
-rw-r--r--board/falco/board.h16
-rw-r--r--board/host/board.h7
-rw-r--r--board/link/board.c8
-rw-r--r--board/link/board.h17
-rw-r--r--board/peppy/board.c8
-rw-r--r--board/peppy/board.h18
-rw-r--r--board/pit/board.c9
-rw-r--r--board/pit/board.h7
-rw-r--r--board/puppy/board.c9
-rw-r--r--board/puppy/board.h7
-rw-r--r--board/slippy/board.c8
-rw-r--r--board/slippy/board.h16
-rw-r--r--board/snow/board.c9
-rw-r--r--board/snow/board.h7
-rw-r--r--board/spring/board.c9
-rw-r--r--board/spring/board.h9
-rw-r--r--chip/lm4/build.mk3
-rw-r--r--chip/lm4/fan.c (renamed from chip/lm4/pwm_fan.c)64
-rw-r--r--chip/lm4/lpc.c4
-rw-r--r--chip/lm4/pwm.c112
-rw-r--r--chip/lm4/pwm_data.h21
-rw-r--r--chip/lm4/pwm_kblight.c79
-rw-r--r--chip/stm32/build.mk1
-rw-r--r--chip/stm32/power_led.c91
-rw-r--r--chip/stm32/pwm.c154
-rw-r--r--chip/stm32/pwm_data.h37
-rw-r--r--chip/stm32/registers.h11
-rw-r--r--common/build.mk2
-rw-r--r--common/extpower_usb.c58
-rw-r--r--common/lightbar.c4
-rw-r--r--common/pwm_fan.c6
-rw-r--r--include/config.h48
-rw-r--r--include/pwm.h28
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 */