| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * (C) Copyright 2023 |
| * Svyatoslav Ryhel <clamor95@gmail.com> |
| */ |
| |
| #include <dm.h> |
| #include <dm/device_compat.h> |
| #include <dm/pinctrl.h> |
| #include <stdlib.h> |
| |
| #include <asm/arch/pinmux.h> |
| |
| static void tegra_pinctrl_set_pin(struct udevice *config) |
| { |
| int i, count, pin_id, ret; |
| int pull, tristate; |
| const char **pins; |
| |
| ret = dev_read_u32(config, "nvidia,pull", &pull); |
| if (ret) |
| pull = ret; |
| |
| ret = dev_read_u32(config, "nvidia,tristate", &tristate); |
| if (ret) |
| tristate = ret; |
| |
| count = dev_read_string_list(config, "nvidia,pins", &pins); |
| if (count < 0) { |
| log_debug("%s: could not parse property nvidia,pins\n", __func__); |
| return; |
| } |
| |
| for (i = 0; i < count; i++) { |
| for (pin_id = 0; pin_id < PMUX_PINGRP_COUNT; pin_id++) |
| if (tegra_pinctrl_to_pingrp[pin_id]) |
| if (!strcmp(pins[i], tegra_pinctrl_to_pingrp[pin_id])) |
| break; |
| |
| if (pull >= 0) |
| pinmux_set_pullupdown(pin_id, pull); |
| |
| if (tristate >= 0) { |
| if (!tristate) |
| pinmux_tristate_disable(pin_id); |
| else |
| pinmux_tristate_enable(pin_id); |
| } |
| } |
| |
| free(pins); |
| } |
| |
| static void tegra_pinctrl_set_func(struct udevice *config) |
| { |
| int i, count, func_id, pin_id; |
| const char *function; |
| const char **pins; |
| |
| function = dev_read_string(config, "nvidia,function"); |
| if (function) |
| for (i = 0; i < PMUX_FUNC_COUNT; i++) |
| if (tegra_pinctrl_to_func[i]) |
| if (!strcmp(function, tegra_pinctrl_to_func[i])) |
| break; |
| |
| func_id = i; |
| |
| count = dev_read_string_list(config, "nvidia,pins", &pins); |
| if (count < 0) { |
| log_debug("%s: could not parse property nvidia,pins\n", __func__); |
| return; |
| } |
| |
| for (i = 0; i < count; i++) { |
| for (pin_id = 0; pin_id < PMUX_PINGRP_COUNT; pin_id++) |
| if (tegra_pinctrl_to_pingrp[pin_id]) |
| if (!strcmp(pins[i], tegra_pinctrl_to_pingrp[pin_id])) |
| break; |
| |
| debug("%s(%d) muxed to %s(%d)\n", pins[i], pin_id, function, func_id); |
| |
| pinmux_set_func(pin_id, func_id); |
| } |
| |
| free(pins); |
| } |
| |
| static int tegra_pinctrl_set_state(struct udevice *dev, struct udevice *config) |
| { |
| struct udevice *child; |
| |
| device_foreach_child(child, config) { |
| /* |
| * Tegra20 pinmux is set differently then any other |
| * Tegra SOC. Nodes are arranged by function muxing, |
| * then actual pins setup (with node name prefix |
| * conf_*) and then drive setup. |
| */ |
| if (!strncmp(child->name, "conf_", 5)) |
| tegra_pinctrl_set_pin(child); |
| else if (!strncmp(child->name, "drive_", 6)) |
| debug("%s: drive configuration is not supported\n", __func__); |
| else |
| tegra_pinctrl_set_func(child); |
| } |
| |
| return 0; |
| } |
| |
| static int tegra_pinctrl_get_pins_count(struct udevice *dev) |
| { |
| return PMUX_PINGRP_COUNT; |
| } |
| |
| static const char *tegra_pinctrl_get_pin_name(struct udevice *dev, |
| unsigned int selector) |
| { |
| return tegra_pinctrl_to_pingrp[selector]; |
| } |
| |
| static int tegra_pinctrl_get_groups_count(struct udevice *dev) |
| { |
| return PMUX_DRVGRP_COUNT; |
| } |
| |
| static const char *tegra_pinctrl_get_group_name(struct udevice *dev, |
| unsigned int selector) |
| { |
| return tegra_pinctrl_to_drvgrp[selector]; |
| } |
| |
| static int tegra_pinctrl_get_functions_count(struct udevice *dev) |
| { |
| return PMUX_FUNC_COUNT; |
| } |
| |
| static const char *tegra_pinctrl_get_function_name(struct udevice *dev, |
| unsigned int selector) |
| { |
| return tegra_pinctrl_to_func[selector]; |
| } |
| |
| const struct pinctrl_ops tegra_pinctrl_ops = { |
| .get_pins_count = tegra_pinctrl_get_pins_count, |
| .get_pin_name = tegra_pinctrl_get_pin_name, |
| .get_groups_count = tegra_pinctrl_get_groups_count, |
| .get_group_name = tegra_pinctrl_get_group_name, |
| .get_functions_count = tegra_pinctrl_get_functions_count, |
| .get_function_name = tegra_pinctrl_get_function_name, |
| .set_state = tegra_pinctrl_set_state, |
| }; |
| |
| static int tegra_pinctrl_bind(struct udevice *dev) |
| { |
| /* |
| * Make sure that the pinctrl driver gets probed after binding |
| * to provide initial configuration and assure that further |
| * probed devices are working correctly. |
| */ |
| dev_or_flags(dev, DM_FLAG_PROBE_AFTER_BIND); |
| |
| return 0; |
| } |
| |
| static const struct udevice_id tegra_pinctrl_ids[] = { |
| { .compatible = "nvidia,tegra20-pinmux" }, |
| { }, |
| }; |
| |
| U_BOOT_DRIVER(tegra_pinctrl) = { |
| .name = "tegra_pinctrl", |
| .id = UCLASS_PINCTRL, |
| .of_match = tegra_pinctrl_ids, |
| .bind = tegra_pinctrl_bind, |
| .ops = &tegra_pinctrl_ops, |
| }; |