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
omnia_cmd_write(const struct i2c_client * client,void * cmd,unsigned int len)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
omnia_cmd_write_u8(const struct i2c_client * client,u8 cmd,u8 val)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
omnia_cmd_write_u16(const struct i2c_client * client,u8 cmd,u16 val)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
omnia_cmd_write_u32(const struct i2c_client * client,u8 cmd,u32 val)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
omnia_cmd_read(const struct i2c_client * client,u8 cmd,void * reply,unsigned int len)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
omnia_compute_reply_length(unsigned long mask,bool interleaved,unsigned int offset)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 */
omnia_cmd_read_bits(const struct i2c_client * client,u8 cmd,unsigned long bits,unsigned long * dst)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
omnia_cmd_read_bit(const struct i2c_client * client,u8 cmd,unsigned long bit)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
omnia_cmd_read_u32(const struct i2c_client * client,u8 cmd,u32 * dst)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
omnia_cmd_read_u16(const struct i2c_client * client,u8 cmd,u16 * dst)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
omnia_cmd_read_u8(const struct i2c_client * client,u8 cmd,u8 * reply)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
omnia_mcu_register_gpiochip(struct omnia_mcu * mcu)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
omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu * mcu)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
omnia_mcu_register_trng(struct omnia_mcu * mcu)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
omnia_mcu_register_watchdog(struct omnia_mcu * mcu)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