1 /*
2  * Copyright (c) 2012 The Chromium OS Authors.
3  * SPDX-License-Identifier:	GPL-2.0+
4  */
5 
6 #include <common.h>
7 #include <dm.h>
8 #include <errno.h>
9 #include <fdtdec.h>
10 #include <pch.h>
11 #include <pci.h>
12 #include <asm/cpu.h>
13 #include <asm/gpio.h>
14 #include <asm/io.h>
15 #include <asm/pci.h>
16 #include <asm/arch/gpio.h>
17 #include <dt-bindings/gpio/x86-gpio.h>
18 
19 DECLARE_GLOBAL_DATA_PTR;
20 
21 /**
22  * struct broadwell_bank_priv - Private driver data
23  *
24  * @regs:	Pointer to GPIO registers
25  * @bank:	Bank number for this bank (0, 1 or 2)
26  * @offset:	GPIO offset for this bank (0, 32 or 64)
27  */
28 struct broadwell_bank_priv {
29 	struct pch_lp_gpio_regs *regs;
30 	int bank;
31 	int offset;
32 };
33 
34 static int broadwell_gpio_request(struct udevice *dev, unsigned offset,
35 			     const char *label)
36 {
37 	struct broadwell_bank_priv *priv = dev_get_priv(dev);
38 	struct pch_lp_gpio_regs *regs = priv->regs;
39 	u32 val;
40 
41 	/*
42 	 * Make sure that the GPIO pin we want isn't already in use for some
43 	 * built-in hardware function. We have to check this for every
44 	 * requested pin.
45 	 */
46 	debug("%s: request bank %d offset %d: ", __func__, priv->bank, offset);
47 	val = inl(&regs->own[priv->bank]);
48 	if (!(val & (1UL << offset))) {
49 		debug("gpio is reserved for internal use\n");
50 		return -EPERM;
51 	}
52 	debug("ok\n");
53 
54 	return 0;
55 }
56 
57 static int broadwell_gpio_direction_input(struct udevice *dev, unsigned offset)
58 {
59 	struct broadwell_bank_priv *priv = dev_get_priv(dev);
60 	struct pch_lp_gpio_regs *regs = priv->regs;
61 
62 	setio_32(&regs->config[priv->offset + offset], CONFA_DIR_INPUT);
63 
64 	return 0;
65 }
66 
67 static int broadwell_gpio_get_value(struct udevice *dev, unsigned offset)
68 {
69 	struct broadwell_bank_priv *priv = dev_get_priv(dev);
70 	struct pch_lp_gpio_regs *regs = priv->regs;
71 
72 	return inl(&regs->config[priv->offset + offset]) & CONFA_LEVEL_HIGH ?
73 		1 : 0;
74 }
75 
76 static int broadwell_gpio_set_value(struct udevice *dev, unsigned offset,
77 				    int value)
78 {
79 	struct broadwell_bank_priv *priv = dev_get_priv(dev);
80 	struct pch_lp_gpio_regs *regs = priv->regs;
81 
82 	debug("%s: dev=%s, offset=%d, value=%d\n", __func__, dev->name, offset,
83 	      value);
84 	clrsetio_32(&regs->config[priv->offset + offset], CONFA_OUTPUT_HIGH,
85 		      value ? CONFA_OUTPUT_HIGH : 0);
86 
87 	return 0;
88 }
89 
90 static int broadwell_gpio_direction_output(struct udevice *dev, unsigned offset,
91 					   int value)
92 {
93 	struct broadwell_bank_priv *priv = dev_get_priv(dev);
94 	struct pch_lp_gpio_regs *regs = priv->regs;
95 
96 	broadwell_gpio_set_value(dev, offset, value);
97 	clrio_32(&regs->config[priv->offset + offset], CONFA_DIR_INPUT);
98 
99 	return 0;
100 }
101 
102 static int broadwell_gpio_get_function(struct udevice *dev, unsigned offset)
103 {
104 	struct broadwell_bank_priv *priv = dev_get_priv(dev);
105 	struct pch_lp_gpio_regs *regs = priv->regs;
106 	u32 mask = 1UL << offset;
107 
108 	if (!(inl(&regs->own[priv->bank]) & mask))
109 		return GPIOF_FUNC;
110 	if (inl(&regs->config[priv->offset + offset]) & CONFA_DIR_INPUT)
111 		return GPIOF_INPUT;
112 	else
113 		return GPIOF_OUTPUT;
114 }
115 
116 static int broadwell_gpio_probe(struct udevice *dev)
117 {
118 	struct broadwell_bank_platdata *plat = dev_get_platdata(dev);
119 	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
120 	struct broadwell_bank_priv *priv = dev_get_priv(dev);
121 
122 	uc_priv->gpio_count = GPIO_PER_BANK;
123 	uc_priv->bank_name = plat->bank_name;
124 
125 	priv->regs = (struct pch_lp_gpio_regs *)(uintptr_t)plat->base_addr;
126 	priv->bank = plat->bank;
127 	priv->offset = priv->bank * 32;
128 	debug("%s: probe done, regs %p, bank %d\n", __func__, priv->regs,
129 	      priv->bank);
130 
131 	return 0;
132 }
133 
134 static int broadwell_gpio_ofdata_to_platdata(struct udevice *dev)
135 {
136 	struct broadwell_bank_platdata *plat = dev_get_platdata(dev);
137 	u32 gpiobase;
138 	int bank;
139 	int ret;
140 
141 	ret = pch_get_gpio_base(dev->parent, &gpiobase);
142 	if (ret)
143 		return ret;
144 
145 	bank = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", -1);
146 	if (bank == -1) {
147 		debug("%s: Invalid bank number %d\n", __func__, bank);
148 		return -EINVAL;
149 	}
150 	plat->bank = bank;
151 	plat->base_addr = gpiobase;
152 	plat->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset,
153 				      "bank-name", NULL);
154 
155 	return 0;
156 }
157 
158 static const struct dm_gpio_ops gpio_broadwell_ops = {
159 	.request		= broadwell_gpio_request,
160 	.direction_input	= broadwell_gpio_direction_input,
161 	.direction_output	= broadwell_gpio_direction_output,
162 	.get_value		= broadwell_gpio_get_value,
163 	.set_value		= broadwell_gpio_set_value,
164 	.get_function		= broadwell_gpio_get_function,
165 };
166 
167 static const struct udevice_id intel_broadwell_gpio_ids[] = {
168 	{ .compatible = "intel,broadwell-gpio" },
169 	{ }
170 };
171 
172 U_BOOT_DRIVER(gpio_broadwell) = {
173 	.name	= "gpio_broadwell",
174 	.id	= UCLASS_GPIO,
175 	.of_match = intel_broadwell_gpio_ids,
176 	.ops	= &gpio_broadwell_ops,
177 	.ofdata_to_platdata	= broadwell_gpio_ofdata_to_platdata,
178 	.probe	= broadwell_gpio_probe,
179 	.priv_auto_alloc_size = sizeof(struct broadwell_bank_priv),
180 	.platdata_auto_alloc_size = sizeof(struct broadwell_bank_platdata),
181 };
182