ec_chip_mchp: Add PWM and fan files

Add Microchip MEC17xx family PWM and fan
source files for review

BRANCH=none
BUG=
TEST=Review only.

Change-Id: I91439ab999a4662d690b58b0fbbb887f643b3673
Signed-off-by: Scott Worley <scott.worley@microchip.corp-partner.google.com>
This commit is contained in:
Scott Worley
2017-12-21 14:13:47 -05:00
committed by chrome-bot
parent 0a6a7be572
commit eb29ab7acd
3 changed files with 342 additions and 0 deletions

164
chip/mchp/fan.c Normal file
View File

@@ -0,0 +1,164 @@
/* Copyright 2017 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.
*/
/* MCHP MEC fan control module. */
/* This assumes 2-pole fan. For each rotation, 5 edges are measured. */
#include "fan.h"
#include "registers.h"
#include "util.h"
#include "tfdp_chip.h"
/* Maximum tach reading/target value */
#define MAX_TACH 0x1fff
/* Tach target value for disable fan */
#define FAN_OFF_TACH 0xfff8
/*
* RPM = (n - 1) * m * f * 60 / poles / TACH
* n = number of edges = 5
* m = multiplier defined by RANGE = 2 in our case
* f = 32.768K
* poles = 2
*/
#define RPM_TO_TACH(rpm) MIN((7864320 / MAX((rpm), 1)), MAX_TACH)
#define TACH_TO_RPM(tach) (7864320 / MAX((tach), 1))
static int rpm_setting;
static int duty_setting;
static int in_rpm_mode = 1;
static void clear_status(void)
{
/* Clear DRIVE_FAIL, FAN_SPIN, and FAN_STALL bits */
MCHP_FAN_STATUS(0) = 0x23;
}
void fan_set_enabled(int ch, int enabled)
{
if (in_rpm_mode) {
if (enabled)
fan_set_rpm_target(ch, rpm_setting);
else
MCHP_FAN_TARGET(0) = FAN_OFF_TACH;
} else {
if (enabled)
fan_set_duty(ch, duty_setting);
else
MCHP_FAN_SETTING(0) = 0;
}
clear_status();
}
int fan_get_enabled(int ch)
{
if (in_rpm_mode)
return (MCHP_FAN_TARGET(0) & 0xff00) != 0xff00;
else
return !!MCHP_FAN_SETTING(0);
}
void fan_set_duty(int ch, int percent)
{
if (percent < 0)
percent = 0;
else if (percent > 100)
percent = 100;
duty_setting = percent;
MCHP_FAN_SETTING(0) = percent * 255 / 100;
clear_status();
}
int fan_get_duty(int ch)
{
return duty_setting;
}
int fan_get_rpm_mode(int ch)
{
return !!(MCHP_FAN_CFG1(0) & (1 << 7));
}
void fan_set_rpm_mode(int ch, int rpm_mode)
{
if (rpm_mode)
MCHP_FAN_CFG1(0) |= 1 << 7;
else
MCHP_FAN_CFG1(0) &= ~(1 << 7);
clear_status();
}
int fan_get_rpm_actual(int ch)
{
if ((MCHP_FAN_READING(0) >> 8) == 0xff)
return 0;
else
return TACH_TO_RPM(MCHP_FAN_READING(0) >> 3);
}
int fan_get_rpm_target(int ch)
{
return rpm_setting;
}
void fan_set_rpm_target(int ch, int rpm)
{
rpm_setting = rpm;
MCHP_FAN_TARGET(0) = RPM_TO_TACH(rpm) << 3;
clear_status();
}
enum fan_status fan_get_status(int ch)
{
uint8_t sts = MCHP_FAN_STATUS(0);
if (sts & ((1 << 5) | (1 << 1)))
return FAN_STATUS_FRUSTRATED;
if (fan_get_rpm_actual(ch) == 0)
return FAN_STATUS_STOPPED;
return FAN_STATUS_LOCKED;
}
int fan_is_stalled(int ch)
{
uint8_t sts = MCHP_FAN_STATUS(0);
if (fan_get_rpm_actual(ch)) {
MCHP_FAN_STATUS(0) = 0x1;
return 0;
}
return sts & 0x1;
}
void fan_channel_setup(int ch, unsigned int flags)
{
/* Clear PCR sleep enable for RPM2FAN0 */
MCHP_PCR_SLP_DIS_DEV(MCHP_PCR_RPMPWM0);
/*
* Fan configuration 1 register:
* 0x80 = bit 7 = RPM mode (0x00 if FAN_USE_RPM_MODE not set)
* 0x20 = bits 6:5 = min 1000 RPM, multiplier = 2
* 0x08 = bits 4:3 = 5 edges, 2 poles
* 0x03 = bits 2:0 = 400 ms update time
*
* Fan configuration 2 register:
* 0x00 = bit 6 = Ramp control disabled
* 0x00 = bit 5 = Glitch filter enabled
* 0x18 = bits 4:3 = Using both derivative options
* 0x02 = bits 2:1 = error range is 50 RPM
* 0x00 = bits 0 = normal polarity
*/
if (flags & FAN_USE_RPM_MODE)
MCHP_FAN_CFG1(0) = 0xab;
else
MCHP_FAN_CFG1(0) = 0x2b;
MCHP_FAN_CFG2(0) = 0x1a;
clear_status();
}

155
chip/mchp/pwm.c Normal file
View File

