1/* 2 * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com 3 * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 */ 10#include <linux/slab.h> 11#include <linux/err.h> 12#include <linux/init.h> 13#include <linux/list.h> 14#include <linux/io.h> 15#include <linux/idr.h> 16#include <linux/of_address.h> 17#include <linux/of_device.h> 18#include <linux/of_dma.h> 19 20#define TI_XBAR_DRA7 0 21#define TI_XBAR_AM335X 1 22 23static const struct of_device_id ti_dma_xbar_match[] = { 24 { 25 .compatible = "ti,dra7-dma-crossbar", 26 .data = (void *)TI_XBAR_DRA7, 27 }, 28 { 29 .compatible = "ti,am335x-edma-crossbar", 30 .data = (void *)TI_XBAR_AM335X, 31 }, 32 {}, 33}; 34 35/* Crossbar on AM335x/AM437x family */ 36#define TI_AM335X_XBAR_LINES 64 37 38struct ti_am335x_xbar_data { 39 void __iomem *iomem; 40 41 struct dma_router dmarouter; 42 43 u32 xbar_events; /* maximum number of events to select in xbar */ 44 u32 dma_requests; /* number of DMA requests on eDMA */ 45}; 46 47struct ti_am335x_xbar_map { 48 u16 dma_line; 49 u16 mux_val; 50}; 51 52static inline void ti_am335x_xbar_write(void __iomem *iomem, int event, u16 val) 53{ 54 writeb_relaxed(val & 0x1f, iomem + event); 55} 56 57static void ti_am335x_xbar_free(struct device *dev, void *route_data) 58{ 59 struct ti_am335x_xbar_data *xbar = dev_get_drvdata(dev); 60 struct ti_am335x_xbar_map *map = route_data; 61 62 dev_dbg(dev, "Unmapping XBAR event %u on channel %u\n", 63 map->mux_val, map->dma_line); 64 65 ti_am335x_xbar_write(xbar->iomem, map->dma_line, 0); 66 kfree(map); 67} 68 69static void *ti_am335x_xbar_route_allocate(struct of_phandle_args *dma_spec, 70 struct of_dma *ofdma) 71{ 72 struct platform_device *pdev = of_find_device_by_node(ofdma->of_node); 73 struct ti_am335x_xbar_data *xbar = platform_get_drvdata(pdev); 74 struct ti_am335x_xbar_map *map; 75 76 if (dma_spec->args_count != 3) 77 return ERR_PTR(-EINVAL); 78 79 if (dma_spec->args[2] >= xbar->xbar_events) { 80 dev_err(&pdev->dev, "Invalid XBAR event number: %d\n", 81 dma_spec->args[2]); 82 return ERR_PTR(-EINVAL); 83 } 84 85 if (dma_spec->args[0] >= xbar->dma_requests) { 86 dev_err(&pdev->dev, "Invalid DMA request line number: %d\n", 87 dma_spec->args[0]); 88 return ERR_PTR(-EINVAL); 89 } 90 91 /* The of_node_put() will be done in the core for the node */ 92 dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", 0); 93 if (!dma_spec->np) { 94 dev_err(&pdev->dev, "Can't get DMA master\n"); 95 return ERR_PTR(-EINVAL); 96 } 97 98 map = kzalloc(sizeof(*map), GFP_KERNEL); 99 if (!map) { 100 of_node_put(dma_spec->np); 101 return ERR_PTR(-ENOMEM); 102 } 103 104 map->dma_line = (u16)dma_spec->args[0]; 105 map->mux_val = (u16)dma_spec->args[2]; 106 107 dma_spec->args[2] = 0; 108 dma_spec->args_count = 2; 109 110 dev_dbg(&pdev->dev, "Mapping XBAR event%u to DMA%u\n", 111 map->mux_val, map->dma_line); 112 113 ti_am335x_xbar_write(xbar->iomem, map->dma_line, map->mux_val); 114 115 return map; 116} 117 118static const struct of_device_id ti_am335x_master_match[] = { 119 { .compatible = "ti,edma3-tpcc", }, 120 {}, 121}; 122 123static int ti_am335x_xbar_probe(struct platform_device *pdev) 124{ 125 struct device_node *node = pdev->dev.of_node; 126 const struct of_device_id *match; 127 struct device_node *dma_node; 128 struct ti_am335x_xbar_data *xbar; 129 struct resource *res; 130 void __iomem *iomem; 131 int i, ret; 132 133 if (!node) 134 return -ENODEV; 135 136 xbar = devm_kzalloc(&pdev->dev, sizeof(*xbar), GFP_KERNEL); 137 if (!xbar) 138 return -ENOMEM; 139 140 dma_node = of_parse_phandle(node, "dma-masters", 0); 141 if (!dma_node) { 142 dev_err(&pdev->dev, "Can't get DMA master node\n"); 143 return -ENODEV; 144 } 145 146 match = of_match_node(ti_am335x_master_match, dma_node); 147 if (!match) { 148 dev_err(&pdev->dev, "DMA master is not supported\n"); 149 return -EINVAL; 150 } 151 152 if (of_property_read_u32(dma_node, "dma-requests", 153 &xbar->dma_requests)) { 154 dev_info(&pdev->dev, 155 "Missing XBAR output information, using %u.\n", 156 TI_AM335X_XBAR_LINES); 157 xbar->dma_requests = TI_AM335X_XBAR_LINES; 158 } 159 of_node_put(dma_node); 160 161 if (of_property_read_u32(node, "dma-requests", &xbar->xbar_events)) { 162 dev_info(&pdev->dev, 163 "Missing XBAR input information, using %u.\n", 164 TI_AM335X_XBAR_LINES); 165 xbar->xbar_events = TI_AM335X_XBAR_LINES; 166 } 167 168 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 169 iomem = devm_ioremap_resource(&pdev->dev, res); 170 if (IS_ERR(iomem)) 171 return PTR_ERR(iomem); 172 173 xbar->iomem = iomem; 174 175 xbar->dmarouter.dev = &pdev->dev; 176 xbar->dmarouter.route_free = ti_am335x_xbar_free; 177 178 platform_set_drvdata(pdev, xbar); 179 180 /* Reset the crossbar */ 181 for (i = 0; i < xbar->dma_requests; i++) 182 ti_am335x_xbar_write(xbar->iomem, i, 0); 183 184 ret = of_dma_router_register(node, ti_am335x_xbar_route_allocate, 185 &xbar->dmarouter); 186 187 return ret; 188} 189 190/* Crossbar on DRA7xx family */ 191#define TI_DRA7_XBAR_OUTPUTS 127 192#define TI_DRA7_XBAR_INPUTS 256 193 194#define TI_XBAR_EDMA_OFFSET 0 195#define TI_XBAR_SDMA_OFFSET 1 196 197struct ti_dra7_xbar_data { 198 void __iomem *iomem; 199 200 struct dma_router dmarouter; 201 struct idr map_idr; 202 203 u16 safe_val; /* Value to rest the crossbar lines */ 204 u32 xbar_requests; /* number of DMA requests connected to XBAR */ 205 u32 dma_requests; /* number of DMA requests forwarded to DMA */ 206 u32 dma_offset; 207}; 208 209struct ti_dra7_xbar_map { 210 u16 xbar_in; 211 int xbar_out; 212}; 213 214static inline void ti_dra7_xbar_write(void __iomem *iomem, int xbar, u16 val) 215{ 216 writew_relaxed(val, iomem + (xbar * 2)); 217} 218 219static void ti_dra7_xbar_free(struct device *dev, void *route_data) 220{ 221 struct ti_dra7_xbar_data *xbar = dev_get_drvdata(dev); 222 struct ti_dra7_xbar_map *map = route_data; 223 224 dev_dbg(dev, "Unmapping XBAR%u (was routed to %d)\n", 225 map->xbar_in, map->xbar_out); 226 227 ti_dra7_xbar_write(xbar->iomem, map->xbar_out, xbar->safe_val); 228 idr_remove(&xbar->map_idr, map->xbar_out); 229 kfree(map); 230} 231 232static void *ti_dra7_xbar_route_allocate(struct of_phandle_args *dma_spec, 233 struct of_dma *ofdma) 234{ 235 struct platform_device *pdev = of_find_device_by_node(ofdma->of_node); 236 struct ti_dra7_xbar_data *xbar = platform_get_drvdata(pdev); 237 struct ti_dra7_xbar_map *map; 238 239 if (dma_spec->args[0] >= xbar->xbar_requests) { 240 dev_err(&pdev->dev, "Invalid XBAR request number: %d\n", 241 dma_spec->args[0]); 242 return ERR_PTR(-EINVAL); 243 } 244 245 /* The of_node_put() will be done in the core for the node */ 246 dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", 0); 247 if (!dma_spec->np) { 248 dev_err(&pdev->dev, "Can't get DMA master\n"); 249 return ERR_PTR(-EINVAL); 250 } 251 252 map = kzalloc(sizeof(*map), GFP_KERNEL); 253 if (!map) { 254 of_node_put(dma_spec->np); 255 return ERR_PTR(-ENOMEM); 256 } 257 258 map->xbar_out = idr_alloc(&xbar->map_idr, NULL, 0, xbar->dma_requests, 259 GFP_KERNEL); 260 map->xbar_in = (u16)dma_spec->args[0]; 261 262 dma_spec->args[0] = map->xbar_out + xbar->dma_offset; 263 264 dev_dbg(&pdev->dev, "Mapping XBAR%u to DMA%d\n", 265 map->xbar_in, map->xbar_out); 266 267 ti_dra7_xbar_write(xbar->iomem, map->xbar_out, map->xbar_in); 268 269 return map; 270} 271 272static const struct of_device_id ti_dra7_master_match[] = { 273 { 274 .compatible = "ti,omap4430-sdma", 275 .data = (void *)TI_XBAR_SDMA_OFFSET, 276 }, 277 { 278 .compatible = "ti,edma3", 279 .data = (void *)TI_XBAR_EDMA_OFFSET, 280 }, 281 {}, 282}; 283 284static int ti_dra7_xbar_probe(struct platform_device *pdev) 285{ 286 struct device_node *node = pdev->dev.of_node; 287 const struct of_device_id *match; 288 struct device_node *dma_node; 289 struct ti_dra7_xbar_data *xbar; 290 struct resource *res; 291 u32 safe_val; 292 void __iomem *iomem; 293 int i, ret; 294 295 if (!node) 296 return -ENODEV; 297 298 xbar = devm_kzalloc(&pdev->dev, sizeof(*xbar), GFP_KERNEL); 299 if (!xbar) 300 return -ENOMEM; 301 302 idr_init(&xbar->map_idr); 303 304 dma_node = of_parse_phandle(node, "dma-masters", 0); 305 if (!dma_node) { 306 dev_err(&pdev->dev, "Can't get DMA master node\n"); 307 return -ENODEV; 308 } 309 310 match = of_match_node(ti_dra7_master_match, dma_node); 311 if (!match) { 312 dev_err(&pdev->dev, "DMA master is not supported\n"); 313 return -EINVAL; 314 } 315 316 if (of_property_read_u32(dma_node, "dma-requests", 317 &xbar->dma_requests)) { 318 dev_info(&pdev->dev, 319 "Missing XBAR output information, using %u.\n", 320 TI_DRA7_XBAR_OUTPUTS); 321 xbar->dma_requests = TI_DRA7_XBAR_OUTPUTS; 322 } 323 of_node_put(dma_node); 324 325 if (of_property_read_u32(node, "dma-requests", &xbar->xbar_requests)) { 326 dev_info(&pdev->dev, 327 "Missing XBAR input information, using %u.\n", 328 TI_DRA7_XBAR_INPUTS); 329 xbar->xbar_requests = TI_DRA7_XBAR_INPUTS; 330 } 331 332 if (!of_property_read_u32(node, "ti,dma-safe-map", &safe_val)) 333 xbar->safe_val = (u16)safe_val; 334 335 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 336 iomem = devm_ioremap_resource(&pdev->dev, res); 337 if (IS_ERR(iomem)) 338 return PTR_ERR(iomem); 339 340 xbar->iomem = iomem; 341 342 xbar->dmarouter.dev = &pdev->dev; 343 xbar->dmarouter.route_free = ti_dra7_xbar_free; 344 xbar->dma_offset = (u32)match->data; 345 346 platform_set_drvdata(pdev, xbar); 347 348 /* Reset the crossbar */ 349 for (i = 0; i < xbar->dma_requests; i++) 350 ti_dra7_xbar_write(xbar->iomem, i, xbar->safe_val); 351 352 ret = of_dma_router_register(node, ti_dra7_xbar_route_allocate, 353 &xbar->dmarouter); 354 if (ret) { 355 /* Restore the defaults for the crossbar */ 356 for (i = 0; i < xbar->dma_requests; i++) 357 ti_dra7_xbar_write(xbar->iomem, i, i); 358 } 359 360 return ret; 361} 362 363static int ti_dma_xbar_probe(struct platform_device *pdev) 364{ 365 const struct of_device_id *match; 366 int ret; 367 368 match = of_match_node(ti_dma_xbar_match, pdev->dev.of_node); 369 if (unlikely(!match)) 370 return -EINVAL; 371 372 switch ((u32)match->data) { 373 case TI_XBAR_DRA7: 374 ret = ti_dra7_xbar_probe(pdev); 375 break; 376 case TI_XBAR_AM335X: 377 ret = ti_am335x_xbar_probe(pdev); 378 break; 379 default: 380 dev_err(&pdev->dev, "Unsupported crossbar\n"); 381 ret = -ENODEV; 382 break; 383 } 384 385 return ret; 386} 387 388static struct platform_driver ti_dma_xbar_driver = { 389 .driver = { 390 .name = "ti-dma-crossbar", 391 .of_match_table = of_match_ptr(ti_dma_xbar_match), 392 }, 393 .probe = ti_dma_xbar_probe, 394}; 395 396int omap_dmaxbar_init(void) 397{ 398 return platform_driver_register(&ti_dma_xbar_driver); 399} 400arch_initcall(omap_dmaxbar_init); 401