]>
Commit | Line | Data |
---|---|---|
b9a66b63 | 1 | /* |
4e3d8406 MY |
2 | * Copyright (C) 2016 Socionext Inc. |
3 | * Author: Masahiro Yamada <yamada.masahiro@socionext.com> | |
b9a66b63 MY |
4 | * |
5 | * SPDX-License-Identifier: GPL-2.0+ | |
6 | */ | |
7 | ||
8 | #include <common.h> | |
9d922450 | 9 | #include <dm.h> |
b9a66b63 MY |
10 | #include <linux/bitops.h> |
11 | #include <linux/io.h> | |
bc82a131 | 12 | #include <linux/sizes.h> |
1221ce45 | 13 | #include <linux/errno.h> |
b9a66b63 MY |
14 | #include <asm/gpio.h> |
15 | ||
16 | #define UNIPHIER_GPIO_PORTS_PER_BANK 8 | |
17 | ||
18 | #define UNIPHIER_GPIO_REG_DATA 0 /* data */ | |
19 | #define UNIPHIER_GPIO_REG_DIR 4 /* direction (1:in, 0:out) */ | |
20 | ||
21 | struct uniphier_gpio_priv { | |
22 | void __iomem *base; | |
23 | char bank_name[16]; | |
24 | }; | |
25 | ||
26 | static void uniphier_gpio_offset_write(struct udevice *dev, unsigned offset, | |
27 | unsigned reg, int value) | |
28 | { | |
29 | struct uniphier_gpio_priv *priv = dev_get_priv(dev); | |
30 | u32 tmp; | |
31 | ||
32 | tmp = readl(priv->base + reg); | |
33 | if (value) | |
34 | tmp |= BIT(offset); | |
35 | else | |
36 | tmp &= ~BIT(offset); | |
37 | writel(tmp, priv->base + reg); | |
38 | } | |
39 | ||
40 | static int uniphier_gpio_offset_read(struct udevice *dev, unsigned offset, | |
41 | unsigned reg) | |
42 | { | |
43 | struct uniphier_gpio_priv *priv = dev_get_priv(dev); | |
44 | ||
45 | return !!(readl(priv->base + reg) & BIT(offset)); | |
46 | } | |
47 | ||
48 | static int uniphier_gpio_direction_input(struct udevice *dev, unsigned offset) | |
49 | { | |
50 | uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DIR, 1); | |
51 | ||
52 | return 0; | |
53 | } | |
54 | ||
55 | static int uniphier_gpio_direction_output(struct udevice *dev, unsigned offset, | |
56 | int value) | |
57 | { | |
58 | uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DATA, value); | |
59 | uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DIR, 0); | |
60 | ||
61 | return 0; | |
62 | } | |
63 | ||
64 | static int uniphier_gpio_get_value(struct udevice *dev, unsigned offset) | |
65 | { | |
66 | return uniphier_gpio_offset_read(dev, offset, UNIPHIER_GPIO_REG_DATA); | |
67 | } | |
68 | ||
69 | static int uniphier_gpio_set_value(struct udevice *dev, unsigned offset, | |
70 | int value) | |
71 | { | |
72 | uniphier_gpio_offset_write(dev, offset, UNIPHIER_GPIO_REG_DATA, value); | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
77 | static int uniphier_gpio_get_function(struct udevice *dev, unsigned offset) | |
78 | { | |
79 | return uniphier_gpio_offset_read(dev, offset, UNIPHIER_GPIO_REG_DIR) ? | |
80 | GPIOF_INPUT : GPIOF_OUTPUT; | |
81 | } | |
82 | ||
83 | static const struct dm_gpio_ops uniphier_gpio_ops = { | |
84 | .direction_input = uniphier_gpio_direction_input, | |
85 | .direction_output = uniphier_gpio_direction_output, | |
86 | .get_value = uniphier_gpio_get_value, | |
87 | .set_value = uniphier_gpio_set_value, | |
88 | .get_function = uniphier_gpio_get_function, | |
89 | }; | |
90 | ||
91 | static int uniphier_gpio_probe(struct udevice *dev) | |
92 | { | |
93 | struct uniphier_gpio_priv *priv = dev_get_priv(dev); | |
94 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); | |
b9a66b63 | 95 | fdt_addr_t addr; |
b9a66b63 MY |
96 | unsigned int tmp; |
97 | ||
bc82a131 | 98 | addr = dev_get_addr(dev); |
b9a66b63 MY |
99 | if (addr == FDT_ADDR_T_NONE) |
100 | return -EINVAL; | |
101 | ||
4e3d8406 | 102 | priv->base = devm_ioremap(dev, addr, SZ_8); |
b9a66b63 MY |
103 | if (!priv->base) |
104 | return -ENOMEM; | |
105 | ||
106 | uc_priv->gpio_count = UNIPHIER_GPIO_PORTS_PER_BANK; | |
107 | ||
108 | tmp = (addr & 0xfff); | |
109 | ||
110 | /* Unfortunately, there is a register hole at offset 0x90-0x9f. */ | |
111 | if (tmp > 0x90) | |
112 | tmp -= 0x10; | |
113 | ||
114 | snprintf(priv->bank_name, sizeof(priv->bank_name) - 1, | |
115 | "port%d-", (tmp - 8) / 8); | |
116 | ||
117 | uc_priv->bank_name = priv->bank_name; | |
118 | ||
119 | return 0; | |
120 | } | |
121 | ||
b9a66b63 MY |
122 | /* .data = the number of GPIO banks */ |
123 | static const struct udevice_id uniphier_gpio_match[] = { | |
124 | { .compatible = "socionext,uniphier-gpio" }, | |
125 | { /* sentinel */ } | |
126 | }; | |
127 | ||
128 | U_BOOT_DRIVER(uniphier_gpio) = { | |
129 | .name = "uniphier_gpio", | |
130 | .id = UCLASS_GPIO, | |
131 | .of_match = uniphier_gpio_match, | |
132 | .probe = uniphier_gpio_probe, | |
b9a66b63 MY |
133 | .priv_auto_alloc_size = sizeof(struct uniphier_gpio_priv), |
134 | .ops = &uniphier_gpio_ops, | |
135 | }; |