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