1 /* 2 * Copyright (C) 2015 3 * Bhuvanchandra DV, Toradex, Inc. 4 * 5 * SPDX-License-Identifier: GPL-2.0+ 6 */ 7 8 #include <common.h> 9 #include <dm.h> 10 #include <errno.h> 11 #include <fdtdec.h> 12 #include <asm/gpio.h> 13 #include <asm/imx-common/iomux-v3.h> 14 #include <asm/io.h> 15 #include <malloc.h> 16 17 DECLARE_GLOBAL_DATA_PTR; 18 19 struct vybrid_gpios { 20 unsigned int chip; 21 struct vybrid_gpio_regs *reg; 22 }; 23 24 static int vybrid_gpio_direction_input(struct udevice *dev, unsigned gpio) 25 { 26 const struct vybrid_gpios *gpios = dev_get_priv(dev); 27 28 gpio = gpio + (gpios->chip * VYBRID_GPIO_COUNT); 29 imx_iomux_gpio_set_direction(gpio, VF610_GPIO_DIRECTION_IN); 30 31 return 0; 32 } 33 34 static int vybrid_gpio_direction_output(struct udevice *dev, unsigned gpio, 35 int value) 36 { 37 const struct vybrid_gpios *gpios = dev_get_priv(dev); 38 39 gpio = gpio + (gpios->chip * VYBRID_GPIO_COUNT); 40 gpio_set_value(gpio, value); 41 imx_iomux_gpio_set_direction(gpio, VF610_GPIO_DIRECTION_OUT); 42 43 return 0; 44 } 45 46 static int vybrid_gpio_get_value(struct udevice *dev, unsigned gpio) 47 { 48 const struct vybrid_gpios *gpios = dev_get_priv(dev); 49 50 return ((readl(&gpios->reg->gpio_pdir) & (1 << gpio))) ? 1 : 0; 51 } 52 53 static int vybrid_gpio_set_value(struct udevice *dev, unsigned gpio, 54 int value) 55 { 56 const struct vybrid_gpios *gpios = dev_get_priv(dev); 57 if (value) 58 writel((1 << gpio), &gpios->reg->gpio_psor); 59 else 60 writel((1 << gpio), &gpios->reg->gpio_pcor); 61 62 return 0; 63 } 64 65 static int vybrid_gpio_get_function(struct udevice *dev, unsigned gpio) 66 { 67 const struct vybrid_gpios *gpios = dev_get_priv(dev); 68 u32 g_state = 0; 69 70 gpio = gpio + (gpios->chip * VYBRID_GPIO_COUNT); 71 72 imx_iomux_gpio_get_function(gpio, &g_state); 73 74 if (((g_state & (0x07 << PAD_MUX_MODE_SHIFT)) >> PAD_MUX_MODE_SHIFT) > 0) 75 return GPIOF_FUNC; 76 if (g_state & PAD_CTL_OBE_ENABLE) 77 return GPIOF_OUTPUT; 78 if (g_state & PAD_CTL_IBE_ENABLE) 79 return GPIOF_INPUT; 80 if (!(g_state & PAD_CTL_OBE_IBE_ENABLE)) 81 return GPIOF_UNUSED; 82 83 return GPIOF_UNKNOWN; 84 } 85 86 static const struct dm_gpio_ops gpio_vybrid_ops = { 87 .direction_input = vybrid_gpio_direction_input, 88 .direction_output = vybrid_gpio_direction_output, 89 .get_value = vybrid_gpio_get_value, 90 .set_value = vybrid_gpio_set_value, 91 .get_function = vybrid_gpio_get_function, 92 }; 93 94 static int vybrid_gpio_probe(struct udevice *dev) 95 { 96 struct vybrid_gpios *gpios = dev_get_priv(dev); 97 struct vybrid_gpio_platdata *plat = dev_get_platdata(dev); 98 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); 99 100 uc_priv->bank_name = plat->port_name; 101 uc_priv->gpio_count = VYBRID_GPIO_COUNT; 102 gpios->reg = (struct vybrid_gpio_regs *)plat->base; 103 gpios->chip = plat->chip; 104 105 return 0; 106 } 107 108 static int vybrid_gpio_bind(struct udevice *dev) 109 { 110 struct vybrid_gpio_platdata *plat = dev->platdata; 111 fdt_addr_t base_addr; 112 113 if (plat) 114 return 0; 115 116 base_addr = devfdt_get_addr(dev); 117 if (base_addr == FDT_ADDR_T_NONE) 118 return -ENODEV; 119 120 /* 121 * TODO: 122 * When every board is converted to driver model and DT is 123 * supported, this can be done by auto-alloc feature, but 124 * not using calloc to alloc memory for platdata. 125 */ 126 plat = calloc(1, sizeof(*plat)); 127 if (!plat) 128 return -ENOMEM; 129 130 plat->base = base_addr; 131 plat->chip = dev->req_seq; 132 plat->port_name = fdt_get_name(gd->fdt_blob, dev_of_offset(dev), NULL); 133 dev->platdata = plat; 134 135 return 0; 136 } 137 138 static const struct udevice_id vybrid_gpio_ids[] = { 139 { .compatible = "fsl,vf610-gpio" }, 140 { } 141 }; 142 143 U_BOOT_DRIVER(gpio_vybrid) = { 144 .name = "gpio_vybrid", 145 .id = UCLASS_GPIO, 146 .ops = &gpio_vybrid_ops, 147 .probe = vybrid_gpio_probe, 148 .priv_auto_alloc_size = sizeof(struct vybrid_gpios), 149 .of_match = vybrid_gpio_ids, 150 .bind = vybrid_gpio_bind, 151 }; 152