1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2017 Álvaro Fernández Rojas <noltari@gmail.com> 4 * 5 * Derived from linux/arch/mips/bcm63xx/gpio.c: 6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> 7 * Copyright (C) 2008-2011 Florian Fainelli <florian@openwrt.org> 8 */ 9 10 #include <common.h> 11 #include <dm.h> 12 #include <errno.h> 13 #include <asm/gpio.h> 14 #include <asm/io.h> 15 16 DECLARE_GLOBAL_DATA_PTR; 17 18 struct bcm6345_gpio_priv { 19 void __iomem *reg_dirout; 20 void __iomem *reg_data; 21 }; 22 23 static int bcm6345_gpio_get_value(struct udevice *dev, unsigned offset) 24 { 25 struct bcm6345_gpio_priv *priv = dev_get_priv(dev); 26 27 return !!(readl_be(priv->reg_data) & BIT(offset)); 28 } 29 30 static int bcm6345_gpio_set_value(struct udevice *dev, unsigned offset, 31 int value) 32 { 33 struct bcm6345_gpio_priv *priv = dev_get_priv(dev); 34 35 if (value) 36 setbits_be32(priv->reg_data, BIT(offset)); 37 else 38 clrbits_be32(priv->reg_data, BIT(offset)); 39 40 return 0; 41 } 42 43 static int bcm6345_gpio_set_direction(void __iomem *dirout, unsigned offset, 44 bool input) 45 { 46 if (input) 47 clrbits_be32(dirout, BIT(offset)); 48 else 49 setbits_be32(dirout, BIT(offset)); 50 51 return 0; 52 } 53 54 static int bcm6345_gpio_direction_input(struct udevice *dev, unsigned offset) 55 { 56 struct bcm6345_gpio_priv *priv = dev_get_priv(dev); 57 58 return bcm6345_gpio_set_direction(priv->reg_dirout, offset, 1); 59 } 60 61 static int bcm6345_gpio_direction_output(struct udevice *dev, unsigned offset, 62 int value) 63 { 64 struct bcm6345_gpio_priv *priv = dev_get_priv(dev); 65 66 bcm6345_gpio_set_value(dev, offset, value); 67 68 return bcm6345_gpio_set_direction(priv->reg_dirout, offset, 0); 69 } 70 71 static int bcm6345_gpio_get_function(struct udevice *dev, unsigned offset) 72 { 73 struct bcm6345_gpio_priv *priv = dev_get_priv(dev); 74 75 if (readl_be(priv->reg_dirout) & BIT(offset)) 76 return GPIOF_OUTPUT; 77 else 78 return GPIOF_INPUT; 79 } 80 81 static const struct dm_gpio_ops bcm6345_gpio_ops = { 82 .direction_input = bcm6345_gpio_direction_input, 83 .direction_output = bcm6345_gpio_direction_output, 84 .get_value = bcm6345_gpio_get_value, 85 .set_value = bcm6345_gpio_set_value, 86 .get_function = bcm6345_gpio_get_function, 87 }; 88 89 static int bcm6345_gpio_probe(struct udevice *dev) 90 { 91 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); 92 struct bcm6345_gpio_priv *priv = dev_get_priv(dev); 93 fdt_addr_t data_addr, dirout_addr; 94 fdt_size_t data_size, dirout_size; 95 96 dirout_addr = devfdt_get_addr_size_index(dev, 0, &dirout_size); 97 if (dirout_addr == FDT_ADDR_T_NONE) 98 return -EINVAL; 99 100 data_addr = devfdt_get_addr_size_index(dev, 1, &data_size); 101 if (data_addr == FDT_ADDR_T_NONE) 102 return -EINVAL; 103 104 priv->reg_data = ioremap(data_addr, data_size); 105 priv->reg_dirout = ioremap(dirout_addr, dirout_size); 106 107 uc_priv->gpio_count = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(dev), 108 "ngpios", 32); 109 uc_priv->bank_name = dev->name; 110 111 return 0; 112 } 113 114 static const struct udevice_id bcm6345_gpio_ids[] = { 115 { .compatible = "brcm,bcm6345-gpio" }, 116 { /* sentinel */ } 117 }; 118 119 U_BOOT_DRIVER(bcm6345_gpio) = { 120 .name = "bcm6345-gpio", 121 .id = UCLASS_GPIO, 122 .of_match = bcm6345_gpio_ids, 123 .ops = &bcm6345_gpio_ops, 124 .priv_auto_alloc_size = sizeof(struct bcm6345_gpio_priv), 125 .probe = bcm6345_gpio_probe, 126 }; 127