blob: 700fc3df298cfbb821f9e70dec76a99674b94b14 [file] [log] [blame]
Michael Walle07d6cb92022-02-25 18:10:24 +05301// 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
24enum {
25 SL28CPLD_GPIO,
26 SL28CPLD_GPI,
27 SL28CPLD_GPO,
28};
29
30static 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
55static 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
79static 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
94static 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
111static 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
134static 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
142static 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
152static 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
159U_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};