1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Qualcomm GPIO driver 4 * 5 * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> 6 */ 7 8 #include <common.h> 9 #include <dm.h> 10 #include <errno.h> 11 #include <asm/gpio.h> 12 #include <asm/io.h> 13 14 DECLARE_GLOBAL_DATA_PTR; 15 16 /* Register offsets */ 17 #define GPIO_CONFIG_OFF(no) ((no) * 0x1000) 18 #define GPIO_IN_OUT_OFF(no) ((no) * 0x1000 + 0x4) 19 20 /* OE */ 21 #define GPIO_OE_DISABLE (0x0 << 9) 22 #define GPIO_OE_ENABLE (0x1 << 9) 23 #define GPIO_OE_MASK (0x1 << 9) 24 25 /* GPIO_IN_OUT register shifts. */ 26 #define GPIO_IN 0 27 #define GPIO_OUT 1 28 29 struct msm_gpio_bank { 30 phys_addr_t base; 31 }; 32 33 static int msm_gpio_direction_input(struct udevice *dev, unsigned int gpio) 34 { 35 struct msm_gpio_bank *priv = dev_get_priv(dev); 36 phys_addr_t reg = priv->base + GPIO_CONFIG_OFF(gpio); 37 38 /* Disable OE bit */ 39 clrsetbits_le32(reg, GPIO_OE_MASK, GPIO_OE_DISABLE); 40 41 return 0; 42 } 43 44 static int msm_gpio_set_value(struct udevice *dev, unsigned gpio, int value) 45 { 46 struct msm_gpio_bank *priv = dev_get_priv(dev); 47 48 value = !!value; 49 /* set value */ 50 writel(value << GPIO_OUT, priv->base + GPIO_IN_OUT_OFF(gpio)); 51 52 return 0; 53 } 54 55 static int msm_gpio_direction_output(struct udevice *dev, unsigned gpio, 56 int value) 57 { 58 struct msm_gpio_bank *priv = dev_get_priv(dev); 59 phys_addr_t reg = priv->base + GPIO_CONFIG_OFF(gpio); 60 61 value = !!value; 62 /* set value */ 63 writel(value << GPIO_OUT, priv->base + GPIO_IN_OUT_OFF(gpio)); 64 /* switch direction */ 65 clrsetbits_le32(reg, GPIO_OE_MASK, GPIO_OE_ENABLE); 66 67 return 0; 68 } 69 70 static int msm_gpio_get_value(struct udevice *dev, unsigned gpio) 71 { 72 struct msm_gpio_bank *priv = dev_get_priv(dev); 73 74 return !!(readl(priv->base + GPIO_IN_OUT_OFF(gpio)) >> GPIO_IN); 75 } 76 77 static int msm_gpio_get_function(struct udevice *dev, unsigned offset) 78 { 79 struct msm_gpio_bank *priv = dev_get_priv(dev); 80 81 if (readl(priv->base + GPIO_CONFIG_OFF(offset)) & GPIO_OE_ENABLE) 82 return GPIOF_OUTPUT; 83 84 return GPIOF_INPUT; 85 } 86 87 static const struct dm_gpio_ops gpio_msm_ops = { 88 .direction_input = msm_gpio_direction_input, 89 .direction_output = msm_gpio_direction_output, 90 .get_value = msm_gpio_get_value, 91 .set_value = msm_gpio_set_value, 92 .get_function = msm_gpio_get_function, 93 }; 94 95 static int msm_gpio_probe(struct udevice *dev) 96 { 97 struct msm_gpio_bank *priv = dev_get_priv(dev); 98 99 priv->base = devfdt_get_addr(dev); 100 101 return priv->base == FDT_ADDR_T_NONE ? -EINVAL : 0; 102 } 103 104 static int msm_gpio_ofdata_to_platdata(struct udevice *dev) 105 { 106 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); 107 108 uc_priv->gpio_count = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), 109 "gpio-count", 0); 110 uc_priv->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev), 111 "gpio-bank-name", NULL); 112 if (uc_priv->bank_name == NULL) 113 uc_priv->bank_name = "soc"; 114 115 return 0; 116 } 117 118 static const struct udevice_id msm_gpio_ids[] = { 119 { .compatible = "qcom,msm8916-pinctrl" }, 120 { .compatible = "qcom,apq8016-pinctrl" }, 121 { } 122 }; 123 124 U_BOOT_DRIVER(gpio_msm) = { 125 .name = "gpio_msm", 126 .id = UCLASS_GPIO, 127 .of_match = msm_gpio_ids, 128 .ofdata_to_platdata = msm_gpio_ofdata_to_platdata, 129 .probe = msm_gpio_probe, 130 .ops = &gpio_msm_ops, 131 .priv_auto_alloc_size = sizeof(struct msm_gpio_bank), 132 }; 133