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