Files
OpenCellular/chip/npcx/pwm.c
Randall Spangler c3cd10937e WIP - Nuvoton 30-Jan patch
Issues fixed on 0216:
1.Modified CONFIG_KEYBOARD_COL2_INVERTED support in keyboard_raw.c
2.Modified warm_reset checking in gpio.c
3.Modified system_get_chip_name in system.c for package info.
4.Modified fan.c and pwm.c for:
● If the DCRn value is greater than the CTRn value, the PWM_n signal is always low.
● Fan stall condition event:
  If the measured fan speed is lower than the lowLimit value (unless the Fan Speed Low Limit value is 0) or in case of erroneous measurement, the userCallback is called.
5. Change cycle_pluses to 480 in board.c

Issues fixed:
1. Jump data at top of RAM is getting corrupted.  Changed the flag to
RESET_FLAG_RESET_PIN.  Added a workaround method to fix VCC1_RST
issue.

2. Hibernate wake need to report whether wake reason was GPIO or RTC

3. Hibernate wake must be distinguishable from watchdog reset.  The
booter will log reset reason in Code RAM.  I copy the log data to
battery-backup RAM in little FW.  And system driver will refer this
data to distinguish if it's watchdog reset or not.

4. Watchdog reset flag is not set.  Same fix as 3.

5. Should return error if unable to clear SPI flash status register.

6. Remove chip_temp_sensor.c

7. Remove use of pstate from flash driver

8. Remove support for watchdog warm reset

9. Keyboard raw driver must support COL2 inverted

10. LPC memory mapped data must be read-only from host

11. LPC should support PLTRST# signal

12. Problems reading chip type/version.  Use core registers and ROM data to read IDs.

13. When chip type/version is unknown, report hex value.

14. Watchdog does not consistently print panic information.

15. Remove console force enable logic.

16. Enable only the peripheral clocks that are needed.  Please notice
user should add bit mask in CGC_XXX_MASK if they want to enable
additional module.  For example, if user wants to enable PWM3, he must
add PWDWN_CTL2_PWM3_PD bit in CGC_PWM_MASK.

Please see HOOK_FREQ_CHANGE and HOOK_INIT these two hook functions.
If I turn off all I2C modules in system_pre_init and turn on the
modules I need in i2c_init, I found its freq is not correct.  The root
cause is hook_notify(HOOK_FREQ_CHANGE) is executed first (in
clock_init) before i2c_init.  At this time, i2c modules are power-down
and writing to freq register is useless.  I re-execute freq-changed
hook function after turning on modules again.

17. MPU properly configured to prevent code execution from data RAM

18. Partial nvcontext implementation.  Copy these 16 bytes in our battery-backup RAM.

Additional items we also modified:

1. pwm.c: Support open-drain IO type of PWM.  (PWM IO-Type cannot by
determined by GPIO, we use bit 1 & 2 of function byte of gpio_alt_func
array to support it)

2. ec_npcxflash.c: Use definition to replace constant value.  Stop
watchdog during flash programing.

3. npcx_cmds.tcl: Adjust script sequence for robustness.  Add unlock
MPU commands for Data RAM.

BUG=chrome-os-partner:34346
BRANCH=none
TEST=manually verify changes

Change-Id: I722a77d29e7543b054819480c7b7477af4263119
Signed-off-by: Ian Chao <mlchao@nuvoton.com>
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/248670
2015-02-25 03:33:08 +00:00

270 lines
6.6 KiB
C

