Merge tag 'v2021.01-rc5' into next
[platform/kernel/u-boot.git] / drivers / gpio / intel_broadwell_gpio.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2012 The Chromium OS Authors.
4  */
5
6 #include <common.h>
7 #include <dm.h>
8 #include <errno.h>
9 #include <fdtdec.h>
10 #include <log.h>
11 #include <pch.h>
12 #include <pci.h>
13 #include <syscon.h>
14 #include <asm/cpu.h>
15 #include <asm/gpio.h>
16 #include <asm/io.h>
17 #include <asm/pci.h>
18 #include <asm/arch/gpio.h>
19 #include <dt-bindings/gpio/x86-gpio.h>
20
21 DECLARE_GLOBAL_DATA_PTR;
22
23 /**
24  * struct broadwell_bank_priv - Private driver data
25  *
26  * @regs:       Pointer to GPIO registers
27  * @bank:       Bank number for this bank (0, 1 or 2)
28  * @offset:     GPIO offset for this bank (0, 32 or 64)
29  */
30 struct broadwell_bank_priv {
31         struct pch_lp_gpio_regs *regs;
32         int bank;
33         int offset;
34 };
35
36 static int broadwell_gpio_request(struct udevice *dev, unsigned offset,
37                              const char *label)
38 {
39         struct broadwell_bank_priv *priv = dev_get_priv(dev);
40         struct pch_lp_gpio_regs *regs = priv->regs;
41         u32 val;
42
43         /*
44          * Make sure that the GPIO pin we want isn't already in use for some
45          * built-in hardware function. We have to check this for every
46          * requested pin.
47          */
48         debug("%s: request bank %d offset %d: ", __func__, priv->bank, offset);
49         val = inl(&regs->own[priv->bank]);
50         if (!(val & (1UL << offset))) {
51                 debug("gpio is reserved for internal use\n");
52                 return -EPERM;
53         }
54         debug("ok\n");
55
56         return 0;
57 }
58
59 static int broadwell_gpio_direction_input(struct udevice *dev, unsigned offset)
60 {
61         struct broadwell_bank_priv *priv = dev_get_priv(dev);
62         struct pch_lp_gpio_regs *regs = priv->regs;
63
64         setio_32(&regs->config[priv->offset + offset], CONFA_DIR_INPUT);
65
66         return 0;
67 }
68
69 static int broadwell_gpio_get_value(struct udevice *dev, unsigned offset)
70 {
71         struct broadwell_bank_priv *priv = dev_get_priv(dev);
72         struct pch_lp_gpio_regs *regs = priv->regs;
73
74         return inl(&regs->config[priv->offset + offset]) & CONFA_LEVEL_HIGH ?
75                 1 : 0;
76 }
77
78 static int broadwell_gpio_set_value(struct udevice *dev, unsigned offset,
79                                     int value)
80 {
81         struct broadwell_bank_priv *priv = dev_get_priv(dev);
82         struct pch_lp_gpio_regs *regs = priv->regs;
83
84         debug("%s: dev=%s, offset=%d, value=%d\n", __func__, dev->name, offset,
85               value);
86         clrsetio_32(&regs->config[priv->offset + offset], CONFA_OUTPUT_HIGH,
87                       value ? CONFA_OUTPUT_HIGH : 0);
88
89         return 0;
90 }
91
92 static int broadwell_gpio_direction_output(struct udevice *dev, unsigned offset,
93                                            int value)
94 {
95         struct broadwell_bank_priv *priv = dev_get_priv(dev);
96         struct pch_lp_gpio_regs *regs = priv->regs;
97
98         broadwell_gpio_set_value(dev, offset, value);
99         clrio_32(&regs->config[priv->offset + offset], CONFA_DIR_INPUT);
100
101         return 0;
102 }
103
104 static int broadwell_gpio_get_function(struct udevice *dev, unsigned offset)
105 {
106         struct broadwell_bank_priv *priv = dev_get_priv(dev);
107         struct pch_lp_gpio_regs *regs = priv->regs;
108         u32 mask = 1UL << offset;
109
110         if (!(inl(&regs->own[priv->bank]) & mask))
111                 return GPIOF_FUNC;
112         if (inl(&regs->config[priv->offset + offset]) & CONFA_DIR_INPUT)
113                 return GPIOF_INPUT;
114         else
115                 return GPIOF_OUTPUT;
116 }
117
118 static int broadwell_gpio_probe(struct udevice *dev)
119 {
120         struct broadwell_bank_plat *plat = dev_get_plat(dev);
121         struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
122         struct broadwell_bank_priv *priv = dev_get_priv(dev);
123         struct udevice *pinctrl;
124         int ret;
125
126         /* Set up pin control if available */
127         ret = syscon_get_by_driver_data(X86_SYSCON_PINCONF, &pinctrl);
128         debug("%s, pinctrl=%p, ret=%d\n", __func__, pinctrl, ret);
129
130         uc_priv->gpio_count = GPIO_PER_BANK;
131         uc_priv->bank_name = plat->bank_name;
132
133         priv->regs = (struct pch_lp_gpio_regs *)(uintptr_t)plat->base_addr;
134         priv->bank = plat->bank;
135         priv->offset = priv->bank * 32;
136         debug("%s: probe done, regs %p, bank %d\n", __func__, priv->regs,
137               priv->bank);
138
139         return 0;
140 }
141
142 static int broadwell_gpio_of_to_plat(struct udevice *dev)
143 {
144         struct broadwell_bank_plat *plat = dev_get_plat(dev);
145         u32 gpiobase;
146         int bank;
147         int ret;
148
149         ret = pch_get_gpio_base(dev->parent, &gpiobase);
150         if (ret)
151                 return ret;
152
153         bank = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "reg", -1);
154         if (bank == -1) {
155                 debug("%s: Invalid bank number %d\n", __func__, bank);
156                 return -EINVAL;
157         }
158         plat->bank = bank;
159         plat->base_addr = gpiobase;
160         plat->bank_name = fdt_getprop(gd->fdt_blob, dev_of_offset(dev),
161                                       "bank-name", NULL);
162
163         return 0;
164 }
165
166 static const struct dm_gpio_ops gpio_broadwell_ops = {
167         .request                = broadwell_gpio_request,
168         .direction_input        = broadwell_gpio_direction_input,
169         .direction_output       = broadwell_gpio_direction_output,
170         .get_value              = broadwell_gpio_get_value,
171         .set_value              = broadwell_gpio_set_value,
172         .get_function           = broadwell_gpio_get_function,
173 };
174
175 static const struct udevice_id intel_broadwell_gpio_ids[] = {
176         { .compatible = "intel,broadwell-gpio" },
177         { }
178 };
179
180 U_BOOT_DRIVER(gpio_broadwell) = {
181         .name   = "gpio_broadwell",
182         .id     = UCLASS_GPIO,
183         .of_match = intel_broadwell_gpio_ids,
184         .ops    = &gpio_broadwell_ops,
185         .of_to_plat     = broadwell_gpio_of_to_plat,
186         .probe  = broadwell_gpio_probe,
187         .priv_auto      = sizeof(struct broadwell_bank_priv),
188         .plat_auto      = sizeof(struct broadwell_bank_plat),
189 };