summaryrefslogtreecommitdiff
path: root/drivers/pinctrl/mvebu/pinctrl-mvebu.c
blob: e5820375393ad5675dfa4fab8e2918b274b9d275 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2016 Marvell International Ltd.
 * https://spdx.org/licenses
 */

#include <common.h>
#include <config.h>
#include <fdtdec.h>
#include <errno.h>
#include <dm.h>
#include <log.h>
#include <asm/global_data.h>
#include <dm/pinctrl.h>
#include <dm/root.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/arch-armada8k/soc-info.h>
#include <linux/bitops.h>
#include <linux/errno.h>
#include "pinctrl-mvebu.h"

#define AP_EMMC_PHY_CTRL_REG		0x100
#define CP_EMMC_PHY_CTRL_REG		0x424
#define EMMC_PHY_CTRL_SDPHY_EN		BIT(0)

#define AP806_EMMC_CLK_PIN_ID		0
#define AP806_EMMC_CLK_FUNC		0x1
#define CP110_EMMC_CLK_PIN_ID		56
#define CP110_EMMC_CLK_FUNC		0xe

DECLARE_GLOBAL_DATA_PTR;

/* mvebu_pinctl_emmc_set_mux: configure sd/mmc PHY mux
 * To enable SDIO/eMMC in Armada-APN806/CP110, need to configure PHY mux.
 * eMMC/SD PHY register responsible for muxing between MPPs and SD/eMMC
 * controller:
 * - Bit0 enabled SDIO/eMMC PHY is used as a MPP muxltiplexer,
 * - Bit0 disabled SDIO/eMMC PHY is connected to SDIO/eMMC controller
 * If pin function is set to eMMC/SD, then configure the eMMC/SD PHY
 * muxltiplexer register to be on SDIO/eMMC controller
 */
void mvebu_pinctl_emmc_set_mux(struct udevice *dev, u32 pin, u32 func)
{
	const void *blob = gd->fdt_blob;
	int node = dev_of_offset(dev);
	struct mvebu_pinctrl_priv *priv = dev_get_priv(dev);

	if (!fdt_node_check_compatible(blob, node, "marvell,ap806-pinctrl")) {
		if ((pin == AP806_EMMC_CLK_PIN_ID) &&
		    (func == AP806_EMMC_CLK_FUNC)) {
			clrbits_le32(priv->base_reg + AP_EMMC_PHY_CTRL_REG,
				     EMMC_PHY_CTRL_SDPHY_EN);
		}
	} else if (!fdt_node_check_compatible(blob, node,
					"marvell,armada-8k-cpm-pinctrl")) {
		if ((pin == CP110_EMMC_CLK_PIN_ID) &&
		    (func == CP110_EMMC_CLK_FUNC)) {
			clrbits_le32(priv->base_reg + CP_EMMC_PHY_CTRL_REG,
				     EMMC_PHY_CTRL_SDPHY_EN);
		}
	}
}

/*
 * mvebu_pinctrl_set_state: configure pin functions.
 * @dev: the pinctrl device to be configured.
 * @config: the state to be configured.
 * @return: 0 in success
 */
int mvebu_pinctrl_set_state(struct udevice *dev, struct udevice *config)
{
	const void *blob = gd->fdt_blob;
	int node = dev_of_offset(config);
	struct mvebu_pinctrl_priv *priv;
	u32 pin_arr[MVEBU_MAX_PINS_PER_BANK];
	u32 function;
	int i, pin_count;

	priv = dev_get_priv(dev);

	pin_count = fdtdec_get_int_array_count(blob, node,
					       "marvell,pins",
					       pin_arr,
					       MVEBU_MAX_PINS_PER_BANK);
	if (pin_count <= 0) {
		debug("Failed reading pins array for pinconfig %s (%d)\n",
		      config->name, pin_count);
		return -EINVAL;
	}

	function = fdtdec_get_int(blob, node, "marvell,function", 0xff);

	/*
	 * Check if setup of PHY mux is needed for this pins group.
	 * Only the first pin id in array is tested, all the rest use the same
	 * pin function.
	 */
	mvebu_pinctl_emmc_set_mux(dev, pin_arr[0], function);

	for (i = 0; i < pin_count; i++) {
		int reg_offset;
		int field_offset;
		int pin = pin_arr[i];

		if (function > priv->max_func) {
			debug("Illegal function %d for pinconfig %s\n",
			      function, config->name);
			return -EINVAL;
		}

		/* Calculate register address and bit in register */
		reg_offset   = priv->reg_direction * 4 *
					(pin >> (PIN_REG_SHIFT));
		field_offset = (BITS_PER_PIN) * (pin & PIN_FIELD_MASK);

		clrsetbits_le32(priv->base_reg + reg_offset,
				PIN_FUNC_MASK << field_offset,
				(function & PIN_FUNC_MASK) << field_offset);
	}

	return 0;
}

