1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright (C) 2016 Peng Fan <van.freenix@gmail.com>
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <common.h>
8*4882a593Smuzhiyun #include <mapmem.h>
9*4882a593Smuzhiyun #include <linux/io.h>
10*4882a593Smuzhiyun #include <linux/err.h>
11*4882a593Smuzhiyun #include <dm.h>
12*4882a593Smuzhiyun #include <dm/pinctrl.h>
13*4882a593Smuzhiyun
14*4882a593Smuzhiyun #include "pinctrl-imx.h"
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun DECLARE_GLOBAL_DATA_PTR;
17*4882a593Smuzhiyun
imx_pinctrl_set_state(struct udevice * dev,struct udevice * config)18*4882a593Smuzhiyun static int imx_pinctrl_set_state(struct udevice *dev, struct udevice *config)
19*4882a593Smuzhiyun {
20*4882a593Smuzhiyun struct imx_pinctrl_priv *priv = dev_get_priv(dev);
21*4882a593Smuzhiyun struct imx_pinctrl_soc_info *info = priv->info;
22*4882a593Smuzhiyun int node = dev_of_offset(config);
23*4882a593Smuzhiyun const struct fdt_property *prop;
24*4882a593Smuzhiyun u32 *pin_data;
25*4882a593Smuzhiyun int npins, size, pin_size;
26*4882a593Smuzhiyun int mux_reg, conf_reg, input_reg, input_val, mux_mode, config_val;
27*4882a593Smuzhiyun u32 mux_shift = info->mux_mask ? ffs(info->mux_mask) - 1 : 0;
28*4882a593Smuzhiyun int i, j = 0;
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun dev_dbg(dev, "%s: %s\n", __func__, config->name);
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun if (info->flags & SHARE_MUX_CONF_REG)
33*4882a593Smuzhiyun pin_size = SHARE_FSL_PIN_SIZE;
34*4882a593Smuzhiyun else
35*4882a593Smuzhiyun pin_size = FSL_PIN_SIZE;
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun prop = fdt_getprop(gd->fdt_blob, node, "fsl,pins", &size);
38*4882a593Smuzhiyun if (!prop) {
39*4882a593Smuzhiyun dev_err(dev, "No fsl,pins property in node %s\n", config->name);
40*4882a593Smuzhiyun return -EINVAL;
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun if (!size || size % pin_size) {
44*4882a593Smuzhiyun dev_err(dev, "Invalid fsl,pins property in node %s\n",
45*4882a593Smuzhiyun config->name);
46*4882a593Smuzhiyun return -EINVAL;
47*4882a593Smuzhiyun }
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun pin_data = devm_kzalloc(dev, size, 0);
50*4882a593Smuzhiyun if (!pin_data)
51*4882a593Smuzhiyun return -ENOMEM;
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun if (fdtdec_get_int_array(gd->fdt_blob, node, "fsl,pins",
54*4882a593Smuzhiyun pin_data, size >> 2)) {
55*4882a593Smuzhiyun dev_err(dev, "Error reading pin data.\n");
56*4882a593Smuzhiyun devm_kfree(dev, pin_data);
57*4882a593Smuzhiyun return -EINVAL;
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun npins = size / pin_size;
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun /*
63*4882a593Smuzhiyun * Refer to linux documentation for details:
64*4882a593Smuzhiyun * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt
65*4882a593Smuzhiyun */
66*4882a593Smuzhiyun for (i = 0; i < npins; i++) {
67*4882a593Smuzhiyun mux_reg = pin_data[j++];
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun if (!(info->flags & ZERO_OFFSET_VALID) && !mux_reg)
70*4882a593Smuzhiyun mux_reg = -1;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun if (info->flags & SHARE_MUX_CONF_REG) {
73*4882a593Smuzhiyun conf_reg = mux_reg;
74*4882a593Smuzhiyun } else {
75*4882a593Smuzhiyun conf_reg = pin_data[j++];
76*4882a593Smuzhiyun if (!(info->flags & ZERO_OFFSET_VALID) && !conf_reg)
77*4882a593Smuzhiyun conf_reg = -1;
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun if ((mux_reg == -1) || (conf_reg == -1)) {
81*4882a593Smuzhiyun dev_err(dev, "Error mux_reg or conf_reg\n");
82*4882a593Smuzhiyun devm_kfree(dev, pin_data);
83*4882a593Smuzhiyun return -EINVAL;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun input_reg = pin_data[j++];
87*4882a593Smuzhiyun mux_mode = pin_data[j++];
88*4882a593Smuzhiyun input_val = pin_data[j++];
89*4882a593Smuzhiyun config_val = pin_data[j++];
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun dev_dbg(dev, "mux_reg 0x%x, conf_reg 0x%x, input_reg 0x%x, "
92*4882a593Smuzhiyun "mux_mode 0x%x, input_val 0x%x, config_val 0x%x\n",
93*4882a593Smuzhiyun mux_reg, conf_reg, input_reg, mux_mode, input_val,
94*4882a593Smuzhiyun config_val);
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun if (config_val & IMX_PAD_SION)
97*4882a593Smuzhiyun mux_mode |= IOMUXC_CONFIG_SION;
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun config_val &= ~IMX_PAD_SION;
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun /* Set Mux */
102*4882a593Smuzhiyun if (info->flags & SHARE_MUX_CONF_REG) {
103*4882a593Smuzhiyun clrsetbits_le32(info->base + mux_reg, info->mux_mask,
104*4882a593Smuzhiyun mux_mode << mux_shift);
105*4882a593Smuzhiyun } else {
106*4882a593Smuzhiyun writel(mux_mode, info->base + mux_reg);
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun dev_dbg(dev, "write mux: offset 0x%x val 0x%x\n", mux_reg,
110*4882a593Smuzhiyun mux_mode);
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun /*
113*4882a593Smuzhiyun * Set select input
114*4882a593Smuzhiyun *
115*4882a593Smuzhiyun * If the select input value begins with 0xff, it's a quirky
116*4882a593Smuzhiyun * select input and the value should be interpreted as below.
117*4882a593Smuzhiyun * 31 23 15 7 0
118*4882a593Smuzhiyun * | 0xff | shift | width | select |
119*4882a593Smuzhiyun * It's used to work around the problem that the select
120*4882a593Smuzhiyun * input for some pin is not implemented in the select
121*4882a593Smuzhiyun * input register but in some general purpose register.
122*4882a593Smuzhiyun * We encode the select input value, width and shift of
123*4882a593Smuzhiyun * the bit field into input_val cell of pin function ID
124*4882a593Smuzhiyun * in device tree, and then decode them here for setting
125*4882a593Smuzhiyun * up the select input bits in general purpose register.
126*4882a593Smuzhiyun */
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun if (input_val >> 24 == 0xff) {
129*4882a593Smuzhiyun u32 val = input_val;
130*4882a593Smuzhiyun u8 select = val & 0xff;
131*4882a593Smuzhiyun u8 width = (val >> 8) & 0xff;
132*4882a593Smuzhiyun u8 shift = (val >> 16) & 0xff;
133*4882a593Smuzhiyun u32 mask = ((1 << width) - 1) << shift;
134*4882a593Smuzhiyun /*
135*4882a593Smuzhiyun * The input_reg[i] here is actually some IOMUXC general
136*4882a593Smuzhiyun * purpose register, not regular select input register.
137*4882a593Smuzhiyun */
138*4882a593Smuzhiyun val = readl(info->base + input_reg);
139*4882a593Smuzhiyun val &= ~mask;
140*4882a593Smuzhiyun val |= select << shift;
141*4882a593Smuzhiyun writel(val, info->base + input_reg);
142*4882a593Smuzhiyun } else if (input_reg) {
143*4882a593Smuzhiyun /*
144*4882a593Smuzhiyun * Regular select input register can never be at offset
145*4882a593Smuzhiyun * 0, and we only print register value for regular case.
146*4882a593Smuzhiyun */
147*4882a593Smuzhiyun if (info->input_sel_base)
148*4882a593Smuzhiyun writel(input_val, info->input_sel_base +
149*4882a593Smuzhiyun input_reg);
150*4882a593Smuzhiyun else
151*4882a593Smuzhiyun writel(input_val, info->base + input_reg);
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun dev_dbg(dev, "select_input: offset 0x%x val 0x%x\n",
154*4882a593Smuzhiyun input_reg, input_val);
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun
157*4882a593Smuzhiyun /* Set config */
158*4882a593Smuzhiyun if (!(config_val & IMX_NO_PAD_CTL)) {
159*4882a593Smuzhiyun if (info->flags & SHARE_MUX_CONF_REG) {
160*4882a593Smuzhiyun clrsetbits_le32(info->base + conf_reg,
161*4882a593Smuzhiyun ~info->mux_mask, config_val);
162*4882a593Smuzhiyun } else {
163*4882a593Smuzhiyun writel(config_val, info->base + conf_reg);
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun dev_dbg(dev, "write config: offset 0x%x val 0x%x\n",
167*4882a593Smuzhiyun conf_reg, config_val);
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun devm_kfree(dev, pin_data);
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun return 0;
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun const struct pinctrl_ops imx_pinctrl_ops = {
177*4882a593Smuzhiyun .set_state = imx_pinctrl_set_state,
178*4882a593Smuzhiyun };
179*4882a593Smuzhiyun
imx_pinctrl_probe(struct udevice * dev,struct imx_pinctrl_soc_info * info)180*4882a593Smuzhiyun int imx_pinctrl_probe(struct udevice *dev,
181*4882a593Smuzhiyun struct imx_pinctrl_soc_info *info)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun struct imx_pinctrl_priv *priv = dev_get_priv(dev);
184*4882a593Smuzhiyun int node = dev_of_offset(dev), ret;
185*4882a593Smuzhiyun struct fdtdec_phandle_args arg;
186*4882a593Smuzhiyun fdt_addr_t addr;
187*4882a593Smuzhiyun fdt_size_t size;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun if (!info) {
190*4882a593Smuzhiyun dev_err(dev, "wrong pinctrl info\n");
191*4882a593Smuzhiyun return -EINVAL;
192*4882a593Smuzhiyun }
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun priv->dev = dev;
195*4882a593Smuzhiyun priv->info = info;
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg",
198*4882a593Smuzhiyun &size);
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun if (addr == FDT_ADDR_T_NONE)
201*4882a593Smuzhiyun return -EINVAL;
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun info->base = map_sysmem(addr, size);
204*4882a593Smuzhiyun if (!info->base)
205*4882a593Smuzhiyun return -ENOMEM;
206*4882a593Smuzhiyun priv->info = info;
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun info->mux_mask = fdtdec_get_int(gd->fdt_blob, node, "fsl,mux_mask", 0);
209*4882a593Smuzhiyun /*
210*4882a593Smuzhiyun * Refer to linux documentation for details:
211*4882a593Smuzhiyun * Documentation/devicetree/bindings/pinctrl/fsl,imx7d-pinctrl.txt
212*4882a593Smuzhiyun */
213*4882a593Smuzhiyun if (fdtdec_get_bool(gd->fdt_blob, node, "fsl,input-sel")) {
214*4882a593Smuzhiyun ret = fdtdec_parse_phandle_with_args(gd->fdt_blob,
215*4882a593Smuzhiyun node, "fsl,input-sel",
216*4882a593Smuzhiyun NULL, 0, 0, &arg);
217*4882a593Smuzhiyun if (ret) {
218*4882a593Smuzhiyun dev_err(dev, "iomuxc fsl,input-sel property not found\n");
219*4882a593Smuzhiyun return -EINVAL;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun addr = fdtdec_get_addr_size(gd->fdt_blob, arg.node, "reg",
223*4882a593Smuzhiyun &size);
224*4882a593Smuzhiyun if (addr == FDT_ADDR_T_NONE)
225*4882a593Smuzhiyun return -EINVAL;
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun info->input_sel_base = map_sysmem(addr, size);
228*4882a593Smuzhiyun if (!info->input_sel_base)
229*4882a593Smuzhiyun return -ENOMEM;
230*4882a593Smuzhiyun }
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun dev_dbg(dev, "initialized IMX pinctrl driver\n");
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun return 0;
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun
imx_pinctrl_remove(struct udevice * dev)237*4882a593Smuzhiyun int imx_pinctrl_remove(struct udevice *dev)
238*4882a593Smuzhiyun {
239*4882a593Smuzhiyun struct imx_pinctrl_priv *priv = dev_get_priv(dev);
240*4882a593Smuzhiyun struct imx_pinctrl_soc_info *info = priv->info;
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun if (info->input_sel_base)
243*4882a593Smuzhiyun unmap_sysmem(info->input_sel_base);
244*4882a593Smuzhiyun if (info->base)
245*4882a593Smuzhiyun unmap_sysmem(info->base);
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun return 0;
248*4882a593Smuzhiyun }
249