summaryrefslogtreecommitdiff
path: root/chip/lm4/pwm.c
blob: 6e52ebb36961c71f56a7106aa155ab0163f83cfd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/* 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_chip.h"
#include "registers.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;

	if (pwm->flags & PWM_CONFIG_ACTIVE_LOW)
		percent = 100 - percent;

	duty = (MAX_PWM * percent + 50) / 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;
	int percent = ((LM4_FAN_FANCMD(pwm->channel) >> 16) * 100 + MAX_PWM / 2)
		/ MAX_PWM;

	if (pwm->flags & PWM_CONFIG_ACTIVE_LOW)
		percent = 100 - percent;

	return percent;
}

static void pwm_init(void)
{
	int i;
	const struct pwm_t *pwm;

	/* Enable the fan module and delay a few clocks */
	clock_enable_peripheral(CGC_OFFSET_FAN, 0x1,
			CGC_MODE_RUN | CGC_MODE_SLEEP);

	/* 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_INIT_PWM);