]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
386d934e PCM |
2 | /* |
3 | * Copyright (c) 2015 Microchip Technology Inc | |
4 | * Purna Chandra Mandal <[email protected]> | |
386d934e PCM |
5 | */ |
6 | ||
386d934e PCM |
7 | #include <dm.h> |
8 | #include <errno.h> | |
9 | #include <malloc.h> | |
401d1c4f | 10 | #include <asm/global_data.h> |
386d934e PCM |
11 | #include <asm/io.h> |
12 | #include <asm/gpio.h> | |
cd93d625 | 13 | #include <linux/bitops.h> |
386d934e | 14 | #include <linux/compat.h> |
386d934e PCM |
15 | #include <mach/pic32.h> |
16 | ||
17 | DECLARE_GLOBAL_DATA_PTR; | |
18 | ||
19 | /* Peripheral Pin Control */ | |
20 | struct pic32_reg_port { | |
21 | struct pic32_reg_atomic ansel; | |
22 | struct pic32_reg_atomic tris; | |
23 | struct pic32_reg_atomic port; | |
24 | struct pic32_reg_atomic lat; | |
25 | struct pic32_reg_atomic open_drain; | |
26 | struct pic32_reg_atomic cnpu; | |
27 | struct pic32_reg_atomic cnpd; | |
28 | struct pic32_reg_atomic cncon; | |
29 | }; | |
30 | ||
31 | enum { | |
32 | MICROCHIP_GPIO_DIR_OUT, | |
33 | MICROCHIP_GPIO_DIR_IN, | |
34 | MICROCHIP_GPIOS_PER_BANK = 16, | |
35 | }; | |
36 | ||
37 | struct pic32_gpio_priv { | |
38 | struct pic32_reg_port *regs; | |
39 | char name[2]; | |
40 | }; | |
41 | ||
42 | static int pic32_gpio_get_value(struct udevice *dev, unsigned offset) | |
43 | { | |
44 | struct pic32_gpio_priv *priv = dev_get_priv(dev); | |
45 | ||
46 | return !!(readl(&priv->regs->port.raw) & BIT(offset)); | |
47 | } | |
48 | ||
49 | static int pic32_gpio_set_value(struct udevice *dev, unsigned offset, | |
50 | int value) | |
51 | { | |
52 | struct pic32_gpio_priv *priv = dev_get_priv(dev); | |
53 | int mask = BIT(offset); | |
54 | ||
55 | if (value) | |
56 | writel(mask, &priv->regs->port.set); | |
57 | else | |
58 | writel(mask, &priv->regs->port.clr); | |
59 | ||
60 | return 0; | |
61 | } | |
62 | ||
63 | static int pic32_gpio_direction(struct udevice *dev, unsigned offset) | |
64 | { | |
65 | struct pic32_gpio_priv *priv = dev_get_priv(dev); | |
66 | ||
67 | /* pin in analog mode ? */ | |
68 | if (readl(&priv->regs->ansel.raw) & BIT(offset)) | |
69 | return -EPERM; | |
70 | ||
71 | if (readl(&priv->regs->tris.raw) & BIT(offset)) | |
72 | return MICROCHIP_GPIO_DIR_IN; | |
73 | else | |
74 | return MICROCHIP_GPIO_DIR_OUT; | |
75 | } | |
76 | ||
77 | static int pic32_gpio_direction_input(struct udevice *dev, unsigned offset) | |
78 | { | |
79 | struct pic32_gpio_priv *priv = dev_get_priv(dev); | |
80 | int mask = BIT(offset); | |
81 | ||
82 | writel(mask, &priv->regs->ansel.clr); | |
83 | writel(mask, &priv->regs->tris.set); | |
84 | ||
85 | return 0; | |
86 | } | |
87 | ||
88 | static int pic32_gpio_direction_output(struct udevice *dev, | |
89 | unsigned offset, int value) | |
90 | { | |
91 | struct pic32_gpio_priv *priv = dev_get_priv(dev); | |
92 | int mask = BIT(offset); | |
93 | ||
94 | writel(mask, &priv->regs->ansel.clr); | |
95 | writel(mask, &priv->regs->tris.clr); | |
96 | ||
97 | pic32_gpio_set_value(dev, offset, value); | |
98 | return 0; | |
99 | } | |
100 | ||
386d934e PCM |
101 | static int pic32_gpio_get_function(struct udevice *dev, unsigned offset) |
102 | { | |
103 | int ret = GPIOF_UNUSED; | |
104 | ||
105 | switch (pic32_gpio_direction(dev, offset)) { | |
106 | case MICROCHIP_GPIO_DIR_OUT: | |
107 | ret = GPIOF_OUTPUT; | |
108 | break; | |
109 | case MICROCHIP_GPIO_DIR_IN: | |
110 | ret = GPIOF_INPUT; | |
111 | break; | |
112 | default: | |
113 | ret = GPIOF_UNUSED; | |
114 | break; | |
115 | } | |
116 | return ret; | |
117 | } | |
118 | ||
119 | static const struct dm_gpio_ops gpio_pic32_ops = { | |
120 | .direction_input = pic32_gpio_direction_input, | |
121 | .direction_output = pic32_gpio_direction_output, | |
122 | .get_value = pic32_gpio_get_value, | |
123 | .set_value = pic32_gpio_set_value, | |
124 | .get_function = pic32_gpio_get_function, | |
386d934e PCM |
125 | }; |
126 | ||
127 | static int pic32_gpio_probe(struct udevice *dev) | |
128 | { | |
129 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); | |
130 | struct pic32_gpio_priv *priv = dev_get_priv(dev); | |
131 | fdt_addr_t addr; | |
132 | fdt_size_t size; | |
133 | char *end; | |
134 | int bank; | |
135 | ||
e160f7d4 SG |
136 | addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg", |
137 | &size); | |
386d934e PCM |
138 | if (addr == FDT_ADDR_T_NONE) |
139 | return -EINVAL; | |
140 | ||
141 | priv->regs = ioremap(addr, size); | |
142 | ||
143 | uc_priv->gpio_count = MICROCHIP_GPIOS_PER_BANK; | |
144 | /* extract bank name */ | |
145 | end = strrchr(dev->name, '@'); | |
146 | bank = trailing_strtoln(dev->name, end); | |
147 | priv->name[0] = 'A' + bank; | |
148 | uc_priv->bank_name = priv->name; | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
153 | static const struct udevice_id pic32_gpio_ids[] = { | |
154 | { .compatible = "microchip,pic32mzda-gpio" }, | |
155 | { } | |
156 | }; | |
157 | ||
158 | U_BOOT_DRIVER(gpio_pic32) = { | |
159 | .name = "gpio_pic32", | |
160 | .id = UCLASS_GPIO, | |
161 | .of_match = pic32_gpio_ids, | |
162 | .ops = &gpio_pic32_ops, | |
163 | .probe = pic32_gpio_probe, | |
41575d8e | 164 | .priv_auto = sizeof(struct pic32_gpio_priv), |
386d934e | 165 | }; |