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