Commit 355c9b757e1110151c78ea82fd834e6cb09bdb96
Committed by
Bin Meng
1 parent
224742a390
Exists in
smarc_8mq_lf_v2020.04
and in
11 other branches
x86: tangier: pinmux: add API to configure protected pins
This API is going to be used to configure some pins that are protected for simple modification. It's not a comprehensive pinctrl driver but can be turned into one when we need this in the future. Now it is planned to be used only in one place. So that's why I decided not to pollute the codebase with a full-blown pinctrl-merrifield nobody will use. This driver reads corresponding fields in DT and configures pins accordingly. The "protected" flag is used to distinguish configuration of SCU-owned pins from the ordinary ones. The code has been adapted from Linux work done by Andy Shevchenko in pinctrl-merrfifield.c Signed-off-by: Georgii Staroselskii <georgii.staroselskii@emlid.com> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Reviewed-by: Bin Meng <bmeng.cn@gmail.com> [bmeng: fix build warning] Signed-off-by: Bin Meng <bmeng.cn@gmail.com>
Showing 2 changed files with 195 additions and 1 deletions Side-by-side Diff
arch/x86/cpu/tangier/Makefile
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 <dm/pinctrl.h> | |
9 | +#include <dm/read.h> | |
10 | +#include <regmap.h> | |
11 | +#include <syscon.h> | |
12 | +#include <asm/cpu.h> | |
13 | +#include <asm/scu.h> | |
14 | +#include <linux/io.h> | |
15 | + | |
16 | +#define BUFCFG_OFFSET 0x100 | |
17 | + | |
18 | +#define MRFLD_FAMILY_LEN 0x400 | |
19 | + | |
20 | +/* These are taken from Linux kernel */ | |
21 | +#define MRFLD_PINMODE_MASK 0x07 | |
22 | + | |
23 | +#define pin_to_bufno(f, p) ((p) - (f)->pin_base) | |
24 | + | |
25 | +struct mrfld_family { | |
26 | + unsigned int family_number; | |
27 | + unsigned int pin_base; | |
28 | + size_t npins; | |
29 | + void __iomem *regs; | |
30 | +}; | |
31 | + | |
32 | +#define MRFLD_FAMILY(b, s, e) \ | |
33 | + { \ | |
34 | + .family_number = (b), \ | |
35 | + .pin_base = (s), \ | |
36 | + .npins = (e) - (s) + 1, \ | |
37 | + } | |
38 | + | |
39 | +/* Now we only support I2C family of pins */ | |
40 | +static struct mrfld_family mrfld_families[] = { | |
41 | + MRFLD_FAMILY(7, 101, 114), | |
42 | +}; | |
43 | + | |
44 | +struct mrfld_pinctrl { | |
45 | + const struct mrfld_family *families; | |
46 | + size_t nfamilies; | |
47 | +}; | |
48 | + | |
49 | +static const struct mrfld_family * | |
50 | +mrfld_get_family(struct mrfld_pinctrl *mp, unsigned int pin) | |
51 | +{ | |
52 | + const struct mrfld_family *family; | |
53 | + unsigned int i; | |
54 | + | |
55 | + for (i = 0; i < mp->nfamilies; i++) { | |
56 | + family = &mp->families[i]; | |
57 | + if (pin >= family->pin_base && | |
58 | + pin < family->pin_base + family->npins) | |
59 | + return family; | |
60 | + } | |
61 | + | |
62 | + pr_err("failed to find family for pin %u\n", pin); | |
63 | + return NULL; | |
64 | +} | |
65 | + | |
66 | +static void __iomem * | |
67 | +mrfld_get_bufcfg(struct mrfld_pinctrl *pinctrl, unsigned int pin) | |
68 | +{ | |
69 | + const struct mrfld_family *family; | |
70 | + unsigned int bufno; | |
71 | + | |
72 | + family = mrfld_get_family(pinctrl, pin); | |
73 | + if (!family) | |
74 | + return NULL; | |
75 | + | |
76 | + bufno = pin_to_bufno(family, pin); | |
77 | + | |
78 | + return family->regs + BUFCFG_OFFSET + bufno * 4; | |
79 | +} | |
80 | + | |
81 | +static void | |
82 | +mrfld_setup_families(void *base_addr, | |
83 | + struct mrfld_family *families, unsigned int nfam) | |
84 | +{ | |
85 | + for (int i = 0; i < nfam; i++) { | |
86 | + struct mrfld_family *family = &families[i]; | |
87 | + | |
88 | + family->regs = base_addr + | |
89 | + family->family_number * MRFLD_FAMILY_LEN; | |
90 | + } | |
91 | +} | |
92 | + | |
93 | +static int mrfld_pinconfig_protected(unsigned int pin, u32 mask, u32 bits) | |
94 | +{ | |
95 | + struct mrfld_pinctrl *pinctrl; | |
96 | + struct udevice *dev; | |
97 | + void __iomem *bufcfg; | |
98 | + u32 v, value; | |
99 | + int ret; | |
100 | + | |
101 | + ret = syscon_get_by_driver_data(X86_SYSCON_PINCONF, &dev); | |
102 | + if (ret) | |
103 | + return ret; | |
104 | + | |
105 | + pinctrl = dev_get_priv(dev); | |
106 | + | |
107 | + bufcfg = mrfld_get_bufcfg(pinctrl, pin); | |
108 | + if (!bufcfg) | |
109 | + return -EINVAL; | |
110 | + | |
111 | + value = readl(bufcfg); | |
112 | + | |
113 | + v = (value & ~mask) | (bits & mask); | |
114 | + | |
115 | + debug("scu: v: 0x%x p: 0x%x bits: %d, mask: %d bufcfg: 0x%p\n", | |
116 | + v, (u32)bufcfg, bits, mask, bufcfg); | |
117 | + | |
118 | + ret = scu_ipc_raw_command(IPCMSG_INDIRECT_WRITE, 0, &v, 4, | |
119 | + NULL, 0, (u32)bufcfg, 0); | |
120 | + if (ret) | |
121 | + pr_err("Failed to set mode via SCU for pin %u (%d)\n", | |
122 | + pin, ret); | |
123 | + | |
124 | + return ret; | |
125 | +} | |
126 | + | |
127 | +static int mrfld_pinctrl_cfg_pin(ofnode pin_node) | |
128 | +{ | |
129 | + bool is_protected; | |
130 | + int pad_offset; | |
131 | + int mode; | |
132 | + u32 mask; | |
133 | + int ret; | |
134 | + | |
135 | + /* For now we only support just protected Family of pins */ | |
136 | + is_protected = ofnode_read_bool(pin_node, "protected"); | |
137 | + if (!is_protected) | |
138 | + return -ENOTSUPP; | |
139 | + | |
140 | + pad_offset = ofnode_read_s32_default(pin_node, "pad-offset", -1); | |
141 | + if (pad_offset == -1) | |
142 | + return -EINVAL; | |
143 | + | |
144 | + mode = ofnode_read_s32_default(pin_node, "mode-func", -1); | |
145 | + if (mode == -1) | |
146 | + return -EINVAL; | |
147 | + | |
148 | + mask = MRFLD_PINMODE_MASK; | |
149 | + | |
150 | + /* We don't support modes not in range 0..7 */ | |
151 | + if (mode & ~mask) | |
152 | + return -ENOTSUPP; | |
153 | + | |
154 | + ret = mrfld_pinconfig_protected(pad_offset, mask, mode); | |
155 | + | |
156 | + return ret; | |
157 | +} | |
158 | + | |
159 | +static int tangier_pinctrl_probe(struct udevice *dev) | |
160 | +{ | |
161 | + void *base_addr = syscon_get_first_range(X86_SYSCON_PINCONF); | |
162 | + struct mrfld_pinctrl *pinctrl = dev_get_priv(dev); | |
163 | + ofnode pin_node; | |
164 | + int ret; | |
165 | + | |
166 | + mrfld_setup_families(base_addr, mrfld_families, | |
167 | + ARRAY_SIZE(mrfld_families)); | |
168 | + | |
169 | + pinctrl->families = mrfld_families; | |
170 | + pinctrl->nfamilies = ARRAY_SIZE(mrfld_families); | |
171 | + | |
172 | + ofnode_for_each_subnode(pin_node, dev_ofnode(dev)) { | |
173 | + ret = mrfld_pinctrl_cfg_pin(pin_node); | |
174 | + if (ret) { | |
175 | + pr_err("%s: invalid configuration for the pin %ld\n", | |
176 | + __func__, pin_node.of_offset); | |
177 | + } | |
178 | + } | |
179 | + | |
180 | + return 0; | |
181 | +} | |
182 | + | |
183 | +static const struct udevice_id tangier_pinctrl_match[] = { | |
184 | + { .compatible = "intel,pinctrl-tangier", .data = X86_SYSCON_PINCONF }, | |
185 | + { /* sentinel */ } | |
186 | +}; | |
187 | + | |
188 | +U_BOOT_DRIVER(tangier_pinctrl) = { | |
189 | + .name = "tangier_pinctrl", | |
190 | + .id = UCLASS_SYSCON, | |
191 | + .of_match = tangier_pinctrl_match, | |
192 | + .probe = tangier_pinctrl_probe, | |
193 | + .priv_auto_alloc_size = sizeof(struct mrfld_pinctrl), | |
194 | +}; |