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