xref: /linux/drivers/platform/cznic/turris-omnia-mcu.h (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * CZ.NIC's Turris Omnia MCU driver
4  *
5  * 2024 by Marek Behún <kabel@kernel.org>
6  */
7 
8 #ifndef __TURRIS_OMNIA_MCU_H
9 #define __TURRIS_OMNIA_MCU_H
10 
11 #include <linux/bitops.h>
12 #include <linux/completion.h>
13 #include <linux/gpio/driver.h>
14 #include <linux/hw_random.h>
15 #include <linux/if_ether.h>
16 #include <linux/mutex.h>
17 #include <linux/types.h>
18 #include <linux/watchdog.h>
19 #include <linux/workqueue.h>
20 #include <asm/byteorder.h>
21 #include <linux/unaligned.h>
22 
23 struct i2c_client;
24 struct rtc_device;
25 
26 /**
27  * struct omnia_mcu - driver private data structure
28  * @client:			I2C client
29  * @type:			MCU type (STM32, GD32, MKL, or unknown)
30  * @features:			bitmap of features supported by the MCU firmware
31  * @board_serial_number:	board serial number, if stored in MCU
32  * @board_first_mac:		board first MAC address, if stored in MCU
33  * @board_revision:		board revision, if stored in MCU
34  * @gc:				GPIO chip
35  * @lock:			mutex to protect internal GPIO chip state
36  * @mask:			bitmap of masked IRQs
37  * @rising:			bitmap of rising edge IRQs
38  * @falling:			bitmap of falling edge IRQs
39  * @both:			bitmap of both edges IRQs
40  * @cached:			bitmap of cached IRQ line values (when an IRQ line is configured for
41  *				both edges, we cache the corresponding GPIO values in the IRQ
42  *				handler)
43  * @is_cached:			bitmap of which IRQ line values are cached
44  * @button_release_emul_work:	front button release emulation work, used with old MCU firmware
45  *				versions which did not send button release events, only button press
46  *				events
47  * @last_status:		cached value of the status word, to be compared with new value to
48  *				determine which interrupt events occurred, used with old MCU
49  *				firmware versions which only informed that the status word changed,
50  *				but not which bits of the status word changed
51  * @button_pressed_emul:	the front button is still emulated to be pressed
52  * @rtcdev:			RTC device, does not actually count real-time, the device is only
53  *				used for the RTC alarm mechanism, so that the board can be
54  *				configured to wake up from poweroff state at a specific time
55  * @rtc_alarm:			RTC alarm that was set for the board to wake up on, in MCU time
56  *				(seconds since last MCU reset)
57  * @front_button_poweron:	the front button should power on the device after it is powered off
58  * @wdt:			watchdog driver structure
59  * @trng:			RNG driver structure
60  * @trng_entropy_ready:		RNG entropy ready completion
61  */
62 struct omnia_mcu {
63 	struct i2c_client *client;
64 	const char *type;
65 	u32 features;
66 
67 	u64 board_serial_number;
68 	u8 board_first_mac[ETH_ALEN];
69 	u8 board_revision;
70 
71 #ifdef CONFIG_TURRIS_OMNIA_MCU_GPIO
72 	struct gpio_chip gc;
73 	struct mutex lock;
74 	unsigned long mask, rising, falling, both, cached, is_cached;
75 	struct delayed_work button_release_emul_work;
76 	unsigned long last_status;
77 	bool button_pressed_emul;
78 #endif
79 
80 #ifdef CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP
81 	struct rtc_device *rtcdev;
82 	u32 rtc_alarm;
83 	bool front_button_poweron;
84 #endif
85 
86 #ifdef CONFIG_TURRIS_OMNIA_MCU_WATCHDOG
87 	struct watchdog_device wdt;
88 #endif
89 
90 #ifdef CONFIG_TURRIS_OMNIA_MCU_TRNG
91 	struct hwrng trng;
92 	struct completion trng_entropy_ready;
93 #endif
94 };
95 
96 int omnia_cmd_write_read(const struct i2c_client *client,
97 			 void *cmd, unsigned int cmd_len,
98 			 void *reply, unsigned int reply_len);
99 
100 static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,
101 				  unsigned int len)
102 {
103 	return omnia_cmd_write_read(client, cmd, len, NULL, 0);
104 }
105 
106 static inline int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd,
107 				     u8 val)
108 {
109 	u8 buf[2] = { cmd, val };
110 
111 	return omnia_cmd_write(client, buf, sizeof(buf));
112 }
113 
114 static inline int omnia_cmd_write_u16(const struct i2c_client *client, u8 cmd,
115 				      u16 val)
116 {
117 	u8 buf[3];
118 
119 	buf[0] = cmd;
120 	put_unaligned_le16(val, &buf[1]);
121 
122 	return omnia_cmd_write(client, buf, sizeof(buf));
123 }
124 
125 static inline int omnia_cmd_write_u32(const struct i2c_client *client, u8 cmd,
126 				      u32 val)
127 {
128 	u8 buf[5];
129 
130 	buf[0] = cmd;
131 	put_unaligned_le32(val, &buf[1]);
132 
133 	return omnia_cmd_write(client, buf, sizeof(buf));
134 }
135 
136 static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd,
137 				 void *reply, unsigned int len)
138 {
139 	return omnia_cmd_write_read(client, &cmd, 1, reply, len);
140 }
141 
142 static inline unsigned int
143 omnia_compute_reply_length(unsigned long mask, bool interleaved,
144 			   unsigned int offset)
145 {
146 	if (!mask)
147 		return 0;
148 
149 	return ((__fls(mask) >> 3) << interleaved) + 1 + offset;
150 }
151 
152 /* Returns 0 on success */
153 static inline int omnia_cmd_read_bits(const struct i2c_client *client, u8 cmd,
154 				      unsigned long bits, unsigned long *dst)
155 {
156 	__le32 reply;
157 	int err;
158 
159 	if (!bits) {
160 		*dst = 0;
161 		return 0;
162 	}
163 
164 	err = omnia_cmd_read(client, cmd, &reply,
165 			     omnia_compute_reply_length(bits, false, 0));
166 	if (err)
167 		return err;
168 
169 	*dst = le32_to_cpu(reply) & bits;
170 
171 	return 0;
172 }
173 
174 static inline int omnia_cmd_read_bit(const struct i2c_client *client, u8 cmd,
175 				     unsigned long bit)
176 {
177 	unsigned long reply;
178 	int err;
179 
180 	err = omnia_cmd_read_bits(client, cmd, bit, &reply);
181 	if (err)
182 		return err;
183 
184 	return !!reply;
185 }
186 
187 static inline int omnia_cmd_read_u32(const struct i2c_client *client, u8 cmd,
188 				     u32 *dst)
189 {
190 	__le32 reply;
191 	int err;
192 
193 	err = omnia_cmd_read(client, cmd, &reply, sizeof(reply));
194 	if (err)
195 		return err;
196 
197 	*dst = le32_to_cpu(reply);
198 
199 	return 0;
200 }
201 
202 static inline int omnia_cmd_read_u16(const struct i2c_client *client, u8 cmd,
203 				     u16 *dst)
204 {
205 	__le16 reply;
206 	int err;
207 
208 	err = omnia_cmd_read(client, cmd, &reply, sizeof(reply));
209 	if (err)
210 		return err;
211 
212 	*dst = le16_to_cpu(reply);
213 
214 	return 0;
215 }
216 
217 static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd,
218 				    u8 *reply)
219 {
220 	return omnia_cmd_read(client, cmd, reply, sizeof(*reply));
221 }
222 
223 #ifdef CONFIG_TURRIS_OMNIA_MCU_GPIO
224 extern const u8 omnia_int_to_gpio_idx[32];
225 extern const struct attribute_group omnia_mcu_gpio_group;
226 int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu);
227 #else
228 static inline int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu)
229 {
230 	return 0;
231 }
232 #endif
233 
234 #ifdef CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP
235 extern const struct attribute_group omnia_mcu_poweroff_group;
236 int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu);
237 #else
238 static inline int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu)
239 {
240 	return 0;
241 }
242 #endif
243 
244 #ifdef CONFIG_TURRIS_OMNIA_MCU_TRNG
245 int omnia_mcu_register_trng(struct omnia_mcu *mcu);
246 #else
247 static inline int omnia_mcu_register_trng(struct omnia_mcu *mcu)
248 {
249 	return 0;
250 }
251 #endif
252 
253 #ifdef CONFIG_TURRIS_OMNIA_MCU_WATCHDOG
254 int omnia_mcu_register_watchdog(struct omnia_mcu *mcu);
255 #else
256 static inline int omnia_mcu_register_watchdog(struct omnia_mcu *mcu)
257 {
258 	return 0;
259 }
260 #endif
261 
262 #endif /* __TURRIS_OMNIA_MCU_H */
263