18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com 48c2ecf20Sopenharmony_ci * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci#include <linux/err.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/list.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/of_address.h> 128c2ecf20Sopenharmony_ci#include <linux/of_device.h> 138c2ecf20Sopenharmony_ci#include <linux/of_dma.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define TI_XBAR_DRA7 0 168c2ecf20Sopenharmony_ci#define TI_XBAR_AM335X 1 178c2ecf20Sopenharmony_cistatic const u32 ti_xbar_type[] = { 188c2ecf20Sopenharmony_ci [TI_XBAR_DRA7] = TI_XBAR_DRA7, 198c2ecf20Sopenharmony_ci [TI_XBAR_AM335X] = TI_XBAR_AM335X, 208c2ecf20Sopenharmony_ci}; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic const struct of_device_id ti_dma_xbar_match[] = { 238c2ecf20Sopenharmony_ci { 248c2ecf20Sopenharmony_ci .compatible = "ti,dra7-dma-crossbar", 258c2ecf20Sopenharmony_ci .data = &ti_xbar_type[TI_XBAR_DRA7], 268c2ecf20Sopenharmony_ci }, 278c2ecf20Sopenharmony_ci { 288c2ecf20Sopenharmony_ci .compatible = "ti,am335x-edma-crossbar", 298c2ecf20Sopenharmony_ci .data = &ti_xbar_type[TI_XBAR_AM335X], 308c2ecf20Sopenharmony_ci }, 318c2ecf20Sopenharmony_ci {}, 328c2ecf20Sopenharmony_ci}; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Crossbar on AM335x/AM437x family */ 358c2ecf20Sopenharmony_ci#define TI_AM335X_XBAR_LINES 64 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct ti_am335x_xbar_data { 388c2ecf20Sopenharmony_ci void __iomem *iomem; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci struct dma_router dmarouter; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci u32 xbar_events; /* maximum number of events to select in xbar */ 438c2ecf20Sopenharmony_ci u32 dma_requests; /* number of DMA requests on eDMA */ 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct ti_am335x_xbar_map { 478c2ecf20Sopenharmony_ci u16 dma_line; 488c2ecf20Sopenharmony_ci u8 mux_val; 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic inline void ti_am335x_xbar_write(void __iomem *iomem, int event, u8 val) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci /* 548c2ecf20Sopenharmony_ci * TPCC_EVT_MUX_60_63 register layout is different than the 558c2ecf20Sopenharmony_ci * rest, in the sense, that event 63 is mapped to lowest byte 568c2ecf20Sopenharmony_ci * and event 60 is mapped to highest, handle it separately. 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci if (event >= 60 && event <= 63) 598c2ecf20Sopenharmony_ci writeb_relaxed(val, iomem + (63 - event % 4)); 608c2ecf20Sopenharmony_ci else 618c2ecf20Sopenharmony_ci writeb_relaxed(val, iomem + event); 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic void ti_am335x_xbar_free(struct device *dev, void *route_data) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci struct ti_am335x_xbar_data *xbar = dev_get_drvdata(dev); 678c2ecf20Sopenharmony_ci struct ti_am335x_xbar_map *map = route_data; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci dev_dbg(dev, "Unmapping XBAR event %u on channel %u\n", 708c2ecf20Sopenharmony_ci map->mux_val, map->dma_line); 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci ti_am335x_xbar_write(xbar->iomem, map->dma_line, 0); 738c2ecf20Sopenharmony_ci kfree(map); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic void *ti_am335x_xbar_route_allocate(struct of_phandle_args *dma_spec, 778c2ecf20Sopenharmony_ci struct of_dma *ofdma) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct platform_device *pdev = of_find_device_by_node(ofdma->of_node); 808c2ecf20Sopenharmony_ci struct ti_am335x_xbar_data *xbar = platform_get_drvdata(pdev); 818c2ecf20Sopenharmony_ci struct ti_am335x_xbar_map *map; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci if (dma_spec->args_count != 3) 848c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if (dma_spec->args[2] >= xbar->xbar_events) { 878c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Invalid XBAR event number: %d\n", 888c2ecf20Sopenharmony_ci dma_spec->args[2]); 898c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci if (dma_spec->args[0] >= xbar->dma_requests) { 938c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Invalid DMA request line number: %d\n", 948c2ecf20Sopenharmony_ci dma_spec->args[0]); 958c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci /* The of_node_put() will be done in the core for the node */ 998c2ecf20Sopenharmony_ci dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", 0); 1008c2ecf20Sopenharmony_ci if (!dma_spec->np) { 1018c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't get DMA master\n"); 1028c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1038c2ecf20Sopenharmony_ci } 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci map = kzalloc(sizeof(*map), GFP_KERNEL); 1068c2ecf20Sopenharmony_ci if (!map) { 1078c2ecf20Sopenharmony_ci of_node_put(dma_spec->np); 1088c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci map->dma_line = (u16)dma_spec->args[0]; 1128c2ecf20Sopenharmony_ci map->mux_val = (u8)dma_spec->args[2]; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci dma_spec->args[2] = 0; 1158c2ecf20Sopenharmony_ci dma_spec->args_count = 2; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Mapping XBAR event%u to DMA%u\n", 1188c2ecf20Sopenharmony_ci map->mux_val, map->dma_line); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci ti_am335x_xbar_write(xbar->iomem, map->dma_line, map->mux_val); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci return map; 1238c2ecf20Sopenharmony_ci} 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic const struct of_device_id ti_am335x_master_match[] = { 1268c2ecf20Sopenharmony_ci { .compatible = "ti,edma3-tpcc", }, 1278c2ecf20Sopenharmony_ci {}, 1288c2ecf20Sopenharmony_ci}; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int ti_am335x_xbar_probe(struct platform_device *pdev) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 1338c2ecf20Sopenharmony_ci const struct of_device_id *match; 1348c2ecf20Sopenharmony_ci struct device_node *dma_node; 1358c2ecf20Sopenharmony_ci struct ti_am335x_xbar_data *xbar; 1368c2ecf20Sopenharmony_ci void __iomem *iomem; 1378c2ecf20Sopenharmony_ci int i, ret; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci if (!node) 1408c2ecf20Sopenharmony_ci return -ENODEV; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci xbar = devm_kzalloc(&pdev->dev, sizeof(*xbar), GFP_KERNEL); 1438c2ecf20Sopenharmony_ci if (!xbar) 1448c2ecf20Sopenharmony_ci return -ENOMEM; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci dma_node = of_parse_phandle(node, "dma-masters", 0); 1478c2ecf20Sopenharmony_ci if (!dma_node) { 1488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't get DMA master node\n"); 1498c2ecf20Sopenharmony_ci return -ENODEV; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci match = of_match_node(ti_am335x_master_match, dma_node); 1538c2ecf20Sopenharmony_ci if (!match) { 1548c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "DMA master is not supported\n"); 1558c2ecf20Sopenharmony_ci of_node_put(dma_node); 1568c2ecf20Sopenharmony_ci return -EINVAL; 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (of_property_read_u32(dma_node, "dma-requests", 1608c2ecf20Sopenharmony_ci &xbar->dma_requests)) { 1618c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 1628c2ecf20Sopenharmony_ci "Missing XBAR output information, using %u.\n", 1638c2ecf20Sopenharmony_ci TI_AM335X_XBAR_LINES); 1648c2ecf20Sopenharmony_ci xbar->dma_requests = TI_AM335X_XBAR_LINES; 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci of_node_put(dma_node); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (of_property_read_u32(node, "dma-requests", &xbar->xbar_events)) { 1698c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 1708c2ecf20Sopenharmony_ci "Missing XBAR input information, using %u.\n", 1718c2ecf20Sopenharmony_ci TI_AM335X_XBAR_LINES); 1728c2ecf20Sopenharmony_ci xbar->xbar_events = TI_AM335X_XBAR_LINES; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci iomem = devm_platform_ioremap_resource(pdev, 0); 1768c2ecf20Sopenharmony_ci if (IS_ERR(iomem)) 1778c2ecf20Sopenharmony_ci return PTR_ERR(iomem); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci xbar->iomem = iomem; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci xbar->dmarouter.dev = &pdev->dev; 1828c2ecf20Sopenharmony_ci xbar->dmarouter.route_free = ti_am335x_xbar_free; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, xbar); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Reset the crossbar */ 1878c2ecf20Sopenharmony_ci for (i = 0; i < xbar->dma_requests; i++) 1888c2ecf20Sopenharmony_ci ti_am335x_xbar_write(xbar->iomem, i, 0); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci ret = of_dma_router_register(node, ti_am335x_xbar_route_allocate, 1918c2ecf20Sopenharmony_ci &xbar->dmarouter); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci return ret; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* Crossbar on DRA7xx family */ 1978c2ecf20Sopenharmony_ci#define TI_DRA7_XBAR_OUTPUTS 127 1988c2ecf20Sopenharmony_ci#define TI_DRA7_XBAR_INPUTS 256 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_cistruct ti_dra7_xbar_data { 2018c2ecf20Sopenharmony_ci void __iomem *iomem; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci struct dma_router dmarouter; 2048c2ecf20Sopenharmony_ci struct mutex mutex; 2058c2ecf20Sopenharmony_ci unsigned long *dma_inuse; 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci u16 safe_val; /* Value to rest the crossbar lines */ 2088c2ecf20Sopenharmony_ci u32 xbar_requests; /* number of DMA requests connected to XBAR */ 2098c2ecf20Sopenharmony_ci u32 dma_requests; /* number of DMA requests forwarded to DMA */ 2108c2ecf20Sopenharmony_ci u32 dma_offset; 2118c2ecf20Sopenharmony_ci}; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistruct ti_dra7_xbar_map { 2148c2ecf20Sopenharmony_ci u16 xbar_in; 2158c2ecf20Sopenharmony_ci int xbar_out; 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic inline void ti_dra7_xbar_write(void __iomem *iomem, int xbar, u16 val) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci writew_relaxed(val, iomem + (xbar * 2)); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic void ti_dra7_xbar_free(struct device *dev, void *route_data) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct ti_dra7_xbar_data *xbar = dev_get_drvdata(dev); 2268c2ecf20Sopenharmony_ci struct ti_dra7_xbar_map *map = route_data; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci dev_dbg(dev, "Unmapping XBAR%u (was routed to %d)\n", 2298c2ecf20Sopenharmony_ci map->xbar_in, map->xbar_out); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci ti_dra7_xbar_write(xbar->iomem, map->xbar_out, xbar->safe_val); 2328c2ecf20Sopenharmony_ci mutex_lock(&xbar->mutex); 2338c2ecf20Sopenharmony_ci clear_bit(map->xbar_out, xbar->dma_inuse); 2348c2ecf20Sopenharmony_ci mutex_unlock(&xbar->mutex); 2358c2ecf20Sopenharmony_ci kfree(map); 2368c2ecf20Sopenharmony_ci} 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_cistatic void *ti_dra7_xbar_route_allocate(struct of_phandle_args *dma_spec, 2398c2ecf20Sopenharmony_ci struct of_dma *ofdma) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct platform_device *pdev = of_find_device_by_node(ofdma->of_node); 2428c2ecf20Sopenharmony_ci struct ti_dra7_xbar_data *xbar = platform_get_drvdata(pdev); 2438c2ecf20Sopenharmony_ci struct ti_dra7_xbar_map *map; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (dma_spec->args[0] >= xbar->xbar_requests) { 2468c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Invalid XBAR request number: %d\n", 2478c2ecf20Sopenharmony_ci dma_spec->args[0]); 2488c2ecf20Sopenharmony_ci put_device(&pdev->dev); 2498c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* The of_node_put() will be done in the core for the node */ 2538c2ecf20Sopenharmony_ci dma_spec->np = of_parse_phandle(ofdma->of_node, "dma-masters", 0); 2548c2ecf20Sopenharmony_ci if (!dma_spec->np) { 2558c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't get DMA master\n"); 2568c2ecf20Sopenharmony_ci put_device(&pdev->dev); 2578c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci map = kzalloc(sizeof(*map), GFP_KERNEL); 2618c2ecf20Sopenharmony_ci if (!map) { 2628c2ecf20Sopenharmony_ci of_node_put(dma_spec->np); 2638c2ecf20Sopenharmony_ci put_device(&pdev->dev); 2648c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci mutex_lock(&xbar->mutex); 2688c2ecf20Sopenharmony_ci map->xbar_out = find_first_zero_bit(xbar->dma_inuse, 2698c2ecf20Sopenharmony_ci xbar->dma_requests); 2708c2ecf20Sopenharmony_ci if (map->xbar_out == xbar->dma_requests) { 2718c2ecf20Sopenharmony_ci mutex_unlock(&xbar->mutex); 2728c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Run out of free DMA requests\n"); 2738c2ecf20Sopenharmony_ci kfree(map); 2748c2ecf20Sopenharmony_ci of_node_put(dma_spec->np); 2758c2ecf20Sopenharmony_ci put_device(&pdev->dev); 2768c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2778c2ecf20Sopenharmony_ci } 2788c2ecf20Sopenharmony_ci set_bit(map->xbar_out, xbar->dma_inuse); 2798c2ecf20Sopenharmony_ci mutex_unlock(&xbar->mutex); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci map->xbar_in = (u16)dma_spec->args[0]; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci dma_spec->args[0] = map->xbar_out + xbar->dma_offset; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Mapping XBAR%u to DMA%d\n", 2868c2ecf20Sopenharmony_ci map->xbar_in, map->xbar_out); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci ti_dra7_xbar_write(xbar->iomem, map->xbar_out, map->xbar_in); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci return map; 2918c2ecf20Sopenharmony_ci} 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci#define TI_XBAR_EDMA_OFFSET 0 2948c2ecf20Sopenharmony_ci#define TI_XBAR_SDMA_OFFSET 1 2958c2ecf20Sopenharmony_cistatic const u32 ti_dma_offset[] = { 2968c2ecf20Sopenharmony_ci [TI_XBAR_EDMA_OFFSET] = 0, 2978c2ecf20Sopenharmony_ci [TI_XBAR_SDMA_OFFSET] = 1, 2988c2ecf20Sopenharmony_ci}; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic const struct of_device_id ti_dra7_master_match[] = { 3018c2ecf20Sopenharmony_ci { 3028c2ecf20Sopenharmony_ci .compatible = "ti,omap4430-sdma", 3038c2ecf20Sopenharmony_ci .data = &ti_dma_offset[TI_XBAR_SDMA_OFFSET], 3048c2ecf20Sopenharmony_ci }, 3058c2ecf20Sopenharmony_ci { 3068c2ecf20Sopenharmony_ci .compatible = "ti,edma3", 3078c2ecf20Sopenharmony_ci .data = &ti_dma_offset[TI_XBAR_EDMA_OFFSET], 3088c2ecf20Sopenharmony_ci }, 3098c2ecf20Sopenharmony_ci { 3108c2ecf20Sopenharmony_ci .compatible = "ti,edma3-tpcc", 3118c2ecf20Sopenharmony_ci .data = &ti_dma_offset[TI_XBAR_EDMA_OFFSET], 3128c2ecf20Sopenharmony_ci }, 3138c2ecf20Sopenharmony_ci {}, 3148c2ecf20Sopenharmony_ci}; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_cistatic inline void ti_dra7_xbar_reserve(int offset, int len, unsigned long *p) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci for (; len > 0; len--) 3198c2ecf20Sopenharmony_ci set_bit(offset + (len - 1), p); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic int ti_dra7_xbar_probe(struct platform_device *pdev) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct device_node *node = pdev->dev.of_node; 3258c2ecf20Sopenharmony_ci const struct of_device_id *match; 3268c2ecf20Sopenharmony_ci struct device_node *dma_node; 3278c2ecf20Sopenharmony_ci struct ti_dra7_xbar_data *xbar; 3288c2ecf20Sopenharmony_ci struct property *prop; 3298c2ecf20Sopenharmony_ci u32 safe_val; 3308c2ecf20Sopenharmony_ci int sz; 3318c2ecf20Sopenharmony_ci void __iomem *iomem; 3328c2ecf20Sopenharmony_ci int i, ret; 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (!node) 3358c2ecf20Sopenharmony_ci return -ENODEV; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci xbar = devm_kzalloc(&pdev->dev, sizeof(*xbar), GFP_KERNEL); 3388c2ecf20Sopenharmony_ci if (!xbar) 3398c2ecf20Sopenharmony_ci return -ENOMEM; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci dma_node = of_parse_phandle(node, "dma-masters", 0); 3428c2ecf20Sopenharmony_ci if (!dma_node) { 3438c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Can't get DMA master node\n"); 3448c2ecf20Sopenharmony_ci return -ENODEV; 3458c2ecf20Sopenharmony_ci } 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci match = of_match_node(ti_dra7_master_match, dma_node); 3488c2ecf20Sopenharmony_ci if (!match) { 3498c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "DMA master is not supported\n"); 3508c2ecf20Sopenharmony_ci of_node_put(dma_node); 3518c2ecf20Sopenharmony_ci return -EINVAL; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci if (of_property_read_u32(dma_node, "dma-requests", 3558c2ecf20Sopenharmony_ci &xbar->dma_requests)) { 3568c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 3578c2ecf20Sopenharmony_ci "Missing XBAR output information, using %u.\n", 3588c2ecf20Sopenharmony_ci TI_DRA7_XBAR_OUTPUTS); 3598c2ecf20Sopenharmony_ci xbar->dma_requests = TI_DRA7_XBAR_OUTPUTS; 3608c2ecf20Sopenharmony_ci } 3618c2ecf20Sopenharmony_ci of_node_put(dma_node); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci xbar->dma_inuse = devm_kcalloc(&pdev->dev, 3648c2ecf20Sopenharmony_ci BITS_TO_LONGS(xbar->dma_requests), 3658c2ecf20Sopenharmony_ci sizeof(unsigned long), GFP_KERNEL); 3668c2ecf20Sopenharmony_ci if (!xbar->dma_inuse) 3678c2ecf20Sopenharmony_ci return -ENOMEM; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (of_property_read_u32(node, "dma-requests", &xbar->xbar_requests)) { 3708c2ecf20Sopenharmony_ci dev_info(&pdev->dev, 3718c2ecf20Sopenharmony_ci "Missing XBAR input information, using %u.\n", 3728c2ecf20Sopenharmony_ci TI_DRA7_XBAR_INPUTS); 3738c2ecf20Sopenharmony_ci xbar->xbar_requests = TI_DRA7_XBAR_INPUTS; 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (!of_property_read_u32(node, "ti,dma-safe-map", &safe_val)) 3778c2ecf20Sopenharmony_ci xbar->safe_val = (u16)safe_val; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci prop = of_find_property(node, "ti,reserved-dma-request-ranges", &sz); 3818c2ecf20Sopenharmony_ci if (prop) { 3828c2ecf20Sopenharmony_ci const char pname[] = "ti,reserved-dma-request-ranges"; 3838c2ecf20Sopenharmony_ci u32 (*rsv_events)[2]; 3848c2ecf20Sopenharmony_ci size_t nelm = sz / sizeof(*rsv_events); 3858c2ecf20Sopenharmony_ci int i; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (!nelm) 3888c2ecf20Sopenharmony_ci return -EINVAL; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci rsv_events = kcalloc(nelm, sizeof(*rsv_events), GFP_KERNEL); 3918c2ecf20Sopenharmony_ci if (!rsv_events) 3928c2ecf20Sopenharmony_ci return -ENOMEM; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(node, pname, (u32 *)rsv_events, 3958c2ecf20Sopenharmony_ci nelm * 2); 3968c2ecf20Sopenharmony_ci if (ret) { 3978c2ecf20Sopenharmony_ci kfree(rsv_events); 3988c2ecf20Sopenharmony_ci return ret; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci for (i = 0; i < nelm; i++) { 4028c2ecf20Sopenharmony_ci ti_dra7_xbar_reserve(rsv_events[i][0], rsv_events[i][1], 4038c2ecf20Sopenharmony_ci xbar->dma_inuse); 4048c2ecf20Sopenharmony_ci } 4058c2ecf20Sopenharmony_ci kfree(rsv_events); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci iomem = devm_platform_ioremap_resource(pdev, 0); 4098c2ecf20Sopenharmony_ci if (IS_ERR(iomem)) 4108c2ecf20Sopenharmony_ci return PTR_ERR(iomem); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci xbar->iomem = iomem; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci xbar->dmarouter.dev = &pdev->dev; 4158c2ecf20Sopenharmony_ci xbar->dmarouter.route_free = ti_dra7_xbar_free; 4168c2ecf20Sopenharmony_ci xbar->dma_offset = *(u32 *)match->data; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci mutex_init(&xbar->mutex); 4198c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, xbar); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci /* Reset the crossbar */ 4228c2ecf20Sopenharmony_ci for (i = 0; i < xbar->dma_requests; i++) { 4238c2ecf20Sopenharmony_ci if (!test_bit(i, xbar->dma_inuse)) 4248c2ecf20Sopenharmony_ci ti_dra7_xbar_write(xbar->iomem, i, xbar->safe_val); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci ret = of_dma_router_register(node, ti_dra7_xbar_route_allocate, 4288c2ecf20Sopenharmony_ci &xbar->dmarouter); 4298c2ecf20Sopenharmony_ci if (ret) { 4308c2ecf20Sopenharmony_ci /* Restore the defaults for the crossbar */ 4318c2ecf20Sopenharmony_ci for (i = 0; i < xbar->dma_requests; i++) { 4328c2ecf20Sopenharmony_ci if (!test_bit(i, xbar->dma_inuse)) 4338c2ecf20Sopenharmony_ci ti_dra7_xbar_write(xbar->iomem, i, i); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci return ret; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic int ti_dma_xbar_probe(struct platform_device *pdev) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci const struct of_device_id *match; 4438c2ecf20Sopenharmony_ci int ret; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci match = of_match_node(ti_dma_xbar_match, pdev->dev.of_node); 4468c2ecf20Sopenharmony_ci if (unlikely(!match)) 4478c2ecf20Sopenharmony_ci return -EINVAL; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci switch (*(u32 *)match->data) { 4508c2ecf20Sopenharmony_ci case TI_XBAR_DRA7: 4518c2ecf20Sopenharmony_ci ret = ti_dra7_xbar_probe(pdev); 4528c2ecf20Sopenharmony_ci break; 4538c2ecf20Sopenharmony_ci case TI_XBAR_AM335X: 4548c2ecf20Sopenharmony_ci ret = ti_am335x_xbar_probe(pdev); 4558c2ecf20Sopenharmony_ci break; 4568c2ecf20Sopenharmony_ci default: 4578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unsupported crossbar\n"); 4588c2ecf20Sopenharmony_ci ret = -ENODEV; 4598c2ecf20Sopenharmony_ci break; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci return ret; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic struct platform_driver ti_dma_xbar_driver = { 4668c2ecf20Sopenharmony_ci .driver = { 4678c2ecf20Sopenharmony_ci .name = "ti-dma-crossbar", 4688c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(ti_dma_xbar_match), 4698c2ecf20Sopenharmony_ci }, 4708c2ecf20Sopenharmony_ci .probe = ti_dma_xbar_probe, 4718c2ecf20Sopenharmony_ci}; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_cistatic int omap_dmaxbar_init(void) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci return platform_driver_register(&ti_dma_xbar_driver); 4768c2ecf20Sopenharmony_ci} 4778c2ecf20Sopenharmony_ciarch_initcall(omap_dmaxbar_init); 478