233 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			233 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* SPDX-License-Identifier: GPL-2.0 */
 | |
| /*
 | |
|  * CZ.NIC's Turris Omnia MCU driver
 | |
|  *
 | |
|  * 2024 by Marek Behún <kabel@kernel.org>
 | |
|  */
 | |
| 
 | |
| #ifndef __TURRIS_OMNIA_MCU_H
 | |
| #define __TURRIS_OMNIA_MCU_H
 | |
| 
 | |
| #include <linux/bitops.h>
 | |
| #include <linux/completion.h>
 | |
| #include <linux/gpio/driver.h>
 | |
| #include <linux/hw_random.h>
 | |
| #include <linux/if_ether.h>
 | |
| #include <linux/mutex.h>
 | |
| #include <linux/types.h>
 | |
| #include <linux/watchdog.h>
 | |
| #include <linux/workqueue.h>
 | |
| #include <asm/byteorder.h>
 | |
| #include <linux/unaligned.h>
 | |
| 
 | |
| struct i2c_client;
 | |
| struct rtc_device;
 | |
| 
 | |
| struct omnia_mcu {
 | |
| 	struct i2c_client *client;
 | |
| 	const char *type;
 | |
| 	u32 features;
 | |
| 
 | |
| 	/* board information */
 | |
| 	u64 board_serial_number;
 | |
| 	u8 board_first_mac[ETH_ALEN];
 | |
| 	u8 board_revision;
 | |
| 
 | |
| #ifdef CONFIG_TURRIS_OMNIA_MCU_GPIO
 | |
| 	/* GPIO chip */
 | |
| 	struct gpio_chip gc;
 | |
| 	struct mutex lock;
 | |
| 	unsigned long mask, rising, falling, both, cached, is_cached;
 | |
| 	/* Old MCU firmware handling needs the following */
 | |
| 	struct delayed_work button_release_emul_work;
 | |
| 	unsigned long last_status;
 | |
| 	bool button_pressed_emul;
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP
 | |
| 	/* RTC device for configuring wake-up */
 | |
| 	struct rtc_device *rtcdev;
 | |
| 	u32 rtc_alarm;
 | |
| 	bool front_button_poweron;
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_TURRIS_OMNIA_MCU_WATCHDOG
 | |
| 	/* MCU watchdog */
 | |
| 	struct watchdog_device wdt;
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_TURRIS_OMNIA_MCU_TRNG
 | |
| 	/* true random number generator */
 | |
| 	struct hwrng trng;
 | |
| 	struct completion trng_entropy_ready;
 | |
| #endif
 | |
| };
 | |
| 
 | |
| int omnia_cmd_write_read(const struct i2c_client *client,
 | |
| 			 void *cmd, unsigned int cmd_len,
 | |
| 			 void *reply, unsigned int reply_len);
 | |
| 
 | |
| static inline int omnia_cmd_write(const struct i2c_client *client, void *cmd,
 | |
| 				  unsigned int len)
 | |
| {
 | |
| 	return omnia_cmd_write_read(client, cmd, len, NULL, 0);
 | |
| }
 | |
| 
 | |
| static inline int omnia_cmd_write_u8(const struct i2c_client *client, u8 cmd,
 | |
| 				     u8 val)
 | |
| {
 | |
| 	u8 buf[2] = { cmd, val };
 | |
| 
 | |
| 	return omnia_cmd_write(client, buf, sizeof(buf));
 | |
| }
 | |
| 
 | |
| static inline int omnia_cmd_write_u16(const struct i2c_client *client, u8 cmd,
 | |
| 				      u16 val)
 | |
| {
 | |
| 	u8 buf[3];
 | |
| 
 | |
| 	buf[0] = cmd;
 | |
| 	put_unaligned_le16(val, &buf[1]);
 | |
| 
 | |
| 	return omnia_cmd_write(client, buf, sizeof(buf));
 | |
| }
 | |
| 
 | |
| static inline int omnia_cmd_write_u32(const struct i2c_client *client, u8 cmd,
 | |
| 				      u32 val)
 | |
