From 74ebeb68aae08d8c8193027fa0b7d42304d63058 Mon Sep 17 00:00:00 2001 From: Sergei Miroshnichenko Date: Tue, 21 Feb 2017 17:21:58 +0300 Subject: [PATCH] RM#1313 fdt: stm32: switch: Add run-time gpio-based status switching of DT nodes. Add a new driver named "switch" for run-time selection of wanted devices in DT based on gpios: configuration_switch: configuration_switch { compatible = "emcraft,configuration-switch"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_switch>; }; ... pinctrl_switch: switch { st,pins { /* e5 = 4*16 + 5 = 69 */ status_pullup = <&gpioe 5 IN PULL_UP PUSH_PULL LOW_SPEED>; }; }; ... &usb_fs { ... configuration-switch-gpios = <&gpioe 5 GPIO_ACTIVE_LOW>; }; --- Makefile | 1 + drivers/switch/Makefile | 30 ++++++ drivers/switch/switch_stm32.c | 172 ++++++++++++++++++++++++++++++++++ include/fdt_support.h | 4 + lib_arm/bootm.c | 3 + 5 files changed, 210 insertions(+) create mode 100644 drivers/switch/Makefile create mode 100644 drivers/switch/switch_stm32.c diff --git a/Makefile b/Makefile index 96175ec..f281ca3 100644 --- a/Makefile +++ b/Makefile @@ -222,6 +222,7 @@ LIBS += drivers/pci/libpci.a LIBS += drivers/pcmcia/libpcmcia.a LIBS += drivers/power/libpower.a LIBS += drivers/spi/libspi.a +LIBS += drivers/switch/libswitch.a ifeq ($(CPU),mpc83xx) LIBS += drivers/qe/qe.a endif diff --git a/drivers/switch/Makefile b/drivers/switch/Makefile new file mode 100644 index 0000000..c302e0b --- /dev/null +++ b/drivers/switch/Makefile @@ -0,0 +1,30 @@ +# +# Copyright (C) 2017 Emcraft Systems +# Sergei Miroshnichenko +# +# SPDX-License-Identifier: GPL-2.0+ +# + +include $(TOPDIR)/config.mk + +LIB := $(obj)libswitch.a + +COBJS-$(CONFIG_SWITCH_STM32) += switch_stm32.o + +COBJS := $(COBJS-y) +SRCS := $(COBJS:.o=.c) +OBJS := $(addprefix $(obj),$(COBJS)) + +all: $(LIB) + +$(LIB): $(obj).depend $(OBJS) + $(AR) $(ARFLAGS) $@ $(OBJS) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/switch/switch_stm32.c b/drivers/switch/switch_stm32.c new file mode 100644 index 0000000..293d4c6 --- /dev/null +++ b/drivers/switch/switch_stm32.c @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2017 Emcraft Systems + * Sergei Miroshnichenko + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include + +struct gpio_banks { + const char *name; + enum stm32f2_gpio_port port; +}; + +static struct gpio_banks gpio_banks[] = { + { "GPIOA", STM32F2_GPIO_PORT_A }, + { "GPIOB", STM32F2_GPIO_PORT_B }, + { "GPIOC", STM32F2_GPIO_PORT_C }, + { "GPIOD", STM32F2_GPIO_PORT_D }, + { "GPIOE", STM32F2_GPIO_PORT_E }, + { "GPIOF", STM32F2_GPIO_PORT_F }, + { "GPIOG", STM32F2_GPIO_PORT_G }, + { "GPIOH", STM32F2_GPIO_PORT_H }, + { "GPIOI", STM32F2_GPIO_PORT_I }, + { "GPIOJ", STM32F2_GPIO_PORT_J }, + { "GPIOK", STM32F2_GPIO_PORT_K }, +}; + +void fdt_fixup_switch(void *fdt) +{ + int switch_node = fdt_node_offset_by_compatible(fdt, -1, "gpio-configuration-switch"); + + if (switch_node != -FDT_ERR_NOTFOUND) { + int pinctrl_node, pins_node, offset, nextoffset; + const u32 *pinctrl_h; + uint32_t tag; + + pinctrl_h = fdt_getprop(fdt, switch_node, "pinctrl-0", NULL); + if (!pinctrl_h) { + error("%s: no pinctrl defined for the switch node\n", __func__); + return; + } + + pinctrl_node = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(*pinctrl_h)); + /* + * pins_node is the "st,pins" node + */ + pins_node = fdt_next_node(fdt, pinctrl_node, NULL); + + /* + * Set up pinctrl for the signal gpios: pullups, etc. + */ + offset = pins_node; + while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END_NODE) { + if (tag == FDT_PROP) { + const int pin_size = 6; + const struct fdt_property *fdt_prop = fdt_offset_ptr(fdt, offset, + sizeof(*fdt_prop)); + int len = fdt32_to_cpu(fdt_prop->len); + u32 *v = (u32*)fdt_prop->data; + int gpio_bank_off; + const char *bank_name; + int i, port = -1; + struct stm32f2_gpio_dsc iomux; + int err; + /* + * Example of data format: + * status_pullup = <&gpioe 5 IN PULL_UP PUSH_PULL LOW_SPEED>; + * v[0] - gpio bank + * v[1] - gpio pin + * v[2] - direction + * v[3] - NO_PULL/PULL_UP/PULL_DOWN + * v[4] - PUSH_PULL/OPEN_DRAIN + * v[5] - speed + */ + int pin = fdt32_to_cpu(v[1]); + int direction_in = !fdt32_to_cpu(v[2]); + int pullup = fdt32_to_cpu(v[3]) == 1; + + if (len % (pin_size * sizeof(u32))) { + printf("%s: invalid pinctrl value\n", __func__); + } + gpio_bank_off = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(v[0])); + bank_name = (const char *)fdt_getprop(fdt, gpio_bank_off, "st,bank-name", NULL); + for (i = 0; i < sizeof(gpio_banks)/sizeof(gpio_banks[0]); ++i) { + if (0 == strcmp(bank_name, gpio_banks[i].name)) { + port = gpio_banks[i].port; + break; + } + } + + if (port < 0) { + error("%s: unsupported bank name: %s\n", __func__, bank_name); + return; + } + + if (!direction_in) { + error("%s: only input switch gpios are supported\n", __func__); + return; + } + + iomux.port = port; + iomux.pin = pin; + err = stm32f2_gpio_config(&iomux, pullup ? STM32F2_GPIO_ROLE_GPIN_PULLUP : STM32F2_GPIO_ROLE_GPIN); + if (err) { + error("%s: gpio configuration failed: %d\n", __func__, err); + return; + } + } + offset = nextoffset; + } + + /* + * Delay for the gpios to establish + */ + udelay(200); + + /* + * Find every node with the "configuration-switch-gpios" property set and change its status accordingly + */ + for (offset = fdt_next_node(fdt, -1, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + int len; + struct fdt_property *prop = fdt_get_property_w(fdt, offset, "configuration-switch-gpios", &len); + if (prop) { + const int pin_size = 3; + int len = fdt32_to_cpu(prop->len); + u32 *v = (u32*)prop->data; + int port = -1; + int pin = fdt32_to_cpu(v[1]); + int active_low = !!fdt32_to_cpu(v[2]); + int gpio_value; + struct stm32f2_gpio_dsc iomux; + int i; + int gpio_bank_off = fdt_node_offset_by_phandle(fdt, fdt32_to_cpu(v[0])); + const char *bank_name = (const char *)fdt_getprop(fdt, gpio_bank_off, "st,bank-name", NULL); + const char *status; + for (i = 0; i < sizeof(gpio_banks)/sizeof(gpio_banks[0]); ++i) { + if (0 == strcmp(bank_name, gpio_banks[i].name)) { + port = gpio_banks[i].port; + break; + } + } + + if (port < 0) { + error("%s: unsupported bank name: %s\n", __func__, bank_name); + return; + } + + if (len % (pin_size * sizeof(u32))) { + printf("%s: invalid pinctrl value\n", __func__); + } + + iomux.port = port; + iomux.pin = pin; + gpio_value = stm32f2_gpout_get(&iomux); + status = ((gpio_value && !active_low) || (!gpio_value && active_low)) ? "okay" : "disabled"; + printf("%s: Setting node %s to \"%s\" as instructed by gpio %d.%d (value %d, active %s)\n", + __func__, fdt_get_name(fdt, offset, NULL), status, + port, pin, gpio_value, active_low ? "low" : "high"); + fdt_setprop(fdt, offset, "status", status, strlen(status) + 1); + } + } + } else { + debug("%s: no switch node\n", __func__); + } +} diff --git a/include/fdt_support.h b/include/fdt_support.h index 57c506d..3fcfb23 100644 --- a/include/fdt_support.h +++ b/include/fdt_support.h @@ -58,6 +58,10 @@ void fdt_fixup_qe_firmware(void *fdt); void fdt_fixup_dmamem(void *fdt); #endif +#ifdef CONFIG_SWITCH +void fdt_fixup_switch(void *fdt); +#endif + #ifdef CONFIG_HAS_FSL_DR_USB void fdt_fixup_dr_usb(void *blob, bd_t *bd); #else diff --git a/lib_arm/bootm.c b/lib_arm/bootm.c index c812894..a0bf242 100644 --- a/lib_arm/bootm.c +++ b/lib_arm/bootm.c @@ -235,6 +235,9 @@ static int bootm_linux_fdt(int machid, bootm_headers_t *images) #ifdef CONFIG_DMAMEM fdt_fixup_dmamem(*of_flat_tree); #endif +#ifdef CONFIG_SWITCH + fdt_fixup_switch(*of_flat_tree); +#endif fdt_initrd(*of_flat_tree, *initrd_start, *initrd_end, 1); -- GitLab