@@ -0,0 +1,155 @@
/* Copyright 2017 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 MCHP MEC family */
#include "common.h"
#include "console.h"
#include "gpio.h"
#include "hooks.h"
#include "pwm.h"
#include "pwm_chip.h"
#include "registers.h"
#include "util.h"
#include "tfdp_chip.h"
#define CPUTS(outstr) cputs(CC_PWM, outstr)
#define CPRINTS(format, args...) cprints(CC_PWM, format, ## args)
/*
* PWMs that must remain active in low-power idle -
* PWM 0,1-8 are b[4,20:27] of MCHP_PCR_SLP_EN1
* PWM 9 is b[31] of MCHP_PCR_SLP_EN3
* PWM 10 - 11 are b[0:1] of MCHP_PCR_SLP_EN4
* store 32-bit word with
* b[0:1] = PWM 10-11
* b[4,20:27] = PWM 0, 1-8
* b[31] = PWM 9
*/
static uint32_t pwm_keep_awake_mask;
const uint8_t pwm_slp_bitpos[12] = {
4, 20, 21, 22, 23, 24, 25, 26, 27, 31, 0, 1
};
static uint32_t pwm_get_sleep_mask(int id)
{
uint32_t bitpos = 32;
if (id < 12)
bitpos = (uint32_t)pwm_slp_bitpos[id];
return (1ul << bitpos);
}
void pwm_enable(enum pwm_channel ch, int enabled)
{
int id = pwm_channels[ch].channel;
uint32_t pwm_slp_mask;
pwm_slp_mask = pwm_get_sleep_mask(id);
if (enabled) {
MCHP_PWM_CFG(id) |= 0x1;
if (pwm_channels[ch].flags & PWM_CONFIG_DSLEEP)
pwm_keep_awake_mask |= pwm_slp_mask;
} else {
MCHP_PWM_CFG(id) &= ~0x1;
pwm_keep_awake_mask &= ~pwm_slp_mask;
}
}
int pwm_get_enabled(enum pwm_channel ch)
{
return MCHP_PWM_CFG(pwm_channels[ch].channel) & 0x1;
}
void pwm_set_duty(enum pwm_channel ch, int percent)
{
int id = pwm_channels[ch].channel;
if (percent < 0)
percent = 0;
else if (percent > 100)
percent = 100;
MCHP_PWM_ON(id) = percent;
MCHP_PWM_OFF(id) = 100 - percent;
}
int pwm_get_duty(enum pwm_channel ch)
{
return MCHP_PWM_ON(pwm_channels[ch].channel);
}
void pwm_keep_awake(void)
{
if (pwm_keep_awake_mask) {
/* b[4,20:27] */
MCHP_PCR_SLP_EN1 &= ~(pwm_keep_awake_mask &
(MCHP_PCR_SLP_EN1_PWM_ALL));
/* b[31] */
MCHP_PCR_SLP_EN3 &= ~(pwm_keep_awake_mask &
(MCHP_PCR_SLP_EN3_PWM_ALL));
/* b[1:0] */
MCHP_PCR_SLP_EN4 &= ~(pwm_keep_awake_mask &
(MCHP_PCR_SLP_EN4_PWM_ALL));
} else {
MCHP_PCR_SLOW_CLK_CTL &= 0xFFFFFC00;
}
}
static void pwm_configure(int ch, int active_low, int clock_low)
{
/*
* clock_low=0 selects the 48MHz Ring Oscillator source
* clock_low=1 selects the 100kHz_Clk source
*/
MCHP_PWM_CFG(ch) = (15 << 3) | /* Pre-divider = 16 */
(active_low ? (1 << 2) : 0) |
(clock_low ? (1 << 1) : 0);
}
static const uint16_t pwm_pcr[MCHP_PWM_ID_MAX] = {
MCHP_PCR_PWM0,
MCHP_PCR_PWM1,
MCHP_PCR_PWM2,
MCHP_PCR_PWM3,
MCHP_PCR_PWM4,
MCHP_PCR_PWM5,
MCHP_PCR_PWM6,
MCHP_PCR_PWM7,
MCHP_PCR_PWM8,
MCHP_PCR_PWM9,
MCHP_PCR_PWM10,
MCHP_PCR_PWM11,
};
static void pwm_slp_en(int pwm_id, int sleep_en)
{
if ((pwm_id < 0) || (pwm_id > MCHP_PWM_ID_MAX))
return;
if (sleep_en)
MCHP_PCR_SLP_EN_DEV(pwm_pcr[pwm_id]);
else
MCHP_PCR_SLP_DIS_DEV(pwm_pcr[pwm_id]);
}
static void pwm_init(void)
{
int i;
for (i = 0; i < PWM_CH_COUNT; ++i) {
pwm_slp_en(pwm_channels[i].channel, 0);
pwm_configure(pwm_channels[i].channel,
pwm_channels[i].flags & PWM_CONFIG_ACTIVE_LOW,
pwm_channels[i].flags & PWM_CONFIG_ALT_CLOCK);
pwm_set_duty(i, 0);
}
}
DECLARE_HOOK(HOOK_INIT, pwm_init, HOOK_PRIO_DEFAULT);

23
chip/mchp/pwm_chip.h Normal file
View File

@@ -0,0 +1,23 @@
/* Copyright 2017 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.
*/
/* MEC1701H-specific PWM module for Chrome EC */
#ifndef __CROS_EC_PWM_CHIP_H
#define __CROS_EC_PWM_CHIP_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[];
void pwm_keep_awake(void);
#endif /* __CROS_EC_PWM_CHIP_H */