Files
OpenCellular/chip/npcx/pwm.c
Ian Chao 957638c78c nuc: Add SHI driver for arm-based platform in chip folder.
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>
2015-06-26 18:57:32 +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 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);