/*
 * mvebu_pinctrl_set_state_all: configure the entire bank pin functions.
 * @dev: the pinctrl device to be configured.
 * @config: the state to be configured.
 * @return: 0 in success
 */
static int mvebu_pinctrl_set_state_all(struct udevice *dev,
				       struct udevice *config)
{
	const void *blob = gd->fdt_blob;
	int node = dev_of_offset(config);
	struct mvebu_pinctrl_priv *priv;
	u32 func_arr[MVEBU_MAX_PINS_PER_BANK];
	int pin, err;

	priv = dev_get_priv(dev);

	err = fdtdec_get_int_array(blob, node, "pin-func",
				   func_arr, priv->pin_cnt);
	if (err) {
		debug("Failed reading pin functions for bank %s\n",
		      priv->bank_name);
		return -EINVAL;
	}

	/* Check if setup of PHY mux is needed for this pins group. */
	if (priv->pin_cnt < CP110_EMMC_CLK_PIN_ID)
		mvebu_pinctl_emmc_set_mux(dev, AP806_EMMC_CLK_PIN_ID,
					  func_arr[AP806_EMMC_CLK_PIN_ID]);
	else
		mvebu_pinctl_emmc_set_mux(dev, CP110_EMMC_CLK_PIN_ID,
					  func_arr[CP110_EMMC_CLK_PIN_ID]);

	for (pin = 0; pin < priv->pin_cnt; pin++) {
		int reg_offset;
		int field_offset;
		u32 func = func_arr[pin];

		/* Bypass pins with function 0xFF */
		if (func == 0xff) {
			debug("Warning: pin %d value is not modified ", pin);
			debug("(kept as default)\n");
			continue;
		} else if (func > priv->max_func) {
			debug("Illegal function %d for pin %d\n", func, pin);
			return -EINVAL;
		}

		/* Calculate register address and bit in register */
		reg_offset   = priv->reg_direction * 4 *
					(pin >> (PIN_REG_SHIFT));
		field_offset = (BITS_PER_PIN) * (pin & PIN_FIELD_MASK);

		clrsetbits_le32(priv->base_reg + reg_offset,
				PIN_FUNC_MASK << field_offset,
				(func & PIN_FUNC_MASK) << field_offset);
	}

	return 0;
}

int mvebu_pinctl_probe(struct udevice *dev)
{
	const void *blob = gd->fdt_blob;
	int node = dev_of_offset(dev);
	struct mvebu_pinctrl_priv *priv;

	priv = dev_get_priv(dev);
	if (!priv) {
		debug("%s: Failed to get private\n", __func__);
		return -EINVAL;
	}

	priv->base_reg = devfdt_get_addr_ptr(dev);
	if (priv->base_reg == (void *)FDT_ADDR_T_NONE) {
		debug("%s: Failed to get base address\n", __func__);
		return -EINVAL;
	}

	priv->pin_cnt   = fdtdec_get_int(blob, node, "pin-count",
					MVEBU_MAX_PINS_PER_BANK);
	priv->max_func  = fdtdec_get_int(blob, node, "max-func",
					 MVEBU_MAX_FUNC);
	priv->bank_name = fdt_getprop(blob, node, "bank-name", NULL);

	priv->reg_direction = 1;
	if (fdtdec_get_bool(blob, node, "reverse-reg"))
		priv->reg_direction = -1;

	return mvebu_pinctrl_set_state_all(dev, dev);
}

static struct pinctrl_ops mvebu_pinctrl_ops = {
	.set_state	= mvebu_pinctrl_set_state
};

static const struct udevice_id mvebu_pinctrl_ids[] = {
	{ .compatible = "marvell,mvebu-pinctrl" },
	{ .compatible = "marvell,ap806-pinctrl" },
	{ .compatible = "marvell,armada-7k-pinctrl" },
	{ .compatible = "marvell,armada-8k-cpm-pinctrl" },
	{ .compatible = "marvell,armada-8k-cps-pinctrl" },
	{ }
};

U_BOOT_DRIVER(pinctrl_mvebu) = {
	.name		= "mvebu_pinctrl",
	.id		= UCLASS_PINCTRL,
	.of_match	= mvebu_pinctrl_ids,
	.priv_auto_alloc_size = sizeof(struct mvebu_pinctrl_priv),
	.ops		= &mvebu_pinctrl_ops,
	.probe		= mvebu_pinctl_probe
};