mxc_gpio.c 9.0 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0+
2 3 4 5
/*
 * Copyright (C) 2009
 * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
 *
6 7
 * Copyright (C) 2011
 * Stefano Babic, DENX Software Engineering, <sbabic@denx.de>
8 9
 */
#include <common.h>
10 11 12
#include <errno.h>
#include <dm.h>
#include <malloc.h>
13
#include <asm/arch/imx-regs.h>
14
#include <asm/gpio.h>
15
#include <asm/io.h>
16

17 18 19 20 21
enum mxc_gpio_direction {
	MXC_GPIO_DIRECTION_IN,
	MXC_GPIO_DIRECTION_OUT,
};

22 23 24
#define GPIO_PER_BANK			32

struct mxc_gpio_plat {
25
	int bank_index;
26 27 28 29 30 31 32 33
	struct gpio_regs *regs;
};

struct mxc_bank_info {
	struct gpio_regs *regs;
};

#ifndef CONFIG_DM_GPIO
34
#define GPIO_TO_PORT(n)		(n / 32)
35

36 37
/* GPIO port description */
static unsigned long gpio_ports[] = {
38 39 40
	[0] = GPIO1_BASE_ADDR,
	[1] = GPIO2_BASE_ADDR,
	[2] = GPIO3_BASE_ADDR,
T
trem 已提交
41
#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \
42
		defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
P
Peng Fan 已提交
43 44
		defined(CONFIG_MX7) || defined(CONFIG_MX8M) || \
		defined(CONFIG_ARCH_IMX8)
45 46
	[3] = GPIO4_BASE_ADDR,
#endif
47
#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
P
Peng Fan 已提交
48 49
		defined(CONFIG_MX7) || defined(CONFIG_MX8M) || \
		defined(CONFIG_ARCH_IMX8)
50
	[4] = GPIO5_BASE_ADDR,
P
Peng Fan 已提交
51
#if !(defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL) || defined(CONFIG_MX8M))
52
	[5] = GPIO6_BASE_ADDR,
T
trem 已提交
53
#endif
P
Peng Fan 已提交
54
#endif
P
Peng Fan 已提交
55 56
#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_MX7) || \
		defined(CONFIG_ARCH_IMX8)
57
#if !(defined(CONFIG_MX6UL) || defined(CONFIG_MX6ULL))
58 59
	[6] = GPIO7_BASE_ADDR,
#endif
P
Peng Fan 已提交
60
#endif
P
Peng Fan 已提交
61 62 63
#if defined(CONFIG_ARCH_IMX8)
	[7] = GPIO8_BASE_ADDR,
#endif
64 65
};

66 67
static int mxc_gpio_direction(unsigned int gpio,
	enum mxc_gpio_direction direction)
68
{
69
	unsigned int port = GPIO_TO_PORT(gpio);
70
	struct gpio_regs *regs;
71 72 73
	u32 l;

	if (port >= ARRAY_SIZE(gpio_ports))
74
		return -1;
75 76 77

	gpio &= 0x1f;

78 79 80 81
	regs = (struct gpio_regs *)gpio_ports[port];

	l = readl(&regs->gpio_dir);

82
	switch (direction) {
83
	case MXC_GPIO_DIRECTION_OUT:
84 85
		l |= 1 << gpio;
		break;
86
	case MXC_GPIO_DIRECTION_IN:
87 88
		l &= ~(1 << gpio);
	}
89
	writel(l, &regs->gpio_dir);
90 91 92 93

	return 0;
}

94
int gpio_set_value(unsigned gpio, int value)
95
{
96
	unsigned int port = GPIO_TO_PORT(gpio);
97
	struct gpio_regs *regs;
98 99 100
	u32 l;

	if (port >= ARRAY_SIZE(gpio_ports))
101
		return -1;
102 103 104

	gpio &= 0x1f;

105 106 107
	regs = (struct gpio_regs *)gpio_ports[port];

	l = readl(&regs->gpio_dr);
108 109 110 111
	if (value)
		l |= 1 << gpio;
	else
		l &= ~(1 << gpio);
112
	writel(l, &regs->gpio_dr);
113 114

	return 0;
115
}
116

117
int gpio_get_value(unsigned gpio)
118
{
119
	unsigned int port = GPIO_TO_PORT(gpio);
120
	struct gpio_regs *regs;
121
	u32 val;
122 123

	if (port >= ARRAY_SIZE(gpio_ports))
124
		return -1;
125 126 127

	gpio &= 0x1f;

128 129
	regs = (struct gpio_regs *)gpio_ports[port];

130
	val = (readl(&regs->gpio_psr) >> gpio) & 0x01;
131

132
	return val;
133
}
134

135
int gpio_request(unsigned gpio, const char *label)
136
{
137
	unsigned int port = GPIO_TO_PORT(gpio);
138
	if (port >= ARRAY_SIZE(gpio_ports))
139
		return -1;
140 141 142
	return 0;
}

