root/drivers/dma/ti/dma-crossbar.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ti_am335x_xbar_write
  2. ti_am335x_xbar_free
  3. ti_am335x_xbar_route_allocate
  4. ti_am335x_xbar_probe
  5. ti_dra7_xbar_write
  6. ti_dra7_xbar_free
  7. ti_dra7_xbar_route_allocate
  8. ti_dra7_xbar_reserve
  9. ti_dra7_xbar_probe
  10. ti_dma_xbar_probe
  11. omap_dmaxbar_init

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

/* [<][>][^][v][top][bottom][index][help] */