x86: tangier: Enable support for SD/SDIO family in the pinmux driver
[platform/kernel/u-boot.git] / arch / x86 / cpu / tangier / pinmux.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2018 Emlid Limited
4  */
5
6 #include <common.h>
7 #include <dm.h>
8 #include <log.h>
9 #include <dm/pinctrl.h>
10 #include <dm/read.h>
11 #include <regmap.h>
12 #include <syscon.h>
13 #include <asm/cpu.h>
14 #include <asm/scu.h>
15 #include <linux/io.h>
16
17 #define BUFCFG_OFFSET                           0x100
18
19 #define MRFLD_FAMILY_LEN                        0x400
20
21 /* These are taken from Linux kernel */
22 #define MRFLD_PINMODE_MASK                      0x07
23
24 #define pin_to_bufno(f, p)                      ((p) - (f)->pin_base)
25
26 struct mrfld_family {
27         unsigned int family_number;
28         unsigned int pin_base;
29         size_t npins;
30         void __iomem *regs;
31 };
32
33 #define MRFLD_FAMILY(b, s, e)                           \
34         {                                               \
35                 .family_number = (b),                   \
36                 .pin_base = (s),                        \
37                 .npins = (e) - (s) + 1,                 \
38         }
39
40 /* Now we only support SD/SDIO and I2C families of pins */
41 static struct mrfld_family mrfld_families[] = {
42         MRFLD_FAMILY(3, 37, 56),
43         MRFLD_FAMILY(7, 101, 114),
44 };
45
46 struct mrfld_pinctrl {
47         const struct mrfld_family *families;
48         size_t nfamilies;
49 };
50
51 static const struct mrfld_family *
52 mrfld_get_family(struct mrfld_pinctrl *mp, unsigned int pin)
53 {
54         const struct mrfld_family *family;
55         unsigned int i;
56
57         for (i = 0; i < mp->nfamilies; i++) {
58                 family = &mp->families[i];
59                 if (pin >= family->pin_base &&
60                     pin < family->pin_base + family->npins)
61                         return family;
62         }
63
64         pr_err("failed to find family for pin %u\n", pin);
65         return NULL;
66 }
67
68 static void __iomem *
69 mrfld_get_bufcfg(struct mrfld_pinctrl *pinctrl, unsigned int pin)
70 {
71         const struct mrfld_family *family;
72         unsigned int bufno;
73
74         family =  mrfld_get_family(pinctrl, pin);
75         if (!family)
76                 return NULL;
77
78         bufno = pin_to_bufno(family, pin);
79
80         return family->regs + BUFCFG_OFFSET + bufno * 4;
81 }
82
83 static void
84 mrfld_setup_families(void *base_addr,
85                      struct mrfld_family *families, unsigned int nfam)
86 {
87         for (int i = 0; i < nfam; i++) {
88                 struct mrfld_family *family = &families[i];
89
90                 family->regs = base_addr +
91                                family->family_number * MRFLD_FAMILY_LEN;
92         }
93 }
94
95 static int mrfld_pinconfig_protected(unsigned int pin, u32 mask, u32 bits)
96 {
97         struct mrfld_pinctrl *pinctrl;
98         struct udevice *dev;
99         void __iomem *bufcfg;
100         u32 v, value;
101         int ret;
102
103         ret = syscon_get_by_driver_data(X86_SYSCON_PINCONF, &dev);
104         if (ret)
105                 return ret;
106
107         pinctrl = dev_get_priv(dev);
108
109         bufcfg = mrfld_get_bufcfg(pinctrl, pin);
110         if (!bufcfg)
111                 return -EINVAL;
112
113         value = readl(bufcfg);
114
115         v = (value & ~mask) | (bits & mask);
116
117         debug("scu: v: 0x%x p: 0x%x bits: %d, mask: %d bufcfg: 0x%p\n",
118               v, (u32)bufcfg, bits, mask, bufcfg);
119
120         ret = scu_ipc_raw_command(IPCMSG_INDIRECT_WRITE, 0, &v, 4,
121                                   NULL, 0, (u32)bufcfg, 0);
122         if (ret)
123                 pr_err("Failed to set mode via SCU for pin %u (%d)\n",
124                        pin, ret);
125
126         return ret;
127 }
128
129 static int mrfld_pinconfig(unsigned int pin, u32 mask, u32 bits)
130 {
131         struct mrfld_pinctrl *pinctrl;
132         struct udevice *dev;
133         void __iomem *bufcfg;
134         u32 v, value;
135         int ret;
136
137         ret = syscon_get_by_driver_data(X86_SYSCON_PINCONF, &dev);
138         if (ret)
139                 return ret;
140
141         pinctrl = dev_get_priv(dev);
142
143         bufcfg = mrfld_get_bufcfg(pinctrl, pin);
144         if (!bufcfg)
145                 return -EINVAL;
146
147         value = readl(bufcfg);
148         v = (value & ~mask) | (bits & mask);
149         writel(v, bufcfg);
150
151         debug("v: 0x%x p: 0x%x bits: %d, mask: %d bufcfg: 0x%p\n",
152               v, (u32)bufcfg, bits, mask, bufcfg);
153
154         return 0;
155 }
156
157 static int mrfld_pinctrl_cfg_pin(ofnode pin_node)
158 {
159         bool is_protected;
160         int pad_offset;
161         int mode;
162         u32 mask;
163         int ret;
164
165         is_protected = ofnode_read_bool(pin_node, "protected");
166
167         pad_offset = ofnode_read_s32_default(pin_node, "pad-offset", -1);
168         if (pad_offset == -1)
169                 return -EINVAL;
170
171         mode = ofnode_read_s32_default(pin_node, "mode-func", -1);
172         if (mode == -1)
173                 return -EINVAL;
174
175         mask = MRFLD_PINMODE_MASK;
176
177         /* We don't support modes not in range 0..7 */
178         if (mode & ~mask)
179                 return -ENOTSUPP;
180
181         if (is_protected)
182                 ret = mrfld_pinconfig_protected(pad_offset, mask, mode);
183         else
184                 ret = mrfld_pinconfig(pad_offset, mask, mode);
185
186         return ret;
187 }
188
189 static int tangier_pinctrl_probe(struct udevice *dev)
190 {
191         void *base_addr = syscon_get_first_range(X86_SYSCON_PINCONF);
192         struct mrfld_pinctrl *pinctrl = dev_get_priv(dev);
193         ofnode pin_node;
194         int ret;
195
196         mrfld_setup_families(base_addr, mrfld_families,
197                              ARRAY_SIZE(mrfld_families));
198
199         pinctrl->families = mrfld_families;
200         pinctrl->nfamilies = ARRAY_SIZE(mrfld_families);
201
202         ofnode_for_each_subnode(pin_node, dev_ofnode(dev)) {
203                 ret = mrfld_pinctrl_cfg_pin(pin_node);
204                 if (ret) {
205                         pr_err("%s: invalid configuration for the pin %ld\n",
206                                __func__, pin_node.of_offset);
207                 }
208         }
209
210         return 0;
211 }
212
213 static const struct udevice_id tangier_pinctrl_match[] = {
214         { .compatible = "intel,pinctrl-tangier", .data = X86_SYSCON_PINCONF },
215         { /* sentinel */ }
216 };
217
218 U_BOOT_DRIVER(tangier_pinctrl) = {
219         .name = "tangier_pinctrl",
220         .id = UCLASS_SYSCON,
221         .of_match = tangier_pinctrl_match,
222         .probe = tangier_pinctrl_probe,
223         .priv_auto      = sizeof(struct mrfld_pinctrl),
224 };