forked from zephyrproject-rtos/zephyr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtach_ite_it8xxx2.c
249 lines (220 loc) · 8.67 KB
/
tach_ite_it8xxx2.c
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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/*
* Copyright (c) 2021 ITE Technology Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ite_it8xxx2_tach
/**
* @file
* @brief ITE it8xxx2 tachometer sensor module driver
*
* This file contains a driver for the tachometer sensor module which contains
* two independent counters (F1TL/MRR and F2TL/MRR). The content of the
* Tachometer Reading Register is still update based on the sampling counter
* that samples the tachometer input (T0A, T0B, T1A or T1B pins).
* The following is block diagram of this module:
*
* Sample Rate = TACH_FREQ / 128
* |
* | Tachometer 0 | T0A (GPD6)
* | | | +-----------+ |
* | +-----+-----+ | | _ _ |<--+
* |------>| F1TL/MRR |<-+-| | |_| |_ |<--+
* | +-----------+ +-----------+ |
* | capture pulses T0B (GPJ2)
* | in sample rate
* | period
* +-----------+ |
* Crystal-->| Prescaler |--->| Tachometer 1 T1A (GPD7)
* 32.768k +-----------+ | | +-----------+ |
* | +-----+-----+ | _ _ |<--+
* |------>| F2TL/MRR |<-+-| | |_| |_ |<--+
* | +-----------+ +-----------+ |
* | capture pulses T1B (GPJ3)
* | in one second
* | period
* |
*
* Based on the counter value, we can compute the current RPM of external signal
* from encoders.
*/
#include <zephyr/device.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/dt-bindings/sensor/it8xxx2_tach.h>
#include <errno.h>
#include <soc.h>
#include <soc_dt.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(tach_ite_it8xxx2, CONFIG_SENSOR_LOG_LEVEL);
/*
* NOTE: The PWM output maximum is 324Hz in EC LPM, so if we need fan to work
* then don't let EC enter LPM.
*/
#define TACH_FREQ EC_FREQ
struct tach_it8xxx2_config {
/* Fan x tachometer LSB reading register */
uintptr_t reg_fxtlrr;
/* Fan x tachometer MSB reading register */
uintptr_t reg_fxtmrr;
/* Tachometer switch control register */
uintptr_t reg_tswctlr;
/* Tachometer data valid bit of tswctlr register */
int dvs_bit;
/* Tachometer data valid status bit of tswctlr register */
int chsel_bit;
/* Tachometer alternate configuration */
const struct pinctrl_dev_config *pcfg;
/* Select channel of tachometer */
int channel;
/* Number of pulses per round of tachometer's input */
int pulses_per_round;
};
/* Driver data */
struct tach_it8xxx2_data {
/* Captured counts of tachometer */
uint32_t capture;
};
static bool tach_ch_is_valid(const struct device *dev, int tach_ch)
{
const struct tach_it8xxx2_config *const config = dev->config;
volatile uint8_t *reg_tswctlr = (uint8_t *)config->reg_tswctlr;
int dvs_bit = config->dvs_bit;
int chsel_bit = config->chsel_bit;
int mask = (dvs_bit | chsel_bit);
bool valid = false;
switch (tach_ch) {
case IT8XXX2_TACH_CHANNEL_A:
if ((*reg_tswctlr & mask) == dvs_bit) {
valid = true;
}
break;
case IT8XXX2_TACH_CHANNEL_B:
if ((*reg_tswctlr & mask) == mask) {
valid = true;
}
break;
default:
break;
}
return valid;
}
static int tach_it8xxx2_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
const struct tach_it8xxx2_config *const config = dev->config;
volatile uint8_t *reg_fxtlrr = (uint8_t *)config->reg_fxtlrr;
volatile uint8_t *reg_fxtmrr = (uint8_t *)config->reg_fxtmrr;
volatile uint8_t *reg_tswctlr = (uint8_t *)config->reg_tswctlr;
int tach_ch = config->channel;
struct tach_it8xxx2_data *const data = dev->data;
if ((chan != SENSOR_CHAN_RPM) && (chan != SENSOR_CHAN_ALL)) {
return -ENOTSUP;
}
if (tach_ch_is_valid(dev, tach_ch)) {
/* If channel data of tachometer is valid, then save it */
data->capture = ((*reg_fxtmrr) << 8) | (*reg_fxtlrr);
if (config->dvs_bit == IT8XXX2_PWM_T0DVS) {
/* Only W/C tach 0 data valid status */
*reg_tswctlr = (*reg_tswctlr & ~IT8XXX2_PWM_T1DVS);
} else {
/* Only W/C tach 1 data valid status */
*reg_tswctlr = (*reg_tswctlr & ~IT8XXX2_PWM_T0DVS);
}
} else {
/* If channel data of tachometer isn't valid, then clear it */
data->capture = 0;
}
return 0;
}
static int tach_it8xxx2_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
const struct tach_it8xxx2_config *const config = dev->config;
int tachx = ((config->dvs_bit) == IT8XXX2_PWM_T0DVS) ? 0 : 1;
int p = config->pulses_per_round;
struct tach_it8xxx2_data *const data = dev->data;
if (chan != SENSOR_CHAN_RPM) {
LOG_ERR("Sensor chan %d, only support SENSOR_CHAN_RPM", chan);
return -ENOTSUP;
}
/* Transform count unit to RPM */
if (data->capture > 0) {
if (tachx == 0) {
/*
* Fan Speed (RPM) = 60 / (1/fs * {F1TMRR, F1TLRR} * P)
* - P denotes the numbers of pulses per round
* - {F1TMRR, F1TLRR} = 0000h denotes Fan Speed is zero
* - The sampling rate (fs) is TACH_FREQ / 128
*/
val->val1 = (60 * TACH_FREQ / 128 / p / (data->capture));
} else {
/*
* Fan Speed (RPM) = {F2TMRR, F2TLRR} * 60 / P
* - P denotes the numbers of pulses per round
* - {F2TMRR, F2TLRR} = 0000h denotes Fan Speed is zero
*/
val->val1 = ((data->capture) * 120 / (p * 2));
}
} else {
val->val1 = 0U;
}
val->val2 = 0U;
return 0;
}
static int tach_it8xxx2_init(const struct device *dev)
{
const struct tach_it8xxx2_config *const config = dev->config;
volatile uint8_t *reg_tswctlr = (uint8_t *)config->reg_tswctlr;
int tach_ch = config->channel;
int status;
if (tach_ch > IT8XXX2_TACH_CHANNEL_B) {
LOG_ERR("Tach channel %d, only support 0 or 1", tach_ch);
return -EINVAL;
}
/* Select pin to alternate mode for tachometer */
status = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (status < 0) {
LOG_ERR("Failed to configure TACH pins");
return status;
}
if (tach_ch == IT8XXX2_TACH_CHANNEL_A) {
/* Select IT8XXX2_TACH_CHANNEL_A output to tachometer */
*reg_tswctlr &= ~(config->chsel_bit);
} else {
/* Select IT8XXX2_TACH_CHANNEL_B output to tachometer */
*reg_tswctlr |= config->chsel_bit;
}
if (config->dvs_bit == IT8XXX2_PWM_T0DVS) {
/* Only W/C tach 0 data valid status */
*reg_tswctlr = (*reg_tswctlr & ~IT8XXX2_PWM_T1DVS);
} else {
/* Only W/C tach 1 data valid status */
*reg_tswctlr = (*reg_tswctlr & ~IT8XXX2_PWM_T0DVS);
}
/* Tachometer sensor already start */
return 0;
}
static DEVICE_API(sensor, tach_it8xxx2_driver_api) = {
.sample_fetch = tach_it8xxx2_sample_fetch,
.channel_get = tach_it8xxx2_channel_get,
};
#define TACH_IT8XXX2_INIT(inst) \
PINCTRL_DT_INST_DEFINE(inst); \
\
static const struct tach_it8xxx2_config tach_it8xxx2_cfg_##inst = { \
.reg_fxtlrr = DT_INST_REG_ADDR_BY_IDX(inst, 0), \
.reg_fxtmrr = DT_INST_REG_ADDR_BY_IDX(inst, 1), \
.reg_tswctlr = DT_INST_REG_ADDR_BY_IDX(inst, 2), \
.dvs_bit = DT_INST_PROP(inst, dvs_bit), \
.chsel_bit = DT_INST_PROP(inst, chsel_bit), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
.channel = DT_INST_PROP(inst, channel), \
.pulses_per_round = DT_INST_PROP(inst, pulses_per_round), \
}; \
\
static struct tach_it8xxx2_data tach_it8xxx2_data_##inst; \
\
SENSOR_DEVICE_DT_INST_DEFINE(inst, tach_it8xxx2_init, NULL, &tach_it8xxx2_data_##inst, \
&tach_it8xxx2_cfg_##inst, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, &tach_it8xxx2_driver_api);
DT_INST_FOREACH_STATUS_OKAY(TACH_IT8XXX2_INIT)