summaryrefslogtreecommitdiffstats
path: root/drivers/bus
diff options
context:
space:
mode:
authorMasahiro Yamada <yamada.masahiro@socionext.com>2015-12-09 01:52:59 -0500
committerOlof Johansson <olof@lixom.net>2015-12-22 14:22:39 -0500
commit4b7f48d395a7e3b11ded7695ac2b36d0685e0785 (patch)
tree1fd3f7a0123835280d7b85dc18f8ad409b5d1f71 /drivers/bus
parent70b3590f639f4e753e6f556209cb89b812879402 (diff)
bus: uniphier-system-bus: add UniPhier System Bus driver
The UniPhier System Bus is an external bus that connects on-board devices to the UniPhier SoC. Each bank (chip select) is dynamically mapped to the CPU-viewed address base via the bus controller. The bus controller must be configured before any access to the bus. This driver parses the "ranges" property of the System Bus node and initialized the bus controller. After the bus becomes ready, devices below it are populated. Note: Each bank can be mapped anywhere in the supported address space; there is nothing preventing us from assigning bank 0 on 0x42000000, 0x43000000, or anywhere as long as such region is not used by others. So, the "ranges" is just one possible software configuration, which does not seem to fit in device tree because device tree is a hardware description language. However, of_translate_address() requires "ranges" in every bus node between CPUs and device mapped on the CPU address space. In other words, "ranges" properties must be statically defined in device tree. After some discussion, I decided the dynamic address reassignment by the driver is too bothersome. Instead, the device tree should provide a reasonable translation setup that the OS can rely on. Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com> Acked-by: Rob Herring <robh@kernel.org> Acked-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Olof Johansson <olof@lixom.net>
Diffstat (limited to 'drivers/bus')
-rw-r--r--drivers/bus/Kconfig8
-rw-r--r--drivers/bus/Makefile1
-rw-r--r--drivers/bus/uniphier-system-bus.c281
3 files changed, 290 insertions, 0 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 116b363b7987..9a92c072a485 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -131,6 +131,14 @@ config SUNXI_RSB
131 with various RSB based devices, such as AXP223, AXP8XX PMICs, 131 with various RSB based devices, such as AXP223, AXP8XX PMICs,
132 and AC100/AC200 ICs. 132 and AC100/AC200 ICs.
133 133
134config UNIPHIER_SYSTEM_BUS
135 tristate "UniPhier System Bus driver"
136 depends on ARCH_UNIPHIER && OF
137 default y
138 help
139 Support for UniPhier System Bus, a simple external bus. This is
140 needed to use on-board devices connected to UniPhier SoCs.
141
134config VEXPRESS_CONFIG 142config VEXPRESS_CONFIG
135 bool "Versatile Express configuration bus" 143 bool "Versatile Express configuration bus"
136 default y if ARCH_VEXPRESS 144 default y if ARCH_VEXPRESS
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index fcb9f9794a1f..ccff007ee7e8 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -17,4 +17,5 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
17obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o 17obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
18obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o 18obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o
19obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o 19obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
20obj-$(CONFIG_UNIPHIER_SYSTEM_BUS) += uniphier-system-bus.o
20obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o 21obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
diff --git a/drivers/bus/uniphier-system-bus.c b/drivers/bus/uniphier-system-bus.c
new file mode 100644
index 000000000000..834a2aeaf27a
--- /dev/null
+++ b/drivers/bus/uniphier-system-bus.c
@@ -0,0 +1,281 @@
1/*
2 * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include <linux/io.h>
16#include <linux/log2.h>
17#include <linux/module.h>
18#include <linux/of.h>
19#include <linux/of_address.h>
20#include <linux/of_platform.h>
21#include <linux/platform_device.h>
22
23/* System Bus Controller registers */
24#define UNIPHIER_SBC_BASE 0x100 /* base address of bank0 space */
25#define UNIPHIER_SBC_BASE_BE BIT(0) /* bank_enable */
26#define UNIPHIER_SBC_CTRL0 0x200 /* timing parameter 0 of bank0 */
27#define UNIPHIER_SBC_CTRL1 0x204 /* timing parameter 1 of bank0 */
28#define UNIPHIER_SBC_CTRL2 0x208 /* timing parameter 2 of bank0 */
29#define UNIPHIER_SBC_CTRL3 0x20c /* timing parameter 3 of bank0 */
30#define UNIPHIER_SBC_CTRL4 0x300 /* timing parameter 4 of bank0 */
31
32#define UNIPHIER_SBC_STRIDE 0x10 /* register stride to next bank */
33#define UNIPHIER_SBC_NR_BANKS 8 /* number of banks (chip select) */
34#define UNIPHIER_SBC_BASE_DUMMY 0xffffffff /* data to squash bank 0, 1 */
35
36struct uniphier_system_bus_bank {
37 u32 base;
38 u32 end;
39};
40
41struct uniphier_system_bus_priv {
42 struct device *dev;
43 void __iomem *membase;
44 struct uniphier_system_bus_bank bank[UNIPHIER_SBC_NR_BANKS];
45};
46
47static int uniphier_system_bus_add_bank(struct uniphier_system_bus_priv *priv,
48 int bank, u32 addr, u64 paddr, u32 size)
49{
50 u64 end, mask;
51
52 dev_dbg(priv->dev,
53 "range found: bank = %d, addr = %08x, paddr = %08llx, size = %08x\n",
54 bank, addr, paddr, size);
55
56 if (bank >= ARRAY_SIZE(priv->bank)) {
57 dev_err(priv->dev, "unsupported bank number %d\n", bank);
58 return -EINVAL;
59 }
60
61 if (priv->bank[bank].base || priv->bank[bank].end) {
62 dev_err(priv->dev,
63 "range for bank %d has already been specified\n", bank);
64 return -EINVAL;
65 }
66
67 if (paddr > U32_MAX) {
68 dev_err(priv->dev, "base address %llx is too high\n", paddr);
69 return -EINVAL;
70 }
71
72 end = paddr + size;
73
74 if (addr > paddr) {
75 dev_err(priv->dev,
76 "base %08x cannot be mapped to %08llx of parent\n",
77 addr, paddr);
78 return -EINVAL;
79 }
80 paddr -= addr;
81
82 paddr = round_down(paddr, 0x00020000);
83 end = round_up(end, 0x00020000);
84
85 if (end > U32_MAX) {
86 dev_err(priv->dev, "end address %08llx is too high\n", end);
87 return -EINVAL;
88 }
89 mask = paddr ^ (end - 1);
90 mask = roundup_pow_of_two(mask);
91
92 paddr = round_down(paddr, mask);
93 end = round_up(end, mask);
94
95 priv->bank[bank].base = paddr;
96 priv->bank[bank].end = end;
97
98 dev_dbg(priv->dev, "range added: bank = %d, addr = %08x, end = %08x\n",
99 bank, priv->bank[bank].base, priv->bank[bank].end);
100
101 return 0;
102}
103
104static int uniphier_system_bus_check_overlap(
105 const struct uniphier_system_bus_priv *priv)
106{
107 int i, j;
108
109 for (i = 0; i < ARRAY_SIZE(priv->bank); i++) {
110 for (j = i + 1; j < ARRAY_SIZE(priv->bank); j++) {
111 if (priv->bank[i].end > priv->bank[j].base ||
112 priv->bank[i].base < priv->bank[j].end) {
113 dev_err(priv->dev,
114 "region overlap between bank%d and bank%d\n",
115 i, j);
116 return -EINVAL;
117 }
118 }
119 }
120
121 return 0;
122}
123
124static void uniphier_system_bus_check_boot_swap(
125 struct uniphier_system_bus_priv *priv)
126{
127 void __iomem *base_reg = priv->membase + UNIPHIER_SBC_BASE;
128 int is_swapped;
129
130 is_swapped = !(readl(base_reg) & UNIPHIER_SBC_BASE_BE);
131
132 dev_dbg(priv->dev, "Boot Swap: %s\n", is_swapped ? "on" : "off");
133
134 /*
135 * If BOOT_SWAP was asserted on power-on-reset, the CS0 and CS1 are
136 * swapped. In this case, bank0 and bank1 should be swapped as well.
137 */
138 if (is_swapped)
139 swap(priv->bank[0], priv->bank[1]);
140}
141
142static void uniphier_system_bus_set_reg(
143 const struct uniphier_system_bus_priv *priv)
144{
145 void __iomem *base_reg = priv->membase + UNIPHIER_SBC_BASE;
146 u32 base, end, mask, val;
147 int i;
148
149 for (i = 0; i < ARRAY_SIZE(priv->bank); i++) {
150 base = priv->bank[i].base;
151 end = priv->bank[i].end;
152
153 if (base == end) {
154 /*
155 * If SBC_BASE0 or SBC_BASE1 is set to zero, the access
156 * to anywhere in the system bus space is routed to
157 * bank 0 (if boot swap if off) or bank 1 (if boot swap
158 * if on). It means that CPUs cannot get access to
159 * bank 2 or later. In other words, bank 0/1 cannot
160 * be disabled even if its bank_enable bits is cleared.
161 * This seems odd, but it is how this hardware goes.
162 * As a workaround, dummy data (0xffffffff) should be
163 * set when the bank 0/1 is unused. As for bank 2 and
164 * later, they can be simply disable by clearing the
165 * bank_enable bit.
166 */
167 if (i < 2)
168 val = UNIPHIER_SBC_BASE_DUMMY;
169 else
170 val = 0;
171 } else {
172 mask = base ^ (end - 1);
173
174 val = base & 0xfffe0000;
175 val |= (~mask >> 16) & 0xfffe;
176 val |= UNIPHIER_SBC_BASE_BE;
177 }
178 dev_dbg(priv->dev, "SBC_BASE[%d] = 0x%08x\n", i, val);
179
180 writel(val, base_reg + UNIPHIER_SBC_STRIDE * i);
181 }
182}
183
184static int uniphier_system_bus_probe(struct platform_device *pdev)
185{
186 struct device *dev = &pdev->dev;
187 struct uniphier_system_bus_priv *priv;
188 struct resource *regs;
189 const __be32 *ranges;
190 u32 cells, addr, size;
191 u64 paddr;
192 int pna, bank, rlen, rone, ret;
193
194 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
195 if (!priv)
196 return -ENOMEM;
197
198 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
199 priv->membase = devm_ioremap_resource(dev, regs);
200 if (IS_ERR(priv->membase))
201 return PTR_ERR(priv->membase);
202
203 priv->dev = dev;
204
205 pna = of_n_addr_cells(dev->of_node);
206
207 ret = of_property_read_u32(dev->of_node, "#address-cells", &cells);
208 if (ret) {
209 dev_err(dev, "failed to get #address-cells\n");
210 return ret;
211 }
212 if (cells != 2) {
213 dev_err(dev, "#address-cells must be 2\n");
214 return -EINVAL;
215 }
216
217 ret = of_property_read_u32(dev->of_node, "#size-cells", &cells);
218 if (ret) {
219 dev_err(dev, "failed to get #size-cells\n");
220 return ret;
221 }
222 if (cells != 1) {
223 dev_err(dev, "#size-cells must be 1\n");
224 return -EINVAL;
225 }
226
227 ranges = of_get_property(dev->of_node, "ranges", &rlen);
228 if (!ranges) {
229 dev_err(dev, "failed to get ranges property\n");
230 return -ENOENT;
231 }
232
233 rlen /= sizeof(*ranges);
234 rone = pna + 2;
235
236 for (; rlen >= rone; rlen -= rone) {
237 bank = be32_to_cpup(ranges++);
238 addr = be32_to_cpup(ranges++);
239 paddr = of_translate_address(dev->of_node, ranges);
240 if (paddr == OF_BAD_ADDR)
241 return -EINVAL;
242 ranges += pna;
243 size = be32_to_cpup(ranges++);
244
245 ret = uniphier_system_bus_add_bank(priv, bank, addr,
246 paddr, size);
247 if (ret)
248 return ret;
249 }
250
251 ret = uniphier_system_bus_check_overlap(priv);
252 if (ret)
253 return ret;
254
255 uniphier_system_bus_check_boot_swap(priv);
256
257 uniphier_system_bus_set_reg(priv);
258
259 /* Now, the bus is configured. Populate platform_devices below it */
260 return of_platform_populate(dev->of_node, of_default_bus_match_table,
261 NULL, dev);
262}
263
264static const struct of_device_id uniphier_system_bus_match[] = {
265 { .compatible = "socionext,uniphier-system-bus" },
266 { /* sentinel */ }
267};
268MODULE_DEVICE_TABLE(of, uniphier_system_bus_match);
269
270static struct platform_driver uniphier_system_bus_driver = {
271 .probe = uniphier_system_bus_probe,
272 .driver = {
273 .name = "uniphier-system-bus",
274 .of_match_table = uniphier_system_bus_match,
275 },
276};
277module_platform_driver(uniphier_system_bus_driver);
278
279MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
280MODULE_DESCRIPTION("UniPhier System Bus driver");
281MODULE_LICENSE("GPL");