1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * GPIO driver for the sl28cpld
4 *
5 * Copyright (c) 2021 Michael Walle <michael@walle.cc>
6 */
7
8 #include <common.h>
9 #include <dm.h>
10 #include <asm/gpio.h>
11 #include <sl28cpld.h>
12
13 /* GPIO flavor */
14 #define SL28CPLD_GPIO_DIR 0x00
15 #define SL28CPLD_GPIO_OUT 0x01
16 #define SL28CPLD_GPIO_IN 0x02
17
18 /* input-only flavor */
19 #define SL28CPLD_GPI_IN 0x00
20
21 /* output-only flavor */
22 #define SL28CPLD_GPO_OUT 0x00
23
24 enum {
25 SL28CPLD_GPIO,
26 SL28CPLD_GPI,
27 SL28CPLD_GPO,
28 };
29
sl28cpld_gpio_get_value(struct udevice * dev,unsigned int gpio)30 static int sl28cpld_gpio_get_value(struct udevice *dev, unsigned int gpio)
31 {
32 ulong type = dev_get_driver_data(dev);
33 int val, reg;
34
35 switch (type) {
36 case SL28CPLD_GPIO:
37 reg = SL28CPLD_GPIO_IN;
38 break;
39 case SL28CPLD_GPI:
40 reg = SL28CPLD_GPI_IN;
41 break;
42 case SL28CPLD_GPO:
43 /* we are output only, thus just return the output value */
44 reg = SL28CPLD_GPO_OUT;
45 break;
46 default:
47 return -EINVAL;
48 }
49
50 val = sl28cpld_read(dev, reg);
51
52 return val < 0 ? val : !!(val & BIT(gpio));
53 }
54
sl28cpld_gpio_set_value(struct udevice * dev,unsigned int gpio,int value)55 static int sl28cpld_gpio_set_value(struct udevice *dev, unsigned int gpio,
56 int value)
57 {
58 ulong type = dev_get_driver_data(dev);
59 uint reg;
60
61 switch (type) {
62 case SL28CPLD_GPIO:
63 reg = SL28CPLD_GPIO_OUT;
64 break;
65 case SL28CPLD_GPO:
66 reg = SL28CPLD_GPO_OUT;
67 break;
68 case SL28CPLD_GPI:
69 default:
70 return -EINVAL;
71 }
72
73 if (value)
74 return sl28cpld_update(dev, reg, 0, BIT(gpio));
75 else
76 return sl28cpld_update(dev, reg, BIT(gpio), 0);
77 }
78
sl28cpld_gpio_direction_input(struct udevice * dev,unsigned int gpio)79 static int sl28cpld_gpio_direction_input(struct udevice *dev, unsigned int gpio)
80 {
81 ulong type = dev_get_driver_data(dev);
82
83 switch (type) {
84 case SL28CPLD_GPI:
85 return 0;
86 case SL28CPLD_GPIO:
87 return sl28cpld_update(dev, SL28CPLD_GPIO_DIR, BIT(gpio), 0);
88 case SL28CPLD_GPO:
89 default:
90 return -EINVAL;
91 }
92 }
93
sl28cpld_gpio_direction_output(struct udevice * dev,unsigned int gpio,int value)94 static int sl28cpld_gpio_direction_output(struct udevice *dev,
95 unsigned int gpio, int value)
96 {
97 ulong type = dev_get_driver_data(dev);
98 int ret;
99
100 /* set_value() will report an error if we are input-only */
101 ret = sl28cpld_gpio_set_value(dev, gpio, value);
102 if (ret)
103 return ret;
104
105 if (type == SL28CPLD_GPIO)
106 return sl28cpld_update(dev, SL28CPLD_GPIO_DIR, 0, BIT(gpio));
107
108 return 0;
109 }
110
sl28cpld_gpio_get_function(struct udevice * dev,unsigned int gpio)111 static int sl28cpld_gpio_get_function(struct udevice *dev, unsigned int gpio)
112 {
113 ulong type = dev_get_driver_data(dev);
114 int val;
115
116 switch (type) {
117 case SL28CPLD_GPIO:
118 val = sl28cpld_read(dev, SL28CPLD_GPIO_DIR);
119 if (val < 0)
120 return val;
121 if (val & BIT(gpio))
122 return GPIOF_OUTPUT;
123 else
124 return GPIOF_INPUT;
125 case SL28CPLD_GPI:
126 return GPIOF_INPUT;
127 case SL28CPLD_GPO:
128 return GPIOF_OUTPUT;
129 default:
130 return -EINVAL;
131 }
132 }
133
134 static const struct dm_gpio_ops sl28cpld_gpio_ops = {
135 .direction_input = sl28cpld_gpio_direction_input,
136 .direction_output = sl28cpld_gpio_direction_output,
137 .get_value = sl28cpld_gpio_get_value,
138 .set_value = sl28cpld_gpio_set_value,
139 .get_function = sl28cpld_gpio_get_function,
140 };
141
sl28cpld_gpio_probe(struct udevice * dev)142 static int sl28cpld_gpio_probe(struct udevice *dev)
143 {
144 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
145
146 uc_priv->gpio_count = 8;
147 uc_priv->bank_name = dev_read_name(dev);
148
149 return 0;
150 }
151
152 static const struct udevice_id sl28cpld_gpio_ids[] = {
153 { .compatible = "kontron,sl28cpld-gpio", .data = SL28CPLD_GPIO},
154 { .compatible = "kontron,sl28cpld-gpo", .data = SL28CPLD_GPO},
155 { .compatible = "kontron,sl28cpld-gpi", .data = SL28CPLD_GPI},
156 { }
157 };
158
159 U_BOOT_DRIVER(sl28cpld_gpio) = {
160 .name = "sl28cpld_gpio",
161 .id = UCLASS_GPIO,
162 .of_match = sl28cpld_gpio_ids,
163 .probe = sl28cpld_gpio_probe,
164 .ops = &sl28cpld_gpio_ops,
165 };
166