1 /*
2  * Copyright (c) 2020 Intel Corporation
3  * Copyright (c) 2022 Microchip Technology Inc.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT microchip_xec_tach
9 
10 #include <errno.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/device.h>
13 #include <zephyr/arch/cpu.h>
14 #ifdef CONFIG_SOC_SERIES_MEC172X
15 #include <zephyr/drivers/clock_control/mchp_xec_clock_control.h>
16 #include <zephyr/drivers/interrupt_controller/intc_mchp_xec_ecia.h>
17 #endif
18 #include <zephyr/drivers/pinctrl.h>
19 #include <zephyr/drivers/sensor.h>
20 #include <soc.h>
21 #include <zephyr/sys/sys_io.h>
22 #include <zephyr/logging/log.h>
23 
24 #include <zephyr/pm/device.h>
25 #include <zephyr/pm/policy.h>
26 
27 LOG_MODULE_REGISTER(tach_xec, CONFIG_SENSOR_LOG_LEVEL);
28 
29 struct tach_xec_config {
30 	struct tach_regs * const regs;
31 	uint8_t girq;
32 	uint8_t girq_pos;
33 	uint8_t pcr_idx;
34 	uint8_t pcr_pos;
35 	const struct pinctrl_dev_config *pcfg;
36 };
37 
38 struct tach_xec_data {
39 	uint32_t control;
40 	uint16_t count;
41 };
42 
43 #define FAN_STOPPED		0xFFFFU
44 #define COUNT_100KHZ_SEC	100000U
45 #define SEC_TO_MINUTE		60U
46 #define PIN_STS_TIMEOUT		20U
47 #define TACH_CTRL_EDGES		(CONFIG_TACH_XEC_EDGES << \
48 				 MCHP_TACH_CTRL_NUM_EDGES_POS)
49 
tach_xec_sample_fetch(const struct device * dev,enum sensor_channel chan)50 int tach_xec_sample_fetch(const struct device *dev, enum sensor_channel chan)
51 {
52 	ARG_UNUSED(chan);
53 
54 	const struct tach_xec_config * const cfg = dev->config;
55 	struct tach_xec_data * const data = dev->data;
56 	struct tach_regs * const tach = cfg->regs;
57 	uint8_t poll_count = 0;
58 
59 	pm_policy_state_lock_get(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
60 
61 	while (poll_count < PIN_STS_TIMEOUT) {
62 		/* See whether internal counter is already latched */
63 		if (tach->STATUS & MCHP_TACH_STS_CNT_RDY) {
64 			data->count =
65 				tach->CONTROL >> MCHP_TACH_CTRL_COUNTER_POS;
66 			break;
67 		}
68 
69 		poll_count++;
70 
71 		/* Allow other threads to run while we sleep */
72 		k_usleep(USEC_PER_MSEC);
73 	}
74 
75 	pm_policy_state_lock_put(PM_STATE_SUSPEND_TO_IDLE, PM_ALL_SUBSTATES);
76 
77 	if (poll_count == PIN_STS_TIMEOUT) {
78 		return -EINVAL;
79 	}
80 
81 	/* We interpret a fan stopped or jammed as 0 */
82 	if (data->count == FAN_STOPPED) {
83 		data->count = 0U;
84 	}
85 
86 	return 0;
87 }
88 
tach_xec_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)89 static int tach_xec_channel_get(const struct device *dev,
90 				enum sensor_channel chan,
91 				struct sensor_value *val)
92 {
93 	struct tach_xec_data * const data = dev->data;
94 
95 	if (chan != SENSOR_CHAN_RPM) {
96 		return -ENOTSUP;
97 	}
98 
99 	/* Convert the count per 100khz cycles to rpm */
100 	if (data->count != FAN_STOPPED && data->count != 0U) {
101 		val->val1 = (SEC_TO_MINUTE * COUNT_100KHZ_SEC)/data->count;
102 		val->val2 = 0U;
103 	} else {
104 		val->val1 =  0U;
105 	}
106 
107 	val->val2 = 0U;
108 
109 	return 0;
110 }
111 
tach_xec_sleep_clr(const struct device * dev)112 static void tach_xec_sleep_clr(const struct device *dev)
113 {
114 	const struct tach_xec_config * const cfg = dev->config;
115 	struct pcr_regs * const pcr = (struct pcr_regs * const)(
116 		DT_REG_ADDR_BY_IDX(DT_NODELABEL(pcr), 0));
117 
118 #ifdef CONFIG_SOC_SERIES_MEC172X
119 	pcr->SLP_EN[cfg->pcr_idx] &= ~BIT(cfg->pcr_pos);
120 #else
121 	uintptr_t addr = (uintptr_t)&pcr->SLP_EN0 + (4u * cfg->pcr_idx);
122 	uint32_t pcr_val = sys_read32(addr) & ~BIT(cfg->pcr_pos);
123 
124 	sys_write32(pcr_val, addr);
125 #endif
126 }
127 
128 #ifdef CONFIG_PM_DEVICE
tach_xec_pm_action(const struct device * dev,enum pm_device_action action)129 static int tach_xec_pm_action(const struct device *dev, enum pm_device_action action)
130 {
131 	const struct tach_xec_config * const cfg = dev->config;
132 	struct tach_xec_data * const data = dev->data;
133 	struct tach_regs * const tach = cfg->regs;
134 	int ret = 0;
135 
136 	switch (action) {
137 	case PM_DEVICE_ACTION_RESUME:
138 		if (data->control & MCHP_TACH_CTRL_EN) {
139 			tach->CONTROL |= MCHP_TACH_CTRL_EN;
140 			data->control &= (~MCHP_TACH_CTRL_EN);
141 		}
142 	break;
143 	case PM_DEVICE_ACTION_SUSPEND:
144 		if (tach->CONTROL & MCHP_TACH_CTRL_EN) {
145 			/* Take a backup */
146 			data->control = tach->CONTROL;
147 			tach->CONTROL &= (~MCHP_TACH_CTRL_EN);
148 		}
149 	break;
150 	default:
151 		ret = -ENOTSUP;
152 	}
153 
154 	return ret;
155 }
156 #endif /* CONFIG_PM_DEVICE */
157 
tach_xec_init(const struct device * dev)158 static int tach_xec_init(const struct device *dev)
159 {
160 	const struct tach_xec_config * const cfg = dev->config;
161 	struct tach_regs * const tach = cfg->regs;
162 
163 	int ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
164 
165 	if (ret != 0) {
166 		LOG_ERR("XEC TACH pinctrl init failed (%d)", ret);
167 		return ret;
168 	}
169 
170 	tach_xec_sleep_clr(dev);
171 
172 	tach->CONTROL = MCHP_TACH_CTRL_READ_MODE_100K_CLOCK	|
173 			TACH_CTRL_EDGES	                        |
174 			MCHP_TACH_CTRL_FILTER_EN		|
175 			MCHP_TACH_CTRL_EN;
176 
177 	return 0;
178 }
179 
180 static DEVICE_API(sensor, tach_xec_driver_api) = {
181 	.sample_fetch = tach_xec_sample_fetch,
182 	.channel_get = tach_xec_channel_get,
183 };
184 
185 #define XEC_TACH_CONFIG(inst)						\
186 	static const struct tach_xec_config tach_xec_config_##inst = {	\
187 		.regs = (struct tach_regs * const)DT_INST_REG_ADDR(inst),	\
188 		.girq = DT_INST_PROP_BY_IDX(inst, girqs, 0),		\
189 		.girq_pos = DT_INST_PROP_BY_IDX(inst, girqs, 1),	\
190 		.pcr_idx = DT_INST_PROP_BY_IDX(inst, pcrs, 0),		\
191 		.pcr_pos = DT_INST_PROP_BY_IDX(inst, pcrs, 1),		\
192 		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),		\
193 	}
194 
195 #define TACH_XEC_DEVICE(id)						\
196 	static struct tach_xec_data tach_xec_data_##id;			\
197 									\
198 	PINCTRL_DT_INST_DEFINE(id);					\
199 									\
200 	XEC_TACH_CONFIG(id);						\
201 									\
202 	PM_DEVICE_DT_INST_DEFINE(id, tach_xec_pm_action);		\
203 									\
204 	SENSOR_DEVICE_DT_INST_DEFINE(id,				\
205 			    tach_xec_init,				\
206 			    PM_DEVICE_DT_INST_GET(id),			\
207 			    &tach_xec_data_##id,			\
208 			    &tach_xec_config_##id,			\
209 			    POST_KERNEL,				\
210 			    CONFIG_SENSOR_INIT_PRIORITY,		\
211 			    &tach_xec_driver_api);
212 
213 DT_INST_FOREACH_STATUS_OKAY(TACH_XEC_DEVICE)
214