platform: cznic: turris-omnia-mcu: Add support for MCU connected GPIOs
Add support for GPIOs connected to the MCU on the Turris Omnia board. This includes: - front button pin - enable pins for USB regulators - MiniPCIe / mSATA card presence pins in MiniPCIe port 0 - LED output pins from WAN ethernet PHY, LAN switch and MiniPCIe ports - on board revisions 32+ also various peripheral resets and another voltage regulator enable pin Signed-off-by: Marek Behún <kabel@kernel.org> Acked-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org> Link: https://lore.kernel.org/r/20240701113010.16447-4-kabel@kernel.org Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
parent
992f1a3d4e
commit
dfa556e45a
6 changed files with 1197 additions and 1 deletions
|
@ -22,6 +22,22 @@ Description: (RO) Contains device first MAC address. Each Turris Omnia is
|
||||||
|
|
||||||
Format: %pM.
|
Format: %pM.
|
||||||
|
|
||||||
|
What: /sys/bus/i2c/devices/<mcu_device>/front_button_mode
|
||||||
|
Date: September 2024
|
||||||
|
KernelVersion: 6.11
|
||||||
|
Contact: Marek Behún <kabel@kernel.org>
|
||||||
|
Description: (RW) The front button on the Turris Omnia router can be
|
||||||
|
configured either to change the intensity of all the LEDs on the
|
||||||
|
front panel, or to send the press event to the CPU as an
|
||||||
|
interrupt.
|
||||||
|
|
||||||
|
This file switches between these two modes:
|
||||||
|
- "mcu" makes the button press event be handled by the MCU to
|
||||||
|
change the LEDs panel intensity.
|
||||||
|
- "cpu" makes the button press event be handled by the CPU.
|
||||||
|
|
||||||
|
Format: %s.
|
||||||
|
|
||||||
What: /sys/bus/i2c/devices/<mcu_device>/fw_features
|
What: /sys/bus/i2c/devices/<mcu_device>/fw_features
|
||||||
Date: September 2024
|
Date: September 2024
|
||||||
KernelVersion: 6.11
|
KernelVersion: 6.11
|
||||||
|
|
|
@ -16,9 +16,24 @@ config TURRIS_OMNIA_MCU
|
||||||
tristate "Turris Omnia MCU driver"
|
tristate "Turris Omnia MCU driver"
|
||||||
depends on MACH_ARMADA_38X || COMPILE_TEST
|
depends on MACH_ARMADA_38X || COMPILE_TEST
|
||||||
depends on I2C
|
depends on I2C
|
||||||
|
select GPIOLIB
|
||||||
|
select GPIOLIB_IRQCHIP
|
||||||
help
|
help
|
||||||
Say Y here to add support for the features implemented by the
|
Say Y here to add support for the features implemented by the
|
||||||
microcontroller on the CZ.NIC's Turris Omnia SOHO router.
|
microcontroller on the CZ.NIC's Turris Omnia SOHO router.
|
||||||
|
The features include:
|
||||||
|
- GPIO pins
|
||||||
|
- to get front button press events (the front button can be
|
||||||
|
configured either to generate press events to the CPU or to change
|
||||||
|
front LEDs panel brightness)
|
||||||
|
- to enable / disable USB port voltage regulators and to detect
|
||||||
|
USB overcurrent
|
||||||
|
- to detect MiniPCIe / mSATA card presence in MiniPCIe port 0
|
||||||
|
- to configure resets of various peripherals on board revisions 32+
|
||||||
|
- to enable / disable the VHV voltage regulator to the SOC in order
|
||||||
|
to be able to program SOC's OTP on board revisions 32+
|
||||||
|
- to get input from the LED output pins of the WAN ethernet PHY, LAN
|
||||||
|
switch and MiniPCIe ports
|
||||||
To compile this driver as a module, choose M here; the module will be
|
To compile this driver as a module, choose M here; the module will be
|
||||||
called turris-omnia-mcu.
|
called turris-omnia-mcu.
|
||||||
|
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
|
|
||||||
obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o
|
obj-$(CONFIG_TURRIS_OMNIA_MCU) += turris-omnia-mcu.o
|
||||||
turris-omnia-mcu-y := turris-omnia-mcu-base.o
|
turris-omnia-mcu-y := turris-omnia-mcu-base.o
|
||||||
|
turris-omnia-mcu-y += turris-omnia-mcu-gpio.o
|
||||||
|
|
|
@ -197,6 +197,7 @@ static const struct attribute_group omnia_mcu_base_group = {
|
||||||
|
|
||||||
static const struct attribute_group *omnia_mcu_groups[] = {
|
static const struct attribute_group *omnia_mcu_groups[] = {
|
||||||
&omnia_mcu_base_group,
|
&omnia_mcu_base_group,
|
||||||
|
&omnia_mcu_gpio_group,
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -371,7 +372,7 @@ static int omnia_mcu_probe(struct i2c_client *client)
|
||||||
"Cannot read board info\n");
|
"Cannot read board info\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return omnia_mcu_register_gpiochip(mcu);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct of_device_id of_omnia_mcu_match[] = {
|
static const struct of_device_id of_omnia_mcu_match[] = {
|
||||||
|
|
1095
drivers/platform/cznic/turris-omnia-mcu-gpio.c
Normal file
1095
drivers/platform/cznic/turris-omnia-mcu-gpio.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -8,8 +8,12 @@
|
||||||
#ifndef __TURRIS_OMNIA_MCU_H
|
#ifndef __TURRIS_OMNIA_MCU_H
|
||||||
#define __TURRIS_OMNIA_MCU_H
|
#define __TURRIS_OMNIA_MCU_H
|
||||||
|
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/gpio/driver.h>
|
||||||
#include <linux/if_ether.h>
|
#include <linux/if_ether.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
#include <asm/byteorder.h>
|
#include <asm/byteorder.h>
|
||||||
|
|
||||||
struct i2c_client;
|
struct i2c_client;
|
||||||
|
@ -23,18 +27,78 @@ struct omnia_mcu {
|
||||||
u64 board_serial_number;
|
u64 board_serial_number;
|
||||||
u8 board_first_mac[ETH_ALEN];
|
u8 board_first_mac[ETH_ALEN];
|
||||||
u8 board_revision;
|
u8 board_revision;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
int omnia_cmd_write_read(const struct i2c_client *client,
|
int omnia_cmd_write_read(const struct i2c_client *client,
|
||||||
void *cmd, unsigned int cmd_len,
|
void *cmd, unsigned int cmd_len,
|
||||||
void *reply, unsigned int reply_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_read(const struct i2c_client *client, u8 cmd,
|
static inline int omnia_cmd_read(const struct i2c_client *client, u8 cmd,
|
||||||
void *reply, unsigned int len)
|
void *reply, unsigned int len)
|
||||||
{
|
{
|
||||||
return omnia_cmd_write_read(client, &cmd, 1, reply, 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,
|
static inline int omnia_cmd_read_u32(const struct i2c_client *client, u8 cmd,
|
||||||
u32 *dst)
|
u32 *dst)
|
||||||
{
|
{
|
||||||
|
@ -71,4 +135,8 @@ static inline int omnia_cmd_read_u8(const struct i2c_client *client, u8 cmd,
|
||||||
return omnia_cmd_read(client, cmd, reply, sizeof(*reply));
|
return omnia_cmd_read(client, cmd, reply, sizeof(*reply));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern const struct attribute_group omnia_mcu_gpio_group;
|
||||||
|
|
||||||
|
int omnia_mcu_register_gpiochip(struct omnia_mcu *mcu);
|
||||||
|
|
||||||
#endif /* __TURRIS_OMNIA_MCU_H */
|
#endif /* __TURRIS_OMNIA_MCU_H */
|
||||||
|
|
Loading…
Add table
Reference in a new issue