summaryrefslogtreecommitdiff
path: root/drivers/gpio/gpio-mpc8xxx.c
blob: c5d72ef34ae4ffd6f98c2d23bbbb37a26855d295 (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

/*
 * GPIOs on MPC512x/8349/8572/8610/T-series and compatible
 * 
 * Driver ported from the linux kernel 4.5 (b562e44f507e863c6792946e4e1b1449fbbac85d)
 * and removed the interrupt functionallity. 
 *
 * Copyright (C) 2008 Peter Korsgaard <jacmet@sunsite.dk>
 * Copyright (c) 2016 Scalys B.V. <u-boot@scalys.com>
 *
 * SPDX-License-Identifier: GPL-2.0+
 */

#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <fdtdec.h>
#include <asm/io.h>
#include <asm/gpio.h>
#include <dm/platform_data/gpio_mpc8xxx.h>

DECLARE_GLOBAL_DATA_PTR;

#define MPC8XXX_GPIO_PINS	32

static inline u32 mpc8xxx_gpio2mask(unsigned int gpio)
{
	return 1u << (MPC8XXX_GPIO_PINS - 1 - gpio);
}

static int mpc8xxx_dm_gpio_set(struct udevice *dev, unsigned pin, int val)
{
	struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev);

#if defined(CONFIG_MPC8572) || defined(CONFIG_MPC8536)

	if (val) {
		plat->data |= mpc8xxx_gpio2mask(pin);
	} else {
		plat->data &= ~mpc8xxx_gpio2mask(pin);
	}

	out_be32(&(plat->regs->gpdat), plat->data);
#else
	if (val) {
		setbits_be32(&(plat->regs->gpdat), mpc8xxx_gpio2mask(pin));
	} else {
		clrbits_be32(&(plat->regs->gpdat), mpc8xxx_gpio2mask(pin));
	}
#endif
	return 0;
}

static int mpc8xxx_dm_gpio_dir_in(struct udevice *dev, unsigned int gpio)
{
	struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev);

	clrbits_be32(&(plat->regs->gpdir), mpc8xxx_gpio2mask(gpio));

	return 0;
}

static int mpc8xxx_dm_gpio_dir_out(struct udevice *dev, unsigned int gpio,
				   int val)
{
	struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev);

	mpc8xxx_dm_gpio_set(dev, gpio, val);

	setbits_be32(&(plat->regs->gpdir), mpc8xxx_gpio2mask(gpio));

	return 0;
}

static int mpc8xxx_dm_gpio_get(struct udevice *dev, unsigned int gpio)
{
	int ret = 0;
	struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev);

#if defined(CONFIG_MPC8572) || defined(CONFIG_MPC8536)
	uint32_t data_val, out_mask, out_shadow;

	/* Workaround GPIO 1 errata on MPC8572/MPC8536. The status of GPIOs
	 * defined as output cannot be determined by reading GPDAT register,
	 * so we use shadow data register instead. The status of input pins
	 * is determined by reading GPDAT register.
	 */
	out_mask = in_be32(&plat->regs->gpdir);

	data_val = in_be32(&plat->regs->gpdat) & ~out_mask;
	out_shadow = plat->data & out_mask;

	ret = ! !((data_val | out_shadow) & mpc8xxx_gpio2mask(gpio));
#else
	if (in_be32(&plat->regs->gpdat) & mpc8xxx_gpio2mask(gpio)) {
		ret = 1;
	} else {
		ret = 0;
	}
#endif
	return ret;
}

static int mpc8xxx_dm_gpio_get_function(struct udevice *dev, unsigned gpio)
{
	int ret = GPIOF_UNUSED;
	struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev);

	if (in_be32(&plat->regs->gpdir) & mpc8xxx_gpio2mask(gpio)) {
		ret = GPIOF_OUTPUT;
	} else {
		ret = GPIOF_INPUT;
	}
	return ret;
}

static const struct udevice_id mpc8xxx_gpio_ids[] = {
	{.compatible = "fsl,mpc8349-gpio",},
	{.compatible = "fsl,mpc8572-gpio",},
	{.compatible = "fsl,mpc8610-gpio",},
	{.compatible = "fsl,mpc5121-gpio",},
	{.compatible = "fsl,mpc5125-gpio",},
	{.compatible = "fsl,pq3-gpio",},
	{.compatible = "fsl,qoriq-gpio",},
	{}
};

static const struct dm_gpio_ops mpc8xxx_gpio_ops = {
	.direction_input = mpc8xxx_dm_gpio_dir_in,
	.direction_output = mpc8xxx_dm_gpio_dir_out,
	.get_value = mpc8xxx_dm_gpio_get,
	.set_value = mpc8xxx_dm_gpio_set,
	.get_function = mpc8xxx_dm_gpio_get_function,
};

#ifdef SPL_OF_CONTROL
static int mpc8xxx_gpio_ofdata_to_platdata(struct udevice *dev)
{
	int register_address;
	struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev);

	register_address =
	    fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1);
	if (register_address == -1) {
		debug("%s: Invalid register offset %d\n", __func__,
		      register_address);
		return -EINVAL;
	}
	plat->regs = map_physmem(register_address, sizeof(ccsr_gpio_t),
				 MAP_NOCACHE);
	plat->gpio_count = MPC8XXX_GPIO_PINS;
	plat->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset,
				      "bank-name", NULL);

	return 0;
}
#endif

static int mpc8xxx_gpio_probe(struct udevice *dev)
{
	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
	struct mpc8xxx_gpio_platdata *plat = dev_get_platdata(dev);

	uc_priv->gpio_count = MPC8XXX_GPIO_PINS;
	uc_priv->bank_name = plat->bank_name;

	return 0;
}

U_BOOT_DRIVER(gpio_mpc8xxx) = {
	.name = "gpio-mpc8xxx",.id = UCLASS_GPIO,.of_match =
	    mpc8xxx_gpio_ids,.ops = &mpc8xxx_gpio_ops,
#ifdef SPL_OF_CONTROL
	    .ofdata_to_platdata = mpc8xxx_gpio_ofdata_to_platdata,
#endif
.platdata_auto_alloc_size =
	    sizeof(struct mpc8xxx_gpio_platdata),.probe = mpc8xxx_gpio_probe,};