-
Notifications
You must be signed in to change notification settings - Fork 6.1k
/
tach_mchp_xec.c
216 lines (181 loc) · 5.36 KB
/
tach_mchp_xec.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
/*
* Copyright (c) 2020 Intel Corporation
* Copyright (c) 2022 Microchip Technology Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT microchip_xec_tach
#include <errno.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/arch/cpu.h>
#ifdef CONFIG_SOC_SERIES_MEC172X
#include <zephyr/drivers/clock_control/mchp_xec_clock_control.h>
#include <zephyr/drivers/interrupt_controller/intc_mchp_xec_ecia.h>
#endif
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/sensor.h>
#include <soc.h>
#include <zephyr/sys/sys_io.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/device.h>
#ifdef CONFIG_PM_DEVICE
#include <zephyr/pm/policy.h>
#endif
LOG_MODULE_REGISTER(tach_xec, CONFIG_SENSOR_LOG_LEVEL);
struct tach_xec_config {
struct tach_regs * const regs;
uint8_t girq;
uint8_t girq_pos;
uint8_t pcr_idx;
uint8_t pcr_pos;
const struct pinctrl_dev_config *pcfg;
};
struct tach_xec_data {
uint32_t control;
uint16_t count;
};
#define FAN_STOPPED 0xFFFFU
#define COUNT_100KHZ_SEC 100000U
#define SEC_TO_MINUTE 60U
#define PIN_STS_TIMEOUT 20U
#define TACH_CTRL_EDGES (CONFIG_TACH_XEC_EDGES << \
MCHP_TACH_CTRL_NUM_EDGES_POS)
int tach_xec_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
ARG_UNUSED(chan);
const struct tach_xec_config * const cfg = dev->config;
struct tach_xec_data * const data = dev->data;
struct tach_regs * const tach = cfg->regs;
uint8_t poll_count = 0;
#ifdef CONFIG_PM_DEVICE
pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
#endif
while (poll_count < PIN_STS_TIMEOUT) {
/* See whether internal counter is already latched */
if (tach->STATUS & MCHP_TACH_STS_CNT_RDY) {
data->count =
tach->CONTROL >> MCHP_TACH_CTRL_COUNTER_POS;
break;
}
poll_count++;
/* Allow other threads to run while we sleep */
k_usleep(USEC_PER_MSEC);
}
#ifdef CONFIG_PM_DEVICE
pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
#endif
if (poll_count == PIN_STS_TIMEOUT) {
return -EINVAL;
}
/* We interpret a fan stopped or jammed as 0 */
if (data->count == FAN_STOPPED) {
data->count = 0U;
}
return 0;
}
static int tach_xec_channel_get(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct tach_xec_data * const data = dev->data;
if (chan != SENSOR_CHAN_RPM) {
return -ENOTSUP;
}
/* Convert the count per 100khz cycles to rpm */
if (data->count != FAN_STOPPED && data->count != 0U) {
val->val1 = (SEC_TO_MINUTE * COUNT_100KHZ_SEC)/data->count;
val->val2 = 0U;
} else {
val->val1 = 0U;
}
val->val2 = 0U;
return 0;
}
static void tach_xec_sleep_clr(const struct device *dev)
{
const struct tach_xec_config * const cfg = dev->config;
struct pcr_regs * const pcr = (struct pcr_regs * const)(
DT_REG_ADDR_BY_IDX(DT_NODELABEL(pcr), 0));
#ifdef CONFIG_SOC_SERIES_MEC172X
pcr->SLP_EN[cfg->pcr_idx] &= ~BIT(cfg->pcr_pos);
#else
uintptr_t addr = (uintptr_t)&pcr->SLP_EN0 + (4u * cfg->pcr_idx);
uint32_t pcr_val = sys_read32(addr) & ~BIT(cfg->pcr_pos);
sys_write32(pcr_val, addr);
#endif
}
#ifdef CONFIG_PM_DEVICE
static int tach_xec_pm_action(const struct device *dev, enum pm_device_action action)
{
const struct tach_xec_config * const cfg = dev->config;
struct tach_xec_data * const data = dev->data;
struct tach_regs * const tach = cfg->regs;
int ret = 0;
switch (action) {
case PM_DEVICE_ACTION_RESUME:
if (data->control & MCHP_TACH_CTRL_EN) {
tach->CONTROL |= MCHP_TACH_CTRL_EN;
data->control &= (~MCHP_TACH_CTRL_EN);
}
break;
case PM_DEVICE_ACTION_SUSPEND:
if (tach->CONTROL & MCHP_TACH_CTRL_EN) {
/* Take a backup */
data->control = tach->CONTROL;
tach->CONTROL &= (~MCHP_TACH_CTRL_EN);
}
break;
default:
ret = -ENOTSUP;
}
return ret;
}
#endif /* CONFIG_PM_DEVICE */
static int tach_xec_init(const struct device *dev)
{
const struct tach_xec_config * const cfg = dev->config;
struct tach_regs * const tach = cfg->regs;
int ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (ret != 0) {
LOG_ERR("XEC TACH pinctrl init failed (%d)", ret);
return ret;
}
tach_xec_sleep_clr(dev);
tach->CONTROL = MCHP_TACH_CTRL_READ_MODE_100K_CLOCK |
TACH_CTRL_EDGES |
MCHP_TACH_CTRL_FILTER_EN |
MCHP_TACH_CTRL_EN;
return 0;
}
static const struct sensor_driver_api tach_xec_driver_api = {
.sample_fetch = tach_xec_sample_fetch,
.channel_get = tach_xec_channel_get,
};
#define XEC_TACH_CONFIG(inst) \
static const struct tach_xec_config tach_xec_config_##inst = { \
.regs = (struct tach_regs * const)DT_INST_REG_ADDR(inst), \
.girq = DT_INST_PROP_BY_IDX(inst, girqs, 0), \
.girq_pos = DT_INST_PROP_BY_IDX(inst, girqs, 1), \
.pcr_idx = DT_INST_PROP_BY_IDX(inst, pcrs, 0), \
.pcr_pos = DT_INST_PROP_BY_IDX(inst, pcrs, 1), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
}
#define TACH_XEC_DEVICE(id) \
static struct tach_xec_data tach_xec_data_##id; \
\
PINCTRL_DT_INST_DEFINE(id); \
\
XEC_TACH_CONFIG(id); \
\
PM_DEVICE_DT_INST_DEFINE(id, tach_xec_pm_action); \
\
SENSOR_DEVICE_DT_INST_DEFINE(id, \
tach_xec_init, \
PM_DEVICE_DT_INST_GET(id), \
&tach_xec_data_##id, \
&tach_xec_config_##id, \
POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, \
&tach_xec_driver_api);
DT_INST_FOREACH_STATUS_OKAY(TACH_XEC_DEVICE)