| {
 | |
| 	u8 buf[5];
 | |
| 
 | |
| 	buf[0] = cmd;
 | |
| 	put_unaligned_le32(val, &buf[1]);
 | |
| 
 | |
| 	return omnia_cmd_write(client, buf, sizeof(buf));
 | |
| }
 | |
| 
 | |
| static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd,
 | |
| 				 void *reply, unsigned int len)
 | |
| {
 | |
| 	return omnia_cmd_write_read(client, &cmd, 1, reply, len);
 | |
| }
 | |
| 
 | |
| static inline unsigned int
 | |
| omnia_compute_reply_length(unsigned long mask, bool interleaved,
 | |
| 			   unsigned int offset)
 | |
| {
 | |
| 	if (!mask)
 | |
| 		return 0;
 | |
| 
 | |
| 	return ((__fls(mask) >> 3) << interleaved) + 1 + offset;
 | |
| }
 | |
| 
 | |
| /* Returns 0 on success */
 | |
| static inline int omnia_cmd_read_bits(const struct i2c_client *client, u8 cmd,
 | |
| 				      unsigned long bits, unsigned long *dst)
 | |
| {
 | |
| 	__le32 reply;
 | |
| 	int err;
 | |
| 
 | |
| 	if (!bits) {
 | |
| 		*dst = 0;
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	err = omnia_cmd_read(client, cmd, &reply,
 | |
| 			     omnia_compute_reply_length(bits, false, 0));
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	*dst = le32_to_cpu(reply) & bits;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static inline int omnia_cmd_read_bit(const struct i2c_client *client, u8 cmd,
 | |
| 				     unsigned long bit)
 | |
| {
 | |
| 	unsigned long reply;
 | |
| 	int err;
 | |
| 
 | |
| 	err = omnia_cmd_read_bits(client, cmd, bit, &reply);
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	return !!reply;
 | |
| }
 | |
| 
 | |
| static inline int omnia_cmd_read_u32(const struct i2c_client *client, u8 cmd,
 | |
| 				     u32 *dst)
 | |
| {
 | |
| 	__le32 reply;
 | |
| 	int err;
 | |
| 
 | |
| 	err = omnia_cmd_read(client, cmd, &reply, sizeof(reply));
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	*dst = le32_to_cpu(reply);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static inline int omnia_cmd_read_u16(const struct i2c_client *client, u8 cmd,
 | |
| 				     u16 *dst)
 | |
| {
 | |
| 	__le16 reply;
 | |
| 	int err;
 | |
| 
 | |
| 	err = omnia_cmd_read(client, cmd, &reply, sizeof(reply));
 | |
| 	if (err)
 | |
| 		return err;
 | |
| 
 | |
| 	*dst = le16_to_cpu(reply);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd,
 | |
| 				    u8 *reply)
 | |
| {
 | |
| 	return omnia_cmd_read(client, cmd, reply, sizeof(*reply));
 | |
| }
 | |
| 
 | |
| #ifdef CONFIG_TURRIS_OMNIA_MCU_GPIO
 | |
| extern const u8 omnia_int_to_gpio_idx[32];
 | |
| extern const struct attribute_group omnia_mcu_gpio_group;
 | |
| int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu);
 | |
| #else
 | |
| static inline int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_TURRIS_OMNIA_MCU_SYSOFF_WAKEUP
 | |
| extern const struct attribute_group omnia_mcu_poweroff_group;
 | |
| int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu);
 | |
| #else
 | |
| static inline int omnia_mcu_register_sys_off_and_wakeup(struct omnia_mcu *mcu)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_TURRIS_OMNIA_MCU_TRNG
 | |
| int omnia_mcu_register_trng(struct omnia_mcu *mcu);
 | |
| #else
 | |
| static inline int omnia_mcu_register_trng(struct omnia_mcu *mcu)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_TURRIS_OMNIA_MCU_WATCHDOG
 | |
| int omnia_mcu_register_watchdog(struct omnia_mcu *mcu);
 | |
| #else
 | |
| static inline int omnia_mcu_register_watchdog(struct omnia_mcu *mcu)
 | |
| {
 | |
| 	return 0;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| #endif /* __TURRIS_OMNIA_MCU_H */
 |