sifive-gpio.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * SiFive GPIO driver
  4. *
  5. * Copyright (C) 2019 SiFive, Inc.
  6. */
  7. #include <common.h>
  8. #include <dm.h>
  9. #include <asm/arch/gpio.h>
  10. #include <asm/io.h>
  11. #include <errno.h>
  12. #include <asm/gpio.h>
  13. #include <linux/bitops.h>
  14. static int sifive_gpio_probe(struct udevice *dev)
  15. {
  16. struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
  17. struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
  18. char name[18], *str;
  19. sprintf(name, "gpio@%4lx_", (uintptr_t)plat->base);
  20. str = strdup(name);
  21. if (!str)
  22. return -ENOMEM;
  23. uc_priv->bank_name = str;
  24. /*
  25. * Use the gpio count mentioned in device tree,
  26. * if not specified in dt, set NR_GPIOS as default
  27. */
  28. uc_priv->gpio_count = dev_read_u32_default(dev, "ngpios", NR_GPIOS);
  29. return 0;
  30. }
  31. static void sifive_update_gpio_reg(void *bptr, u32 offset, bool value)
  32. {
  33. void __iomem *ptr = (void __iomem *)bptr;
  34. u32 bit = BIT(offset);
  35. u32 old = readl(ptr);
  36. if (value)
  37. writel(old | bit, ptr);
  38. else
  39. writel(old & ~bit, ptr);
  40. }
  41. static int sifive_gpio_direction_input(struct udevice *dev, u32 offset)
  42. {
  43. struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
  44. struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
  45. if (offset > uc_priv->gpio_count)
  46. return -EINVAL;
  47. /* Configure gpio direction as input */
  48. sifive_update_gpio_reg(plat->base + GPIO_INPUT_EN, offset, true);
  49. sifive_update_gpio_reg(plat->base + GPIO_OUTPUT_EN, offset, false);
  50. return 0;
  51. }
  52. static int sifive_gpio_direction_output(struct udevice *dev, u32 offset,
  53. int value)
  54. {
  55. struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
  56. struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
  57. if (offset > uc_priv->gpio_count)
  58. return -EINVAL;
  59. /* Configure gpio direction as output */
  60. sifive_update_gpio_reg(plat->base + GPIO_OUTPUT_EN, offset, true);
  61. sifive_update_gpio_reg(plat->base + GPIO_INPUT_EN, offset, false);
  62. /* Set the output state of the pin */
  63. sifive_update_gpio_reg(plat->base + GPIO_OUTPUT_VAL, offset, value);
  64. return 0;
  65. }
  66. static int sifive_gpio_get_value(struct udevice *dev, u32 offset)
  67. {
  68. struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
  69. struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
  70. int val;
  71. int dir;
  72. if (offset > uc_priv->gpio_count)
  73. return -EINVAL;
  74. /* Get direction of the pin */
  75. dir = !(readl(plat->base + GPIO_OUTPUT_EN) & BIT(offset));
  76. if (dir)
  77. val = readl(plat->base + GPIO_INPUT_VAL) & BIT(offset);
  78. else
  79. val = readl(plat->base + GPIO_OUTPUT_VAL) & BIT(offset);
  80. return val ? HIGH : LOW;
  81. }
  82. static int sifive_gpio_set_value(struct udevice *dev, u32 offset, int value)
  83. {
  84. struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
  85. struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
  86. if (offset > uc_priv->gpio_count)
  87. return -EINVAL;
  88. sifive_update_gpio_reg(plat->base + GPIO_OUTPUT_VAL, offset, value);
  89. return 0;
  90. }
  91. static int sifive_gpio_get_function(struct udevice *dev, unsigned int offset)
  92. {
  93. struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
  94. u32 outdir, indir, val;
  95. struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
  96. if (offset > uc_priv->gpio_count)
  97. return -1;
  98. /* Get direction of the pin */
  99. outdir = readl(plat->base + GPIO_OUTPUT_EN) & BIT(offset);
  100. indir = readl(plat->base + GPIO_INPUT_EN) & BIT(offset);
  101. if (outdir)
  102. /* Pin at specified offset is configured as output */
  103. val = GPIOF_OUTPUT;
  104. else if (indir)
  105. /* Pin at specified offset is configured as input */
  106. val = GPIOF_INPUT;
  107. else
  108. /*The requested GPIO is not set as input or output */
  109. val = GPIOF_UNUSED;
  110. return val;
  111. }
  112. static const struct udevice_id sifive_gpio_match[] = {
  113. { .compatible = "sifive,gpio0" },
  114. { }
  115. };
  116. static const struct dm_gpio_ops sifive_gpio_ops = {
  117. .direction_input = sifive_gpio_direction_input,
  118. .direction_output = sifive_gpio_direction_output,
  119. .get_value = sifive_gpio_get_value,
  120. .set_value = sifive_gpio_set_value,
  121. .get_function = sifive_gpio_get_function,
  122. };
  123. static int sifive_gpio_ofdata_to_platdata(struct udevice *dev)
  124. {
  125. struct sifive_gpio_platdata *plat = dev_get_platdata(dev);
  126. fdt_addr_t addr;
  127. addr = dev_read_addr(dev);
  128. if (addr == FDT_ADDR_T_NONE)
  129. return -EINVAL;
  130. plat->base = (void *)addr;
  131. return 0;
  132. }
  133. U_BOOT_DRIVER(gpio_sifive) = {
  134. .name = "gpio_sifive",
  135. .id = UCLASS_GPIO,
  136. .of_match = sifive_gpio_match,
  137. .ofdata_to_platdata = of_match_ptr(sifive_gpio_ofdata_to_platdata),
  138. .platdata_auto_alloc_size = sizeof(struct sifive_gpio_platdata),
  139. .ops = &sifive_gpio_ops,
  140. .probe = sifive_gpio_probe,
  141. };