143
int gpio_free(unsigned gpio)
144
{
145
	return 0;
146 147
}

148
int gpio_direction_input(unsigned gpio)
149
{
150
	return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_IN);
151 152
}

153
int gpio_direction_output(unsigned gpio, int value)
154
{
155
	int ret = gpio_set_value(gpio, value);
156 157 158 159

	if (ret < 0)
		return ret;

160
	return mxc_gpio_direction(gpio, MXC_GPIO_DIRECTION_OUT);
161
}
162 163 164
#endif

#ifdef CONFIG_DM_GPIO
P
Peng Fan 已提交
165
#include <fdtdec.h>
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 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
static int mxc_gpio_is_output(struct gpio_regs *regs, int offset)
{
	u32 val;

	val = readl(&regs->gpio_dir);

	return val & (1 << offset) ? 1 : 0;
}

static void mxc_gpio_bank_direction(struct gpio_regs *regs, int offset,
				    enum mxc_gpio_direction direction)
{
	u32 l;

	l = readl(&regs->gpio_dir);

	switch (direction) {
	case MXC_GPIO_DIRECTION_OUT:
		l |= 1 << offset;
		break;
	case MXC_GPIO_DIRECTION_IN:
		l &= ~(1 << offset);
	}
	writel(l, &regs->gpio_dir);
}

static void mxc_gpio_bank_set_value(struct gpio_regs *regs, int offset,
				    int value)
{
	u32 l;

	l = readl(&regs->gpio_dr);
	if (value)
		l |= 1 << offset;
	else
		l &= ~(1 << offset);
	writel(l, &regs->gpio_dr);
}

static int mxc_gpio_bank_get_value(struct gpio_regs *regs, int offset)
{
	return (readl(&regs->gpio_psr) >> offset) & 0x01;
}

/* set GPIO pin 'gpio' as an input */
static int mxc_gpio_direction_input(struct udevice *dev, unsigned offset)
{
	struct mxc_bank_info *bank = dev_get_priv(dev);

	/* Configure GPIO direction as input. */
	mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_IN);

	return 0;
}

/* set GPIO pin 'gpio' as an output, with polarity 'value' */
static int mxc_gpio_direction_output(struct udevice *dev, unsigned offset,
				       int value)
{
	struct mxc_bank_info *bank = dev_get_priv(dev);

	/* Configure GPIO output value. */
	mxc_gpio_bank_set_value(bank->regs, offset, value);

	/* Configure GPIO direction as output. */
	mxc_gpio_bank_direction(bank->regs, offset, MXC_GPIO_DIRECTION_OUT);

	return 0;
}

/* read GPIO IN value of pin 'gpio' */
static int mxc_gpio_get_value(struct udevice *dev, unsigned offset)
{
	struct mxc_bank_info *bank = dev_get_priv(dev);

	return mxc_gpio_bank_get_value(bank->regs, offset);
}

/* write GPIO OUT value to pin 'gpio' */
static int mxc_gpio_set_value(struct udevice *dev, unsigned offset,
				 int value)
{
	struct mxc_bank_info *bank = dev_get_priv(dev);

	mxc_gpio_bank_set_value(bank->regs, offset, value);

	return 0;
}

static int mxc_gpio_get_function(struct udevice *dev, unsigned offset)
{
	struct mxc_bank_info *bank = dev_get_priv(dev);

	/* GPIOF_FUNC is not implemented yet */
	if (mxc_gpio_is_output(bank->regs, offset))
		return GPIOF_OUTPUT;
	else
		return GPIOF_INPUT;
}

static const struct dm_gpio_ops gpio_mxc_ops = {
	.direction_input	= mxc_gpio_direction_input,
	.direction_output	= mxc_gpio_direction_output,
	.get_value		= mxc_gpio_get_value,
	.set_value		= mxc_gpio_set_value,
	.get_function		= mxc_gpio_get_function,
};

static int mxc_gpio_probe(struct udevice *dev)
{
	struct mxc_bank_info *bank = dev_get_priv(dev);
	struct mxc_gpio_plat *plat = dev_get_platdata(dev);
278
	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
279 280 281
	int banknum;
	char name[18], *str;

282
	banknum = plat->bank_index;
283 284 285 286 287 288 289 290 291 292 293
	sprintf(name, "GPIO%d_", banknum + 1);
	str = strdup(name);
	if (!str)
		return -ENOMEM;
	uc_priv->bank_name = str;
	uc_priv->gpio_count = GPIO_PER_BANK;
	bank->regs = plat->regs;

	return 0;
}

