Commit 74ebeb68 authored by Sergei Miroshnichenko's avatar Sergei Miroshnichenko

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>;
};
parent 28662174
......@@ -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
......
#
# Copyright (C) 2017 Emcraft Systems
# Sergei Miroshnichenko <sergeimir@emcraft.com>
#
# 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
#########################################################################
/*
* Copyright (C) 2017 Emcraft Systems
* Sergei Miroshnichenko <sergeimir@emcraft.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <fdt.h>
#include <libfdt.h>
#include <fdt_support.h>
#include <asm/arch/stm32f2_gpio.h>
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__);
}
}
......@@ -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
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment