mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-24 21:54:01 +00:00
gpio: Add Turris Omnia MCU driver
This driver registers GPIO controller and allows U-Boot to control GPIO pins on MCU which is connected to Turris Omnia via i2c. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Stefan Roese <sr@denx.de>
This commit is contained in:
parent
ef6fcab85f
commit
5e4d24ccc1
4 changed files with 318 additions and 0 deletions
|
@ -6,4 +6,5 @@ F: arch/arm/dts/armada-385-turris-omnia*.dts*
|
|||
F: board/CZ.NIC/turris_atsha_otp.*
|
||||
F: board/CZ.NIC/turris_omnia/
|
||||
F: configs/turris_omnia_defconfig
|
||||
F: drivers/gpio/turris_omnia_mcu.c
|
||||
F: include/configs/turris_omnia.h
|
||||
|
|
|
@ -598,4 +598,11 @@ config SLG7XL45106_I2C_GPO
|
|||
8-bit gpo expander, all gpo lines are controlled by writing
|
||||
value into data register.
|
||||
|
||||
config TURRIS_OMNIA_MCU
|
||||
bool "Turris Omnia MCU GPIO driver"
|
||||
depends on DM_GPIO
|
||||
default y if TARGET_TURRIS_OMNIA
|
||||
help
|
||||
Support for GPIOs on MCU connected to Turris Omnia via i2c.
|
||||
|
||||
endif
|
||||
|
|
|
@ -75,3 +75,4 @@ obj-$(CONFIG_MAX7320_GPIO) += max7320_gpio.o
|
|||
obj-$(CONFIG_SL28CPLD_GPIO) += sl28cpld-gpio.o
|
||||
obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN) += zynqmp_gpio_modepin.o
|
||||
obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o
|
||||
obj-$(CONFIG_$(SPL_TPL_)TURRIS_OMNIA_MCU) += turris_omnia_mcu.o
|
||||
|
|
309
drivers/gpio/turris_omnia_mcu.c
Normal file
309
drivers/gpio/turris_omnia_mcu.c
Normal file
|
@ -0,0 +1,309 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
// (C) 2022 Pali Rohár <pali@kernel.org>
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <i2c.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <linux/log2.h>
|
||||
|
||||
enum commands_e {
|
||||
CMD_GET_STATUS_WORD = 0x01,
|
||||
CMD_GENERAL_CONTROL = 0x02,
|
||||
|
||||
/* available if STS_FEATURES_SUPPORTED bit set in status word */
|
||||
CMD_GET_FEATURES = 0x10,
|
||||
|
||||
/* available if FEAT_EXT_CMDS bit is set in features */
|
||||
CMD_GET_EXT_STATUS_DWORD = 0x11,
|
||||
CMD_EXT_CONTROL = 0x12,
|
||||
CMD_GET_EXT_CONTROL_STATUS = 0x13,
|
||||
};
|
||||
|
||||
/* CMD_GET_STATUS_WORD */
|
||||
enum sts_word_e {
|
||||
STS_MCU_TYPE_MASK = GENMASK(1, 0),
|
||||
STS_MCU_TYPE_STM32 = 0,
|
||||
STS_MCU_TYPE_GD32 = 1,
|
||||
STS_MCU_TYPE_MKL = 2,
|
||||
STS_FEATURES_SUPPORTED = BIT(2),
|
||||
STS_USER_REGULATOR_NOT_SUPPORTED = BIT(3),
|
||||
STS_CARD_DET = BIT(4),
|
||||
STS_MSATA_IND = BIT(5),
|
||||
STS_USB30_OVC = BIT(6),
|
||||
STS_USB31_OVC = BIT(7),
|
||||
STS_USB30_PWRON = BIT(8),
|
||||
STS_USB31_PWRON = BIT(9),
|
||||
STS_ENABLE_4V5 = BIT(10),
|
||||
STS_BUTTON_MODE = BIT(11),
|
||||
STS_BUTTON_PRESSED = BIT(12),
|
||||
STS_BUTTON_COUNTER_MASK = GENMASK(15, 13)
|
||||
};
|
||||
|
||||
/* CMD_GENERAL_CONTROL */
|
||||
enum ctl_byte_e {
|
||||
CTL_LIGHT_RST = BIT(0),
|
||||
CTL_HARD_RST = BIT(1),
|
||||
/*CTL_RESERVED = BIT(2),*/
|
||||
CTL_USB30_PWRON = BIT(3),
|
||||
CTL_USB31_PWRON = BIT(4),
|
||||
CTL_ENABLE_4V5 = BIT(5),
|
||||
CTL_BUTTON_MODE = BIT(6),
|
||||
CTL_BOOTLOADER = BIT(7)
|
||||
};
|
||||
|
||||
/* CMD_GET_FEATURES */
|
||||
enum features_e {
|
||||
FEAT_EXT_CMDS = BIT(1),
|
||||
};
|
||||
|
||||
struct turris_omnia_mcu_info {
|
||||
u16 features;
|
||||
};
|
||||
|
||||
static int turris_omnia_mcu_get_function(struct udevice *dev, uint offset)
|
||||
{
|
||||
struct turris_omnia_mcu_info *info = dev_get_plat(dev);
|
||||
|
||||
switch (offset) {
|
||||
/* bank 0 */
|
||||
case 0 ... 15:
|
||||
switch (offset) {
|
||||
case ilog2(STS_USB30_PWRON):
|
||||
case ilog2(STS_USB31_PWRON):
|
||||
case ilog2(STS_ENABLE_4V5):
|
||||
case ilog2(STS_BUTTON_MODE):
|
||||
return GPIOF_OUTPUT;
|
||||
default:
|
||||
return GPIOF_INPUT;
|
||||
}
|
||||
|
||||
/* bank 1 - supported only when FEAT_EXT_CMDS is set */
|
||||
case (16 + 0) ... (16 + 31):
|
||||
if (!(info->features & FEAT_EXT_CMDS))
|
||||
return -EINVAL;
|
||||
return GPIOF_INPUT;
|
||||
|
||||
/* bank 2 - supported only when FEAT_EXT_CMDS is set */
|
||||
case (16 + 32 + 0) ... (16 + 32 + 15):
|
||||
if (!(info->features & FEAT_EXT_CMDS))
|
||||
return -EINVAL;
|
||||
return GPIOF_OUTPUT;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int turris_omnia_mcu_get_value(struct udevice *dev, uint offset)
|
||||
{
|
||||
struct turris_omnia_mcu_info *info = dev_get_plat(dev);
|
||||
u8 val16[2];
|
||||
u8 val32[4];
|
||||
int ret;
|
||||
|
||||
switch (offset) {
|
||||
/* bank 0 */
|
||||
case 0 ... 15:
|
||||
ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val16, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
return ((((u16)val16[1] << 8) | val16[0]) >> offset) & 0x1;
|
||||
|
||||
/* bank 1 - supported only when FEAT_EXT_CMDS is set */
|
||||
case (16 + 0) ... (16 + 31):
|
||||
if (!(info->features & FEAT_EXT_CMDS))
|
||||
return -EINVAL;
|
||||
ret = dm_i2c_read(dev, CMD_GET_EXT_STATUS_DWORD, val32, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
return ((((u32)val32[3] << 24) | ((u32)val32[2] << 16) |
|
||||
((u32)val32[1] << 8) | val32[0]) >> (offset - 16)) & 0x1;
|
||||
|
||||
/* bank 2 - supported only when FEAT_EXT_CMDS is set */
|
||||
case (16 + 32 + 0) ... (16 + 32 + 15):
|
||||
if (!(info->features & FEAT_EXT_CMDS))
|
||||
return -EINVAL;
|
||||
ret = dm_i2c_read(dev, CMD_GET_EXT_CONTROL_STATUS, val16, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
return ((((u16)val16[1] << 8) | val16[0]) >> (offset - 16 - 32)) & 0x1;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int turris_omnia_mcu_set_value(struct udevice *dev, uint offset, int value)
|
||||
{
|
||||
struct turris_omnia_mcu_info *info = dev_get_plat(dev);
|
||||
u8 val[2];
|
||||
int ret;
|
||||
u8 reg;
|
||||
|
||||
switch (offset) {
|
||||
/* bank 0 */
|
||||
case ilog2(STS_USB30_PWRON):
|
||||
reg = CMD_GENERAL_CONTROL;
|
||||
val[1] = CTL_USB30_PWRON;
|
||||
break;
|
||||
case ilog2(STS_USB31_PWRON):
|
||||
reg = CMD_GENERAL_CONTROL;
|
||||
val[1] = CTL_USB31_PWRON;
|
||||
break;
|
||||
case ilog2(STS_ENABLE_4V5):
|
||||
reg = CMD_GENERAL_CONTROL;
|
||||
val[1] = CTL_ENABLE_4V5;
|
||||
break;
|
||||
case ilog2(STS_BUTTON_MODE):
|
||||
reg = CMD_GENERAL_CONTROL;
|
||||
val[1] = CTL_BUTTON_MODE;
|
||||
break;
|
||||
|
||||
/* bank 2 - supported only when FEAT_EXT_CMDS is set */
|
||||
case (16 + 32 + 0) ... (16 + 32 + 15):
|
||||
if (!(info->features & FEAT_EXT_CMDS))
|
||||
return -EINVAL;
|
||||
reg = CMD_EXT_CONTROL;
|
||||
val[1] = BIT(offset - 16 - 32);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
val[0] = value ? val[1] : 0;
|
||||
|
||||
ret = dm_i2c_write(dev, reg, val, 2);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int turris_omnia_mcu_direction_input(struct udevice *dev, uint offset)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = turris_omnia_mcu_get_function(dev, offset);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != GPIOF_INPUT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int turris_omnia_mcu_direction_output(struct udevice *dev, uint offset, int value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = turris_omnia_mcu_get_function(dev, offset);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
else if (ret != GPIOF_OUTPUT)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return turris_omnia_mcu_set_value(dev, offset, value);
|
||||
}
|
||||
|
||||
static int turris_omnia_mcu_xlate(struct udevice *dev, struct gpio_desc *desc,
|
||||
struct ofnode_phandle_args *args)
|
||||
{
|
||||
uint bank, gpio, flags, offset;
|
||||
int ret;
|
||||
|
||||
if (args->args_count != 3)
|
||||
return -EINVAL;
|
||||
|
||||
bank = args->args[0];
|
||||
gpio = args->args[1];
|
||||
flags = args->args[2];
|
||||
|
||||
switch (bank) {
|
||||
case 0:
|
||||
if (gpio >= 16)
|
||||
return -EINVAL;
|
||||
offset = gpio;
|
||||
break;
|
||||
case 1:
|
||||
if (gpio >= 32)
|
||||
return -EINVAL;
|
||||
offset = 16 + gpio;
|
||||
break;
|
||||
case 2:
|
||||
if (gpio >= 16)
|
||||
return -EINVAL;
|
||||
offset = 16 + 32 + gpio;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = turris_omnia_mcu_get_function(dev, offset);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
desc->offset = offset;
|
||||
desc->flags = gpio_flags_xlate(flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_gpio_ops turris_omnia_mcu_ops = {
|
||||
.direction_input = turris_omnia_mcu_direction_input,
|
||||
.direction_output = turris_omnia_mcu_direction_output,
|
||||
.get_value = turris_omnia_mcu_get_value,
|
||||
.set_value = turris_omnia_mcu_set_value,
|
||||
.get_function = turris_omnia_mcu_get_function,
|
||||
.xlate = turris_omnia_mcu_xlate,
|
||||
};
|
||||
|
||||
static int turris_omnia_mcu_probe(struct udevice *dev)
|
||||
{
|
||||
struct turris_omnia_mcu_info *info = dev_get_plat(dev);
|
||||
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
u16 status;
|
||||
u8 val[2];
|
||||
int ret;
|
||||
|
||||
ret = dm_i2c_read(dev, CMD_GET_STATUS_WORD, val, 2);
|
||||
if (ret) {
|
||||
printf("Error: turris_omnia_mcu CMD_GET_STATUS_WORD failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
status = ((u16)val[1] << 8) | val[0];
|
||||
|
||||
if (status & STS_FEATURES_SUPPORTED) {
|
||||
ret = dm_i2c_read(dev, CMD_GET_FEATURES, val, 2);
|
||||
if (ret) {
|
||||
printf("Error: turris_omnia_mcu CMD_GET_FEATURES failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
info->features = ((u16)val[1] << 8) | val[0];
|
||||
}
|
||||
|
||||
uc_priv->bank_name = "mcu_";
|
||||
|
||||
if (info->features & FEAT_EXT_CMDS)
|
||||
uc_priv->gpio_count = 16 + 32 + 16;
|
||||
else
|
||||
uc_priv->gpio_count = 16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id turris_omnia_mcu_ids[] = {
|
||||
{ .compatible = "cznic,turris-omnia-mcu" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(turris_omnia_mcu) = {
|
||||
.name = "turris-omnia-mcu",
|
||||
.id = UCLASS_GPIO,
|
||||
.ops = &turris_omnia_mcu_ops,
|
||||
.probe = turris_omnia_mcu_probe,
|
||||
.plat_auto = sizeof(struct turris_omnia_mcu_info),
|
||||
.of_match = turris_omnia_mcu_ids,
|
||||
};
|
Loading…
Reference in a new issue