1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2018 Alexander Graf <agraf@suse.de> 4 * 5 * Based on drivers/pinctrl/mvebu/pinctrl-mvebu.c and 6 * drivers/gpio/bcm2835_gpio.c 7 * 8 * This driver gets instantiated by the GPIO driver, because both devices 9 * share the same device node. 10 * https://spdx.org/licenses 11 */ 12 13#include <config.h> 14#include <errno.h> 15#include <dm.h> 16#include <log.h> 17#include <dm/pinctrl.h> 18#include <dm/root.h> 19#include <dm/device-internal.h> 20#include <dm/lists.h> 21#include <asm/system.h> 22#include <asm/io.h> 23#include <asm/gpio.h> 24 25struct bcm283x_pinctrl_priv { 26 u32 *base_reg; 27}; 28 29#define MAX_PINS_PER_BANK 16 30 31static void bcm2835_gpio_set_func_id(struct udevice *dev, unsigned int gpio, 32 int func) 33{ 34 struct bcm283x_pinctrl_priv *priv = dev_get_priv(dev); 35 int reg_offset; 36 int field_offset; 37 38 reg_offset = BCM2835_GPIO_FSEL_BANK(gpio); 39 field_offset = BCM2835_GPIO_FSEL_SHIFT(gpio); 40 41 clrsetbits_le32(&priv->base_reg[reg_offset], 42 BCM2835_GPIO_FSEL_MASK << field_offset, 43 (func & BCM2835_GPIO_FSEL_MASK) << field_offset); 44} 45 46static int bcm2835_gpio_get_func_id(struct udevice *dev, unsigned int gpio) 47{ 48 struct bcm283x_pinctrl_priv *priv = dev_get_priv(dev); 49 u32 val; 50 51 val = readl(&priv->base_reg[BCM2835_GPIO_FSEL_BANK(gpio)]); 52 53 return (val >> BCM2835_GPIO_FSEL_SHIFT(gpio) & BCM2835_GPIO_FSEL_MASK); 54} 55 56/* 57 * bcm283x_pinctrl_set_state: configure pin functions. 58 * @dev: the pinctrl device to be configured. 59 * @config: the state to be configured. 60 * @return: 0 in success 61 */ 62int bcm283x_pinctrl_set_state(struct udevice *dev, struct udevice *config) 63{ 64 u32 pin_arr[MAX_PINS_PER_BANK]; 65 int function; 66 int i, len, pin_count = 0; 67 68 if (!dev_read_prop(config, "brcm,pins", &len) || !len || 69 len & 0x3 || dev_read_u32_array(config, "brcm,pins", pin_arr, 70 len / sizeof(u32))) { 71 debug("Failed reading pins array for pinconfig %s (%d)\n", 72 config->name, len); 73 return -EINVAL; 74 } 75 76 pin_count = len / sizeof(u32); 77 78 function = dev_read_u32_default(config, "brcm,function", -1); 79 if (function < 0) { 80 debug("Failed reading function for pinconfig %s (%d)\n", 81 config->name, function); 82 return -EINVAL; 83 } 84 85 for (i = 0; i < pin_count; i++) 86 bcm2835_gpio_set_func_id(dev, pin_arr[i], function); 87 88 return 0; 89} 90 91static int bcm283x_pinctrl_get_gpio_mux(struct udevice *dev, int banknum, 92 int index) 93{ 94 if (banknum != 0) 95 return -EINVAL; 96 97 return bcm2835_gpio_get_func_id(dev, index); 98} 99 100static const struct udevice_id bcm2835_pinctrl_id[] = { 101 {.compatible = "brcm,bcm2835-gpio"}, 102 {.compatible = "brcm,bcm2711-gpio"}, 103 {} 104}; 105 106int bcm283x_pinctl_of_to_plat(struct udevice *dev) 107{ 108 struct bcm283x_pinctrl_priv *priv; 109 110 priv = dev_get_priv(dev); 111 112 priv->base_reg = dev_read_addr_ptr(dev); 113 if (!priv->base_reg) { 114 debug("%s: Failed to get base address\n", __func__); 115 return -EINVAL; 116 } 117 118 return 0; 119} 120 121int bcm283x_pinctl_probe(struct udevice *dev) 122{ 123 int ret; 124 struct udevice *pdev; 125 126 /* Create GPIO device as well */ 127 ret = device_bind(dev, lists_driver_lookup_name("gpio_bcm2835"), 128 "gpio_bcm2835", NULL, dev_ofnode(dev), &pdev); 129 if (ret) { 130 /* 131 * While we really want the pinctrl driver to work to make 132 * devices go where they should go, the GPIO controller is 133 * not quite as crucial as it's only rarely used, so don't 134 * fail here. 135 */ 136 printf("Failed to bind GPIO driver\n"); 137 } 138 139 return 0; 140} 141 142static struct pinctrl_ops bcm283x_pinctrl_ops = { 143 .set_state = bcm283x_pinctrl_set_state, 144 .get_gpio_mux = bcm283x_pinctrl_get_gpio_mux, 145}; 146 147U_BOOT_DRIVER(pinctrl_bcm283x) = { 148 .name = "bcm283x_pinctrl", 149 .id = UCLASS_PINCTRL, 150 .of_match = of_match_ptr(bcm2835_pinctrl_id), 151 .of_to_plat = bcm283x_pinctl_of_to_plat, 152 .priv_auto = sizeof(struct bcm283x_pinctrl_priv), 153 .ops = &bcm283x_pinctrl_ops, 154 .probe = bcm283x_pinctl_probe, 155#if IS_ENABLED(CONFIG_OF_BOARD) 156 .flags = DM_FLAG_PRE_RELOC, 157#endif 158}; 159