mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-04 14:01:54 +00:00
Add npcx_evb_arm board-level driver for arm-based platform. Add header.c: for booting from NPCX5M5G A3 Booter. Remove lfw folder due to those functionalitie have been replaced with Booter Modified drivers for Patch Set 1: 1. flash.c: Implement UMA lock, tri-state and selection register lock functionalities 2. hwtimer.c: Add ITIM32 for hwtimer 3. lpc.c: Add checking for LRESET 4. system.c: Modified CODERAM_ARCH functions for NPCX5M5G A3 Booter. 5. uart.c: Add support for module 2 Patch Set 2: 6. lpc.c: Modified lpc_get_pltrst_asserted() func Patch Set 3: 7. minimize the changes for CONFIG_CODERAM_ARCH in common layer 8. comments of Patch Set1/2 Patch Set 4: 9. Modified CONFIG_RO_MEM_OFF point to ro image and keep header as a part of ec.RO.flat. 10. Fixed RO_FRID and RW_FRID issues which caused by CONFIG_CODERAM_ARCH. Patch Set 5: 11. Modified system.c in common folder for supporting *_STORAGE_OFF. 12. Use *_STORAGE_OFF in firmware_image.lds.S to indicate flat file layout in flash. Patch Set 6: 13. rebase to newest version 14. system.c: Modified for the newest include/system.h Patch Set 7: 15. Merge from version 0625 BUG=chrome-os-partner:34346 TEST=make buildall -j; test nuvoton IC specific drivers BRANCH=none Change-Id: Ifd7c10b81b5781ccd75bb2558dc236486976e8ed Signed-off-by: Ian Chao <mlchao@nuvoton.com> Reviewed-on: https://chromium-review.googlesource.com/272034 Reviewed-by: Shawn N <shawnn@chromium.org> Tested-by: Shawn N <shawnn@chromium.org> Commit-Queue: Shawn N <shawnn@chromium.org>
270 lines
6.6 KiB
C
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 NPCX_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 NPCX_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);
|