xref: /OK3568_Linux_fs/u-boot/drivers/pinctrl/nxp/pinctrl-imx.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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