ARM: dts: npcm8xx: add npcm845 function node
[platform/kernel/u-boot.git] / drivers / gpio / sl28cpld-gpio.c
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
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
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
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
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
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
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 };