P
Peng Fan 已提交
294 295 296 297 298 299 300 301 302 303 304 305 306 307
static int mxc_gpio_bind(struct udevice *dev)
{
	struct mxc_gpio_plat *plat = dev->platdata;
	fdt_addr_t addr;

	/*
	 * If platdata already exsits, directly return.
	 * Actually only when DT is not supported, platdata
	 * is statically initialized in U_BOOT_DEVICES.Here
	 * will return.
	 */
	if (plat)
		return 0;

S
Simon Glass 已提交
308
	addr = devfdt_get_addr(dev);
P
Peng Fan 已提交
309
	if (addr == FDT_ADDR_T_NONE)
310
		return -EINVAL;
P
Peng Fan 已提交
311 312 313 314 315 316

	/*
	 * TODO:
	 * When every board is converted to driver model and DT is supported,
	 * this can be done by auto-alloc feature, but not using calloc
	 * to alloc memory for platdata.
317 318 319 320 321
	 *
	 * For example mxc_plat below uses platform data rather than device
	 * tree.
	 *
	 * NOTE: DO NOT COPY this code if you are using device tree.
P
Peng Fan 已提交
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
	 */
	plat = calloc(1, sizeof(*plat));
	if (!plat)
		return -ENOMEM;

	plat->regs = (struct gpio_regs *)addr;
	plat->bank_index = dev->req_seq;
	dev->platdata = plat;

	return 0;
}

static const struct udevice_id mxc_gpio_ids[] = {
	{ .compatible = "fsl,imx35-gpio" },
	{ }
};

339 340 341 342 343 344
U_BOOT_DRIVER(gpio_mxc) = {
	.name	= "gpio_mxc",
	.id	= UCLASS_GPIO,
	.ops	= &gpio_mxc_ops,
	.probe	= mxc_gpio_probe,
	.priv_auto_alloc_size = sizeof(struct mxc_bank_info),
P
Peng Fan 已提交
345 346 347 348
	.of_match = mxc_gpio_ids,
	.bind	= mxc_gpio_bind,
};

349
#if !CONFIG_IS_ENABLED(OF_CONTROL)
P
Peng Fan 已提交
350 351 352 353 354
static const struct mxc_gpio_plat mxc_plat[] = {
	{ 0, (struct gpio_regs *)GPIO1_BASE_ADDR },
	{ 1, (struct gpio_regs *)GPIO2_BASE_ADDR },
	{ 2, (struct gpio_regs *)GPIO3_BASE_ADDR },
#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \
P
Peng Fan 已提交
355
		defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
P
Peng Fan 已提交
356
		defined(CONFIG_MX8M) || defined(CONFIG_ARCH_IMX8)
P
Peng Fan 已提交
357 358
	{ 3, (struct gpio_regs *)GPIO4_BASE_ADDR },
#endif
P
Peng Fan 已提交
359
#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
P
Peng Fan 已提交
360
		defined(CONFIG_MX8M) || defined(CONFIG_ARCH_IMX8)
P
Peng Fan 已提交
361
	{ 4, (struct gpio_regs *)GPIO5_BASE_ADDR },
P
Peng Fan 已提交
362
#ifndef CONFIG_MX8M
P
Peng Fan 已提交
363 364
	{ 5, (struct gpio_regs *)GPIO6_BASE_ADDR },
#endif
P
Peng Fan 已提交
365
#endif
P
Peng Fan 已提交
366
#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_ARCH_IMX8)
P
Peng Fan 已提交
367 368
	{ 6, (struct gpio_regs *)GPIO7_BASE_ADDR },
#endif
P
Peng Fan 已提交
369 370 371
#if defined(CONFIG_ARCH_IMX8)
	{ 7, (struct gpio_regs *)GPIO8_BASE_ADDR },
#endif
372 373 374 375 376 377 378
};

U_BOOT_DEVICES(mxc_gpios) = {
	{ "gpio_mxc", &mxc_plat[0] },
	{ "gpio_mxc", &mxc_plat[1] },
	{ "gpio_mxc", &mxc_plat[2] },
#if defined(CONFIG_MX25) || defined(CONFIG_MX27) || defined(CONFIG_MX51) || \
P
Peng Fan 已提交
379
		defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
P
Peng Fan 已提交
380
		defined(CONFIG_MX8M) || defined(CONFIG_ARCH_IMX8)
381 382
	{ "gpio_mxc", &mxc_plat[3] },
#endif
P
Peng Fan 已提交
383
#if defined(CONFIG_MX27) || defined(CONFIG_MX53) || defined(CONFIG_MX6) || \
P
Peng Fan 已提交
384
		defined(CONFIG_MX8M) || defined(CONFIG_ARCH_IMX8)
385
	{ "gpio_mxc", &mxc_plat[4] },
P
Peng Fan 已提交
386
#ifndef CONFIG_MX8M
387 388
	{ "gpio_mxc", &mxc_plat[5] },
#endif
P
Peng Fan 已提交
389
#endif
P
Peng Fan 已提交
390
#if defined(CONFIG_MX53) || defined(CONFIG_MX6) || defined(CONFIG_ARCH_IMX8)
391 392
	{ "gpio_mxc", &mxc_plat[6] },
#endif
P
Peng Fan 已提交
393 394 395
#if defined(CONFIG_ARCH_IMX8)
	{ "gpio_mxc", &mxc_plat[7] },
#endif
396 397
};
#endif
P
Peng Fan 已提交
398
#endif