diff options
-rw-r--r-- | drivers/bus/Kconfig | 8 | ||||
-rw-r--r-- | drivers/bus/Makefile | 1 | ||||
-rw-r--r-- | drivers/bus/brcmstb_gisb.c | 289 |
3 files changed, 298 insertions, 0 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index 552373c4e362..d40d155f4234 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig | |||
@@ -4,6 +4,14 @@ | |||
4 | 4 | ||
5 | menu "Bus devices" | 5 | menu "Bus devices" |
6 | 6 | ||
7 | config BRCMSTB_GISB_ARB | ||
8 | bool "Broadcom STB GISB bus arbiter" | ||
9 | depends on ARM | ||
10 | help | ||
11 | Driver for the Broadcom Set Top Box System-on-a-chip internal bus | ||
12 | arbiter. This driver provides timeout and target abort error handling | ||
13 | and internal bus master decoding. | ||
14 | |||
7 | config IMX_WEIM | 15 | config IMX_WEIM |
8 | bool "Freescale EIM DRIVER" | 16 | bool "Freescale EIM DRIVER" |
9 | depends on ARCH_MXC | 17 | depends on ARCH_MXC |
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index 8947bdd0de8b..41fa45b3f061 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile | |||
@@ -2,6 +2,7 @@ | |||
2 | # Makefile for the bus drivers. | 2 | # Makefile for the bus drivers. |
3 | # | 3 | # |
4 | 4 | ||
5 | obj-$(CONFIG_BRCMSTB_GISB_ARB) += brcmstb_gisb.o | ||
5 | obj-$(CONFIG_IMX_WEIM) += imx-weim.o | 6 | obj-$(CONFIG_IMX_WEIM) += imx-weim.o |
6 | obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o | 7 | obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o |
7 | obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o | 8 | obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o |
diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c new file mode 100644 index 000000000000..6159b7752a64 --- /dev/null +++ b/drivers/bus/brcmstb_gisb.c | |||
@@ -0,0 +1,289 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2014 Broadcom Corporation | ||
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 version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | */ | ||
13 | |||
14 | #include <linux/init.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/sysfs.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/string.h> | ||
22 | #include <linux/device.h> | ||
23 | #include <linux/list.h> | ||
24 | #include <linux/of.h> | ||
25 | #include <linux/bitops.h> | ||
26 | |||
27 | #include <asm/bug.h> | ||
28 | #include <asm/signal.h> | ||
29 | |||
30 | #define ARB_TIMER 0x008 | ||
31 | #define ARB_ERR_CAP_CLR 0x7e4 | ||
32 | #define ARB_ERR_CAP_CLEAR (1 << 0) | ||
33 | #define ARB_ERR_CAP_HI_ADDR 0x7e8 | ||
34 | #define ARB_ERR_CAP_ADDR 0x7ec | ||
35 | #define ARB_ERR_CAP_DATA 0x7f0 | ||
36 | #define ARB_ERR_CAP_STATUS 0x7f4 | ||
37 | #define ARB_ERR_CAP_STATUS_TIMEOUT (1 << 12) | ||
38 | #define ARB_ERR_CAP_STATUS_TEA (1 << 11) | ||
39 | #define ARB_ERR_CAP_STATUS_BS_SHIFT (1 << 2) | ||
40 | #define ARB_ERR_CAP_STATUS_BS_MASK 0x3c | ||
41 | #define ARB_ERR_CAP_STATUS_WRITE (1 << 1) | ||
42 | #define ARB_ERR_CAP_STATUS_VALID (1 << 0) | ||
43 | #define ARB_ERR_CAP_MASTER 0x7f8 | ||
44 | |||
45 | struct brcmstb_gisb_arb_device { | ||
46 | void __iomem *base; | ||
47 | struct mutex lock; | ||
48 | struct list_head next; | ||
49 | u32 valid_mask; | ||
50 | const char *master_names[sizeof(u32) * BITS_PER_BYTE]; | ||
51 | }; | ||
52 | |||
53 | static LIST_HEAD(brcmstb_gisb_arb_device_list); | ||
54 | |||
55 | static ssize_t gisb_arb_get_timeout(struct device *dev, | ||
56 | struct device_attribute *attr, | ||
57 | char *buf) | ||
58 | { | ||
59 | struct platform_device *pdev = to_platform_device(dev); | ||
60 | struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); | ||
61 | u32 timeout; | ||
62 | |||
63 | mutex_lock(&gdev->lock); | ||
64 | timeout = ioread32(gdev->base + ARB_TIMER); | ||
65 | mutex_unlock(&gdev->lock); | ||
66 | |||
67 | return sprintf(buf, "%d", timeout); | ||
68 | } | ||
69 | |||
70 | static ssize_t gisb_arb_set_timeout(struct device *dev, | ||
71 | struct device_attribute *attr, | ||
72 | const char *buf, size_t count) | ||
73 | { | ||
74 | struct platform_device *pdev = to_platform_device(dev); | ||
75 | struct brcmstb_gisb_arb_device *gdev = platform_get_drvdata(pdev); | ||
76 | int val, ret; | ||
77 | |||
78 | ret = kstrtoint(buf, 10, &val); | ||
79 | if (ret < 0) | ||
80 | return ret; | ||
81 | |||
82 | if (val == 0 || val >= 0xffffffff) | ||
83 | return -EINVAL; | ||
84 | |||
85 | mutex_lock(&gdev->lock); | ||
86 | iowrite32(val, gdev->base + ARB_TIMER); | ||
87 | mutex_unlock(&gdev->lock); | ||
88 | |||
89 | return count; | ||
90 | } | ||
91 | |||
92 | static const char * | ||
93 | brcmstb_gisb_master_to_str(struct brcmstb_gisb_arb_device *gdev, | ||
94 | u32 masters) | ||
95 | { | ||
96 | u32 mask = gdev->valid_mask & masters; | ||
97 | |||
98 | if (hweight_long(mask) != 1) | ||
99 | return NULL; | ||
100 | |||
101 | return gdev->master_names[ffs(mask) - 1]; | ||
102 | } | ||
103 | |||
104 | static int brcmstb_gisb_arb_decode_addr(struct brcmstb_gisb_arb_device *gdev, | ||
105 | const char *reason) | ||
106 | { | ||
107 | u32 cap_status; | ||
108 | unsigned long arb_addr; | ||
109 | u32 master; | ||
110 | const char *m_name; | ||
111 | char m_fmt[11]; | ||
112 | |||
113 | cap_status = ioread32(gdev->base + ARB_ERR_CAP_STATUS); | ||
114 | |||
115 | /* Invalid captured address, bail out */ | ||
116 | if (!(cap_status & ARB_ERR_CAP_STATUS_VALID)) | ||
117 | return 1; | ||
118 | |||
119 | /* Read the address and master */ | ||
120 | arb_addr = ioread32(gdev->base + ARB_ERR_CAP_ADDR) & 0xffffffff; | ||
121 | #if (IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT)) | ||
122 | arb_addr |= (u64)ioread32(gdev->base + ARB_ERR_CAP_HI_ADDR) << 32; | ||
123 | #endif | ||
124 | master = ioread32(gdev->base + ARB_ERR_CAP_MASTER); | ||
125 | |||
126 | m_name = brcmstb_gisb_master_to_str(gdev, master); | ||
127 | if (!m_name) { | ||
128 | snprintf(m_fmt, sizeof(m_fmt), "0x%08x", master); | ||
129 | m_name = m_fmt; | ||
130 | } | ||
131 | |||
132 | pr_crit("%s: %s at 0x%lx [%c %s], core: %s\n", | ||
133 | __func__, reason, arb_addr, | ||
134 | cap_status & ARB_ERR_CAP_STATUS_WRITE ? 'W' : 'R', | ||
135 | cap_status & ARB_ERR_CAP_STATUS_TIMEOUT ? "timeout" : "", | ||
136 | m_name); | ||
137 | |||
138 | /* clear the GISB error */ | ||
139 | iowrite32(ARB_ERR_CAP_CLEAR, gdev->base + ARB_ERR_CAP_CLR); | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static int brcmstb_bus_error_handler(unsigned long addr, unsigned int fsr, | ||
145 | struct pt_regs *regs) | ||
146 | { | ||
147 | int ret = 0; | ||
148 | struct brcmstb_gisb_arb_device *gdev; | ||
149 | |||
150 | /* iterate over each GISB arb registered handlers */ | ||
151 | list_for_each_entry(gdev, &brcmstb_gisb_arb_device_list, next) | ||
152 | ret |= brcmstb_gisb_arb_decode_addr(gdev, "bus error"); | ||
153 | /* | ||
154 | * If it was an imprecise abort, then we need to correct the | ||
155 | * return address to be _after_ the instruction. | ||
156 | */ | ||
157 | if (fsr & (1 << 10)) | ||
158 | regs->ARM_pc += 4; | ||
159 | |||
160 | return ret; | ||
161 | } | ||
162 | |||
163 | void __init brcmstb_hook_fault_code(void) | ||
164 | { | ||
165 | hook_fault_code(22, brcmstb_bus_error_handler, SIGBUS, 0, | ||
166 | "imprecise external abort"); | ||
167 | } | ||
168 | |||
169 | static irqreturn_t brcmstb_gisb_timeout_handler(int irq, void *dev_id) | ||
170 | { | ||
171 | brcmstb_gisb_arb_decode_addr(dev_id, "timeout"); | ||
172 | |||
173 | return IRQ_HANDLED; | ||
174 | } | ||
175 | |||
176 | static irqreturn_t brcmstb_gisb_tea_handler(int irq, void *dev_id) | ||
177 | { | ||
178 | brcmstb_gisb_arb_decode_addr(dev_id, "target abort"); | ||
179 | |||
180 | return IRQ_HANDLED; | ||
181 | } | ||
182 | |||
183 | static DEVICE_ATTR(gisb_arb_timeout, S_IWUSR | S_IRUGO, | ||
184 | gisb_arb_get_timeout, gisb_arb_set_timeout); | ||
185 | |||
186 | static struct attribute *gisb_arb_sysfs_attrs[] = { | ||
187 | &dev_attr_gisb_arb_timeout.attr, | ||
188 | NULL, | ||
189 | }; | ||
190 | |||
191 | static struct attribute_group gisb_arb_sysfs_attr_group = { | ||
192 | .attrs = gisb_arb_sysfs_attrs, | ||
193 | }; | ||
194 | |||
195 | static int brcmstb_gisb_arb_probe(struct platform_device *pdev) | ||
196 | { | ||
197 | struct device_node *dn = pdev->dev.of_node; | ||
198 | struct brcmstb_gisb_arb_device *gdev; | ||
199 | struct resource *r; | ||
200 | int err, timeout_irq, tea_irq; | ||
201 | unsigned int num_masters, j = 0; | ||
202 | int i, first, last; | ||
203 | |||
204 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
205 | timeout_irq = platform_get_irq(pdev, 0); | ||
206 | tea_irq = platform_get_irq(pdev, 1); | ||
207 | |||
208 | gdev = devm_kzalloc(&pdev->dev, sizeof(*gdev), GFP_KERNEL); | ||
209 | if (!gdev) | ||
210 | return -ENOMEM; | ||
211 | |||
212 | mutex_init(&gdev->lock); | ||
213 | INIT_LIST_HEAD(&gdev->next); | ||
214 | |||
215 | gdev->base = devm_request_and_ioremap(&pdev->dev, r); | ||
216 | if (!gdev->base) | ||
217 | return -ENOMEM; | ||
218 | |||
219 | err = devm_request_irq(&pdev->dev, timeout_irq, | ||
220 | brcmstb_gisb_timeout_handler, 0, pdev->name, | ||
221 | gdev); | ||
222 | if (err < 0) | ||
223 | return err; | ||
224 | |||
225 | err = devm_request_irq(&pdev->dev, tea_irq, | ||
226 | brcmstb_gisb_tea_handler, 0, pdev->name, | ||
227 | gdev); | ||
228 | if (err < 0) | ||
229 | return err; | ||
230 | |||
231 | /* If we do not have a valid mask, assume all masters are enabled */ | ||
232 | if (of_property_read_u32(dn, "brcm,gisb-arb-master-mask", | ||
233 | &gdev->valid_mask)) | ||
234 | gdev->valid_mask = 0xffffffff; | ||
235 | |||
236 | /* Proceed with reading the litteral names if we agree on the | ||
237 | * number of masters | ||
238 | */ | ||
239 | num_masters = of_property_count_strings(dn, | ||
240 | "brcm,gisb-arb-master-names"); | ||
241 | if (hweight_long(gdev->valid_mask) == num_masters) { | ||
242 | first = ffs(gdev->valid_mask) - 1; | ||
243 | last = fls(gdev->valid_mask) - 1; | ||
244 | |||
245 | for (i = first; i < last; i++) { | ||
246 | if (!(gdev->valid_mask & BIT(i))) | ||
247 | continue; | ||
248 | |||
249 | of_property_read_string_index(dn, | ||
250 | "brcm,gisb-arb-master-names", j, | ||
251 | &gdev->master_names[i]); | ||
252 | j++; | ||
253 | } | ||
254 | } | ||
255 | |||
256 | err = sysfs_create_group(&pdev->dev.kobj, &gisb_arb_sysfs_attr_group); | ||
257 | if (err) | ||
258 | return err; | ||
259 | |||
260 | platform_set_drvdata(pdev, gdev); | ||
261 | |||
262 | list_add_tail(&gdev->next, &brcmstb_gisb_arb_device_list); | ||
263 | |||
264 | dev_info(&pdev->dev, "registered mem: %p, irqs: %d, %d\n", | ||
265 | gdev->base, timeout_irq, tea_irq); | ||
266 | |||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | static const struct of_device_id brcmstb_gisb_arb_of_match[] = { | ||
271 | { .compatible = "brcm,gisb-arb" }, | ||
272 | { }, | ||
273 | }; | ||
274 | |||
275 | static struct platform_driver brcmstb_gisb_arb_driver = { | ||
276 | .probe = brcmstb_gisb_arb_probe, | ||
277 | .driver = { | ||
278 | .name = "brcm-gisb-arb", | ||
279 | .owner = THIS_MODULE, | ||
280 | .of_match_table = brcmstb_gisb_arb_of_match, | ||
281 | }, | ||
282 | }; | ||
283 | |||
284 | static int __init brcm_gisb_driver_init(void) | ||
285 | { | ||
286 | return platform_driver_register(&brcmstb_gisb_arb_driver); | ||
287 | } | ||
288 | |||
289 | module_init(brcm_gisb_driver_init); | ||