/* Copyright (c) 2014 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 NPCX.
*
* On this chip, the PWM logic is implemented by the hardware FAN modules.
*/
#include "clock.h"
#include "clock_chip.h"
#include "fan.h"
#include "gpio.h"
#include "hooks.h"
#include "pwm.h"
#include "pwm_chip.h"
#include "registers.h"
#include "util.h"
#include "console.h"
#if !(DEBUG_PWM)
#define CPRINTS(...)
#else
#define CPRINTS(format, args...) cprints(CC_PWM, format, ## args)
#endif
/* PWM clock source */
enum npcx_pwm_source_clock {
NPCX_PWM_CLOCK_APB2_LFCLK = 0,
NPCX_PWM_CLOCK_FX = 1,
NPCX_PWM_CLOCK_FR = 2,
NPCX_PWM_CLOCK_RESERVED = 0x3,
NPCX_PWM_CLOCK_UNDEF = 0xFF
};
/* PWM heartbeat mode */
enum npcx_pwm_heartbeat_mode {
NPCX_PWM_HBM_NORMAL = 0,
NPCX_PWM_HBM_25 = 1,
NPCX_PWM_HBM_50 = 2,
NPCX_PWM_HBM_100 = 3,
NPCX_PWM_HBM_UNDEF = 0xFF
};
/* Global variables */
static int pwm_init_ch;
/**
* Preset PWM operation clock.
*
* @param none
* @return none
* @notes changed when initial or HOOK_FREQ_CHANGE command
*/
void pwm_freq_changed(void)
{
uint32_t prescaler_divider = 0;
/* Disable PWM for module configuration */
pwm_enable(pwm_init_ch, 0);
if (pwm_init_ch == PWM_CH_FAN) {
/*
* Using PWM Frequency and Resolution we calculate
* prescaler for input clock
*/
#ifdef CONFIG_PWM_INPUT_LFCLK
prescaler_divider = (uint32_t)(32768 /
(pwm_channels[pwm_init_ch].freq)
/(pwm_channels[pwm_init_ch].cycle_pulses));
#else
prescaler_divider = (uint32_t)(
clock_get_apb2_freq() / pwm_channels[pwm_init_ch].freq
/ (pwm_channels[pwm_init_ch].cycle_pulses));
#endif
} else {
prescaler_divider = (uint32_t)(
clock_get_apb2_freq() / pwm_channels[pwm_init_ch].freq
/ (pwm_channels[pwm_init_ch].cycle_pulses));
}
/* Set clock prescalre divider to ADC module*/
if (prescaler_divider >= 1)
prescaler_divider = prescaler_divider - 1;
if (prescaler_divider > 0xFFFF)
prescaler_divider = 0xFFFF;
/* Configure computed prescaler and resolution */
NPCX_PRSC(pwm_channels[pwm_init_ch].channel) =
(uint16_t)prescaler_divider;
}
DECLARE_HOOK(HOOK_FREQ_CHANGE, pwm_freq_changed, HOOK_PRIO_DEFAULT);
/**
* Set PWM enabled.
*
* @param ch operation channel
* @param enabled enabled flag
* @return none
*/
void pwm_enable(enum pwm_channel ch, int enabled)
{
/* Start or close PWM module */
if (enabled)
SET_BIT(NPCX_PWMCTL(pwm_channels[ch].channel), NPCX_PWMCTL_PWR);
else
CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel),
NPCX_PWMCTL_PWR);
}
/**
* Check PWM enabled.
*
* @param ch operation channel
* @return enabled or not
*/
int pwm_get_enabled(enum pwm_channel ch)
{
return IS_BIT_SET(NPCX_PWMCTL(pwm_channels[ch].channel),
NPCX_PWMCTL_PWR);
}
/**
* Set PWM duty cycle.
*
* @param ch operation channel
* @param percent duty cycle percent
* @return none
*/
void pwm_set_duty(enum pwm_channel ch, int percent)
{
uint32_t resolution = 0;
uint16_t duty_cycle = 0;
CPRINTS("pwm0=%d", percent);
/* Assume the fan control is active high and invert it ourselves */
if (pwm_channels[ch].flags & PWM_CONFIG_ACTIVE_LOW)
SET_BIT(NPCX_PWMCTL(pwm_channels[ch].channel),
NPCX_PWMCTL_INVP);
else
CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel),
NPCX_PWMCTL_INVP);
if (percent < 0)
percent = 0;
else if (percent > 100)
percent = 100;
CPRINTS("pwm1duty=%d", percent);
resolution = NPCX_CTR(pwm_channels[ch].channel) + 1;
duty_cycle = percent*resolution/100;
CPRINTS("freq=0x%x", pwm_channels[ch].freq);
CPRINTS("resolution=%d", resolution);
CPRINTS("duty_cycle=%d", duty_cycle);
if (percent*resolution > (duty_cycle*100))
duty_cycle += 1;
/* Set the duty cycle */
if (duty_cycle > 0) {
NPCX_DCR(pwm_channels[ch].channel) = (duty_cycle - 1);
pwm_enable(ch, 1);
} else {
NPCX_DCR(pwm_channels[ch].channel) = resolution;
pwm_enable(ch, 0);
}
}
/**
* Get PWM duty cycle.
*
* @param ch operation channel
* @return duty cycle percent
*/
int pwm_get_duty(enum pwm_channel ch)
{
/* Return percent */
if ((0 == pwm_get_enabled(ch)) || (NPCX_DCR(pwm_channels[ch].channel)
> NPCX_CTR(pwm_channels[ch].channel)))
return 0;
else
return (((NPCX_DCR(pwm_channels[ch].channel) + 1) * 100)
/ (NPCX_CTR(pwm_channels[ch].channel) + 1));
}
/**
* PWM configuration.
*
* @param ch operation channel
* @return none
*/
void pwm_config(enum pwm_channel ch)
{
pwm_init_ch = ch;
/* Configure pins from GPIOs to PWM */
if (ch == PWM_CH_FAN)
gpio_config_module(MODULE_PWM_FAN, 1);
else
gpio_config_module(MODULE_PWM_KBLIGHT, 1);
/* Disable PWM for module configuration */
pwm_enable(ch, 0);
/* Set PWM heartbeat mode is no heartbeat*/
NPCX_PWMCTL(pwm_channels[ch].channel) =
(NPCX_PWMCTL(pwm_channels[ch].channel)
& (~(((1<<2)-1) << NPCX_PWMCTL_HB_DC_CTL)))
| (NPCX_PWM_HBM_NORMAL << NPCX_PWMCTL_HB_DC_CTL);
/* Set PWM operation frequence */
pwm_freq_changed();
/* Set PWM cycle time */
NPCX_CTR(pwm_channels[ch].channel) =
(pwm_channels[ch].cycle_pulses - 1);
/* Set the duty cycle */
NPCX_DCR(pwm_channels[ch].channel) = pwm_channels[ch].cycle_pulses;
/* Set PWM polarity is normal*/
CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel), NPCX_PWMCTL_INVP);
/* Select default CLK or LFCLK clock input to PWM module */
NPCX_PWMCTLEX(pwm_channels[ch].channel) =
(NPCX_PWMCTLEX(pwm_channels[ch].channel)
& (~(((1<<2)-1)<<NPCX_PWMCTLEX_FCK_SEL)))
| (NPCX_PWM_CLOCK_APB2_LFCLK<<NPCX_PWMCTLEX_FCK_SEL);
if (ch == PWM_CH_FAN) {
#ifdef CONFIG_PWM_INPUT_LFCLK
/* Select default LFCLK clock input to PWM module */
SET_BIT(NPCX_PWMCTL(pwm_channels[ch].channel),
NPCX_PWMCTL_CKSEL);
#else
/* Select default core clock input to PWM module */
CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel),
NPCX_PWMCTL_CKSEL);
#endif
} else {
/* Select default core clock input to PWM module */
CLEAR_BIT(NPCX_PWMCTL(pwm_channels[ch].channel),
NPCX_PWMCTL_CKSEL);
}
}
/**
* PWM initial.
*
* @param none
* @return none
*/
static void pwm_init(void)
{
int i;
#ifdef CONFIG_PWM_DSLEEP
/* Enable the PWM module and delay a few clocks */
clock_enable_peripheral(CGC_OFFSET_PWM, CGC_PWM_MASK, CGC_MODE_ALL);
#else
/* Enable the PWM module and delay a few clocks */
clock_enable_peripheral(CGC_OFFSET_PWM, CGC_PWM_MASK,
CGC_MODE_RUN | CGC_MODE_SLEEP);
#endif
for (i = 0; i < PWM_CH_COUNT; i++)
pwm_config(i);
}
/* The chip-specific fan module initializes before this. */
DECLARE_HOOK(HOOK_INIT, pwm_init, HOOK_PRIO_DEFAULT);