18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Qualcomm Technologies HIDMA DMA engine Management interface
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/dmaengine.h>
98c2ecf20Sopenharmony_ci#include <linux/acpi.h>
108c2ecf20Sopenharmony_ci#include <linux/of.h>
118c2ecf20Sopenharmony_ci#include <linux/property.h>
128c2ecf20Sopenharmony_ci#include <linux/of_address.h>
138c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
148c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
198c2ecf20Sopenharmony_ci#include <linux/bitops.h>
208c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "hidma_mgmt.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define HIDMA_QOS_N_OFFSET		0x700
258c2ecf20Sopenharmony_ci#define HIDMA_CFG_OFFSET		0x400
268c2ecf20Sopenharmony_ci#define HIDMA_MAX_BUS_REQ_LEN_OFFSET	0x41C
278c2ecf20Sopenharmony_ci#define HIDMA_MAX_XACTIONS_OFFSET	0x420
288c2ecf20Sopenharmony_ci#define HIDMA_HW_VERSION_OFFSET	0x424
298c2ecf20Sopenharmony_ci#define HIDMA_CHRESET_TIMEOUT_OFFSET	0x418
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define HIDMA_MAX_WR_XACTIONS_MASK	GENMASK(4, 0)
328c2ecf20Sopenharmony_ci#define HIDMA_MAX_RD_XACTIONS_MASK	GENMASK(4, 0)
338c2ecf20Sopenharmony_ci#define HIDMA_WEIGHT_MASK		GENMASK(6, 0)
348c2ecf20Sopenharmony_ci#define HIDMA_MAX_BUS_REQ_LEN_MASK	GENMASK(15, 0)
358c2ecf20Sopenharmony_ci#define HIDMA_CHRESET_TIMEOUT_MASK	GENMASK(19, 0)
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define HIDMA_MAX_WR_XACTIONS_BIT_POS	16
388c2ecf20Sopenharmony_ci#define HIDMA_MAX_BUS_WR_REQ_BIT_POS	16
398c2ecf20Sopenharmony_ci#define HIDMA_WRR_BIT_POS		8
408c2ecf20Sopenharmony_ci#define HIDMA_PRIORITY_BIT_POS		15
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#define HIDMA_AUTOSUSPEND_TIMEOUT	2000
438c2ecf20Sopenharmony_ci#define HIDMA_MAX_CHANNEL_WEIGHT	15
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_cistatic unsigned int max_write_request;
468c2ecf20Sopenharmony_cimodule_param(max_write_request, uint, 0644);
478c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_write_request,
488c2ecf20Sopenharmony_ci		"maximum write burst (default: ACPI/DT value)");
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic unsigned int max_read_request;
518c2ecf20Sopenharmony_cimodule_param(max_read_request, uint, 0644);
528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_read_request,
538c2ecf20Sopenharmony_ci		"maximum read burst (default: ACPI/DT value)");
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic unsigned int max_wr_xactions;
568c2ecf20Sopenharmony_cimodule_param(max_wr_xactions, uint, 0644);
578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_wr_xactions,
588c2ecf20Sopenharmony_ci	"maximum number of write transactions (default: ACPI/DT value)");
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic unsigned int max_rd_xactions;
618c2ecf20Sopenharmony_cimodule_param(max_rd_xactions, uint, 0644);
628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(max_rd_xactions,
638c2ecf20Sopenharmony_ci	"maximum number of read transactions (default: ACPI/DT value)");
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ciint hidma_mgmt_setup(struct hidma_mgmt_dev *mgmtdev)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	unsigned int i;
688c2ecf20Sopenharmony_ci	u32 val;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	if (!is_power_of_2(mgmtdev->max_write_request) ||
718c2ecf20Sopenharmony_ci	    (mgmtdev->max_write_request < 128) ||
728c2ecf20Sopenharmony_ci	    (mgmtdev->max_write_request > 1024)) {
738c2ecf20Sopenharmony_ci		dev_err(&mgmtdev->pdev->dev, "invalid write request %d\n",
748c2ecf20Sopenharmony_ci			mgmtdev->max_write_request);
758c2ecf20Sopenharmony_ci		return -EINVAL;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	if (!is_power_of_2(mgmtdev->max_read_request) ||
798c2ecf20Sopenharmony_ci	    (mgmtdev->max_read_request < 128) ||
808c2ecf20Sopenharmony_ci	    (mgmtdev->max_read_request > 1024)) {
818c2ecf20Sopenharmony_ci		dev_err(&mgmtdev->pdev->dev, "invalid read request %d\n",
828c2ecf20Sopenharmony_ci			mgmtdev->max_read_request);
838c2ecf20Sopenharmony_ci		return -EINVAL;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	if (mgmtdev->max_wr_xactions > HIDMA_MAX_WR_XACTIONS_MASK) {
878c2ecf20Sopenharmony_ci		dev_err(&mgmtdev->pdev->dev,
888c2ecf20Sopenharmony_ci			"max_wr_xactions cannot be bigger than %ld\n",
898c2ecf20Sopenharmony_ci			HIDMA_MAX_WR_XACTIONS_MASK);
908c2ecf20Sopenharmony_ci		return -EINVAL;
918c2ecf20Sopenharmony_ci	}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (mgmtdev->max_rd_xactions > HIDMA_MAX_RD_XACTIONS_MASK) {
948c2ecf20Sopenharmony_ci		dev_err(&mgmtdev->pdev->dev,
958c2ecf20Sopenharmony_ci			"max_rd_xactions cannot be bigger than %ld\n",
968c2ecf20Sopenharmony_ci			HIDMA_MAX_RD_XACTIONS_MASK);
978c2ecf20Sopenharmony_ci		return -EINVAL;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	for (i = 0; i < mgmtdev->dma_channels; i++) {
1018c2ecf20Sopenharmony_ci		if (mgmtdev->priority[i] > 1) {
1028c2ecf20Sopenharmony_ci			dev_err(&mgmtdev->pdev->dev,
1038c2ecf20Sopenharmony_ci				"priority can be 0 or 1\n");
1048c2ecf20Sopenharmony_ci			return -EINVAL;
1058c2ecf20Sopenharmony_ci		}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		if (mgmtdev->weight[i] > HIDMA_MAX_CHANNEL_WEIGHT) {
1088c2ecf20Sopenharmony_ci			dev_err(&mgmtdev->pdev->dev,
1098c2ecf20Sopenharmony_ci				"max value of weight can be %d.\n",
1108c2ecf20Sopenharmony_ci				HIDMA_MAX_CHANNEL_WEIGHT);
1118c2ecf20Sopenharmony_ci			return -EINVAL;
1128c2ecf20Sopenharmony_ci		}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci		/* weight needs to be at least one */
1158c2ecf20Sopenharmony_ci		if (mgmtdev->weight[i] == 0)
1168c2ecf20Sopenharmony_ci			mgmtdev->weight[i] = 1;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	pm_runtime_get_sync(&mgmtdev->pdev->dev);
1208c2ecf20Sopenharmony_ci	val = readl(mgmtdev->virtaddr + HIDMA_MAX_BUS_REQ_LEN_OFFSET);
1218c2ecf20Sopenharmony_ci	val &= ~(HIDMA_MAX_BUS_REQ_LEN_MASK << HIDMA_MAX_BUS_WR_REQ_BIT_POS);
1228c2ecf20Sopenharmony_ci	val |= mgmtdev->max_write_request << HIDMA_MAX_BUS_WR_REQ_BIT_POS;
1238c2ecf20Sopenharmony_ci	val &= ~HIDMA_MAX_BUS_REQ_LEN_MASK;
1248c2ecf20Sopenharmony_ci	val |= mgmtdev->max_read_request;
1258c2ecf20Sopenharmony_ci	writel(val, mgmtdev->virtaddr + HIDMA_MAX_BUS_REQ_LEN_OFFSET);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	val = readl(mgmtdev->virtaddr + HIDMA_MAX_XACTIONS_OFFSET);
1288c2ecf20Sopenharmony_ci	val &= ~(HIDMA_MAX_WR_XACTIONS_MASK << HIDMA_MAX_WR_XACTIONS_BIT_POS);
1298c2ecf20Sopenharmony_ci	val |= mgmtdev->max_wr_xactions << HIDMA_MAX_WR_XACTIONS_BIT_POS;
1308c2ecf20Sopenharmony_ci	val &= ~HIDMA_MAX_RD_XACTIONS_MASK;
1318c2ecf20Sopenharmony_ci	val |= mgmtdev->max_rd_xactions;
1328c2ecf20Sopenharmony_ci	writel(val, mgmtdev->virtaddr + HIDMA_MAX_XACTIONS_OFFSET);
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	mgmtdev->hw_version =
1358c2ecf20Sopenharmony_ci	    readl(mgmtdev->virtaddr + HIDMA_HW_VERSION_OFFSET);
1368c2ecf20Sopenharmony_ci	mgmtdev->hw_version_major = (mgmtdev->hw_version >> 28) & 0xF;
1378c2ecf20Sopenharmony_ci	mgmtdev->hw_version_minor = (mgmtdev->hw_version >> 16) & 0xF;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	for (i = 0; i < mgmtdev->dma_channels; i++) {
1408c2ecf20Sopenharmony_ci		u32 weight = mgmtdev->weight[i];
1418c2ecf20Sopenharmony_ci		u32 priority = mgmtdev->priority[i];
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		val = readl(mgmtdev->virtaddr + HIDMA_QOS_N_OFFSET + (4 * i));
1448c2ecf20Sopenharmony_ci		val &= ~(1 << HIDMA_PRIORITY_BIT_POS);
1458c2ecf20Sopenharmony_ci		val |= (priority & 0x1) << HIDMA_PRIORITY_BIT_POS;
1468c2ecf20Sopenharmony_ci		val &= ~(HIDMA_WEIGHT_MASK << HIDMA_WRR_BIT_POS);
1478c2ecf20Sopenharmony_ci		val |= (weight & HIDMA_WEIGHT_MASK) << HIDMA_WRR_BIT_POS;
1488c2ecf20Sopenharmony_ci		writel(val, mgmtdev->virtaddr + HIDMA_QOS_N_OFFSET + (4 * i));
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	val = readl(mgmtdev->virtaddr + HIDMA_CHRESET_TIMEOUT_OFFSET);
1528c2ecf20Sopenharmony_ci	val &= ~HIDMA_CHRESET_TIMEOUT_MASK;
1538c2ecf20Sopenharmony_ci	val |= mgmtdev->chreset_timeout_cycles & HIDMA_CHRESET_TIMEOUT_MASK;
1548c2ecf20Sopenharmony_ci	writel(val, mgmtdev->virtaddr + HIDMA_CHRESET_TIMEOUT_OFFSET);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(&mgmtdev->pdev->dev);
1578c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(&mgmtdev->pdev->dev);
1588c2ecf20Sopenharmony_ci	return 0;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(hidma_mgmt_setup);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int hidma_mgmt_probe(struct platform_device *pdev)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct hidma_mgmt_dev *mgmtdev;
1658c2ecf20Sopenharmony_ci	struct resource *res;
1668c2ecf20Sopenharmony_ci	void __iomem *virtaddr;
1678c2ecf20Sopenharmony_ci	int irq;
1688c2ecf20Sopenharmony_ci	int rc;
1698c2ecf20Sopenharmony_ci	u32 val;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	pm_runtime_set_autosuspend_delay(&pdev->dev, HIDMA_AUTOSUSPEND_TIMEOUT);
1728c2ecf20Sopenharmony_ci	pm_runtime_use_autosuspend(&pdev->dev);
1738c2ecf20Sopenharmony_ci	pm_runtime_set_active(&pdev->dev);
1748c2ecf20Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
1758c2ecf20Sopenharmony_ci	pm_runtime_get_sync(&pdev->dev);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1788c2ecf20Sopenharmony_ci	virtaddr = devm_ioremap_resource(&pdev->dev, res);
1798c2ecf20Sopenharmony_ci	if (IS_ERR(virtaddr)) {
1808c2ecf20Sopenharmony_ci		rc = -ENOMEM;
1818c2ecf20Sopenharmony_ci		goto out;
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
1858c2ecf20Sopenharmony_ci	if (irq < 0) {
1868c2ecf20Sopenharmony_ci		rc = irq;
1878c2ecf20Sopenharmony_ci		goto out;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	mgmtdev = devm_kzalloc(&pdev->dev, sizeof(*mgmtdev), GFP_KERNEL);
1918c2ecf20Sopenharmony_ci	if (!mgmtdev) {
1928c2ecf20Sopenharmony_ci		rc = -ENOMEM;
1938c2ecf20Sopenharmony_ci		goto out;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	mgmtdev->pdev = pdev;
1978c2ecf20Sopenharmony_ci	mgmtdev->addrsize = resource_size(res);
1988c2ecf20Sopenharmony_ci	mgmtdev->virtaddr = virtaddr;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	rc = device_property_read_u32(&pdev->dev, "dma-channels",
2018c2ecf20Sopenharmony_ci				      &mgmtdev->dma_channels);
2028c2ecf20Sopenharmony_ci	if (rc) {
2038c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "number of channels missing\n");
2048c2ecf20Sopenharmony_ci		goto out;
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	rc = device_property_read_u32(&pdev->dev,
2088c2ecf20Sopenharmony_ci				      "channel-reset-timeout-cycles",
2098c2ecf20Sopenharmony_ci				      &mgmtdev->chreset_timeout_cycles);
2108c2ecf20Sopenharmony_ci	if (rc) {
2118c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "channel reset timeout missing\n");
2128c2ecf20Sopenharmony_ci		goto out;
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	rc = device_property_read_u32(&pdev->dev, "max-write-burst-bytes",
2168c2ecf20Sopenharmony_ci				      &mgmtdev->max_write_request);
2178c2ecf20Sopenharmony_ci	if (rc) {
2188c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "max-write-burst-bytes missing\n");
2198c2ecf20Sopenharmony_ci		goto out;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	if (max_write_request &&
2238c2ecf20Sopenharmony_ci			(max_write_request != mgmtdev->max_write_request)) {
2248c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "overriding max-write-burst-bytes: %d\n",
2258c2ecf20Sopenharmony_ci			max_write_request);
2268c2ecf20Sopenharmony_ci		mgmtdev->max_write_request = max_write_request;
2278c2ecf20Sopenharmony_ci	} else
2288c2ecf20Sopenharmony_ci		max_write_request = mgmtdev->max_write_request;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	rc = device_property_read_u32(&pdev->dev, "max-read-burst-bytes",
2318c2ecf20Sopenharmony_ci				      &mgmtdev->max_read_request);
2328c2ecf20Sopenharmony_ci	if (rc) {
2338c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "max-read-burst-bytes missing\n");
2348c2ecf20Sopenharmony_ci		goto out;
2358c2ecf20Sopenharmony_ci	}
2368c2ecf20Sopenharmony_ci	if (max_read_request &&
2378c2ecf20Sopenharmony_ci			(max_read_request != mgmtdev->max_read_request)) {
2388c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "overriding max-read-burst-bytes: %d\n",
2398c2ecf20Sopenharmony_ci			max_read_request);
2408c2ecf20Sopenharmony_ci		mgmtdev->max_read_request = max_read_request;
2418c2ecf20Sopenharmony_ci	} else
2428c2ecf20Sopenharmony_ci		max_read_request = mgmtdev->max_read_request;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	rc = device_property_read_u32(&pdev->dev, "max-write-transactions",
2458c2ecf20Sopenharmony_ci				      &mgmtdev->max_wr_xactions);
2468c2ecf20Sopenharmony_ci	if (rc) {
2478c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "max-write-transactions missing\n");
2488c2ecf20Sopenharmony_ci		goto out;
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci	if (max_wr_xactions &&
2518c2ecf20Sopenharmony_ci			(max_wr_xactions != mgmtdev->max_wr_xactions)) {
2528c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "overriding max-write-transactions: %d\n",
2538c2ecf20Sopenharmony_ci			max_wr_xactions);
2548c2ecf20Sopenharmony_ci		mgmtdev->max_wr_xactions = max_wr_xactions;
2558c2ecf20Sopenharmony_ci	} else
2568c2ecf20Sopenharmony_ci		max_wr_xactions = mgmtdev->max_wr_xactions;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	rc = device_property_read_u32(&pdev->dev, "max-read-transactions",
2598c2ecf20Sopenharmony_ci				      &mgmtdev->max_rd_xactions);
2608c2ecf20Sopenharmony_ci	if (rc) {
2618c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "max-read-transactions missing\n");
2628c2ecf20Sopenharmony_ci		goto out;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci	if (max_rd_xactions &&
2658c2ecf20Sopenharmony_ci			(max_rd_xactions != mgmtdev->max_rd_xactions)) {
2668c2ecf20Sopenharmony_ci		dev_info(&pdev->dev, "overriding max-read-transactions: %d\n",
2678c2ecf20Sopenharmony_ci			max_rd_xactions);
2688c2ecf20Sopenharmony_ci		mgmtdev->max_rd_xactions = max_rd_xactions;
2698c2ecf20Sopenharmony_ci	} else
2708c2ecf20Sopenharmony_ci		max_rd_xactions = mgmtdev->max_rd_xactions;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	mgmtdev->priority = devm_kcalloc(&pdev->dev,
2738c2ecf20Sopenharmony_ci					 mgmtdev->dma_channels,
2748c2ecf20Sopenharmony_ci					 sizeof(*mgmtdev->priority),
2758c2ecf20Sopenharmony_ci					 GFP_KERNEL);
2768c2ecf20Sopenharmony_ci	if (!mgmtdev->priority) {
2778c2ecf20Sopenharmony_ci		rc = -ENOMEM;
2788c2ecf20Sopenharmony_ci		goto out;
2798c2ecf20Sopenharmony_ci	}
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	mgmtdev->weight = devm_kcalloc(&pdev->dev,
2828c2ecf20Sopenharmony_ci				       mgmtdev->dma_channels,
2838c2ecf20Sopenharmony_ci				       sizeof(*mgmtdev->weight), GFP_KERNEL);
2848c2ecf20Sopenharmony_ci	if (!mgmtdev->weight) {
2858c2ecf20Sopenharmony_ci		rc = -ENOMEM;
2868c2ecf20Sopenharmony_ci		goto out;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	rc = hidma_mgmt_setup(mgmtdev);
2908c2ecf20Sopenharmony_ci	if (rc) {
2918c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "setup failed\n");
2928c2ecf20Sopenharmony_ci		goto out;
2938c2ecf20Sopenharmony_ci	}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	/* start the HW */
2968c2ecf20Sopenharmony_ci	val = readl(mgmtdev->virtaddr + HIDMA_CFG_OFFSET);
2978c2ecf20Sopenharmony_ci	val |= 1;
2988c2ecf20Sopenharmony_ci	writel(val, mgmtdev->virtaddr + HIDMA_CFG_OFFSET);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	rc = hidma_mgmt_init_sys(mgmtdev);
3018c2ecf20Sopenharmony_ci	if (rc) {
3028c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "sysfs setup failed\n");
3038c2ecf20Sopenharmony_ci		goto out;
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	dev_info(&pdev->dev,
3078c2ecf20Sopenharmony_ci		 "HW rev: %d.%d @ %pa with %d physical channels\n",
3088c2ecf20Sopenharmony_ci		 mgmtdev->hw_version_major, mgmtdev->hw_version_minor,
3098c2ecf20Sopenharmony_ci		 &res->start, mgmtdev->dma_channels);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, mgmtdev);
3128c2ecf20Sopenharmony_ci	pm_runtime_mark_last_busy(&pdev->dev);
3138c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(&pdev->dev);
3148c2ecf20Sopenharmony_ci	return 0;
3158c2ecf20Sopenharmony_ciout:
3168c2ecf20Sopenharmony_ci	pm_runtime_put_sync_suspend(&pdev->dev);
3178c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
3188c2ecf20Sopenharmony_ci	return rc;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_ACPI)
3228c2ecf20Sopenharmony_cistatic const struct acpi_device_id hidma_mgmt_acpi_ids[] = {
3238c2ecf20Sopenharmony_ci	{"QCOM8060"},
3248c2ecf20Sopenharmony_ci	{},
3258c2ecf20Sopenharmony_ci};
3268c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, hidma_mgmt_acpi_ids);
3278c2ecf20Sopenharmony_ci#endif
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic const struct of_device_id hidma_mgmt_match[] = {
3308c2ecf20Sopenharmony_ci	{.compatible = "qcom,hidma-mgmt-1.0",},
3318c2ecf20Sopenharmony_ci	{},
3328c2ecf20Sopenharmony_ci};
3338c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, hidma_mgmt_match);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic struct platform_driver hidma_mgmt_driver = {
3368c2ecf20Sopenharmony_ci	.probe = hidma_mgmt_probe,
3378c2ecf20Sopenharmony_ci	.driver = {
3388c2ecf20Sopenharmony_ci		   .name = "hidma-mgmt",
3398c2ecf20Sopenharmony_ci		   .of_match_table = hidma_mgmt_match,
3408c2ecf20Sopenharmony_ci		   .acpi_match_table = ACPI_PTR(hidma_mgmt_acpi_ids),
3418c2ecf20Sopenharmony_ci	},
3428c2ecf20Sopenharmony_ci};
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
3458c2ecf20Sopenharmony_cistatic int object_counter;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic int __init hidma_mgmt_of_populate_channels(struct device_node *np)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct platform_device *pdev_parent = of_find_device_by_node(np);
3508c2ecf20Sopenharmony_ci	struct platform_device_info pdevinfo;
3518c2ecf20Sopenharmony_ci	struct device_node *child;
3528c2ecf20Sopenharmony_ci	struct resource *res;
3538c2ecf20Sopenharmony_ci	int ret = 0;
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	/* allocate a resource array */
3568c2ecf20Sopenharmony_ci	res = kcalloc(3, sizeof(*res), GFP_KERNEL);
3578c2ecf20Sopenharmony_ci	if (!res)
3588c2ecf20Sopenharmony_ci		return -ENOMEM;
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	for_each_available_child_of_node(np, child) {
3618c2ecf20Sopenharmony_ci		struct platform_device *new_pdev;
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci		ret = of_address_to_resource(child, 0, &res[0]);
3648c2ecf20Sopenharmony_ci		if (!ret)
3658c2ecf20Sopenharmony_ci			goto out;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		ret = of_address_to_resource(child, 1, &res[1]);
3688c2ecf20Sopenharmony_ci		if (!ret)
3698c2ecf20Sopenharmony_ci			goto out;
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci		ret = of_irq_to_resource(child, 0, &res[2]);
3728c2ecf20Sopenharmony_ci		if (ret <= 0)
3738c2ecf20Sopenharmony_ci			goto out;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci		memset(&pdevinfo, 0, sizeof(pdevinfo));
3768c2ecf20Sopenharmony_ci		pdevinfo.fwnode = &child->fwnode;
3778c2ecf20Sopenharmony_ci		pdevinfo.parent = pdev_parent ? &pdev_parent->dev : NULL;
3788c2ecf20Sopenharmony_ci		pdevinfo.name = child->name;
3798c2ecf20Sopenharmony_ci		pdevinfo.id = object_counter++;
3808c2ecf20Sopenharmony_ci		pdevinfo.res = res;
3818c2ecf20Sopenharmony_ci		pdevinfo.num_res = 3;
3828c2ecf20Sopenharmony_ci		pdevinfo.data = NULL;
3838c2ecf20Sopenharmony_ci		pdevinfo.size_data = 0;
3848c2ecf20Sopenharmony_ci		pdevinfo.dma_mask = DMA_BIT_MASK(64);
3858c2ecf20Sopenharmony_ci		new_pdev = platform_device_register_full(&pdevinfo);
3868c2ecf20Sopenharmony_ci		if (IS_ERR(new_pdev)) {
3878c2ecf20Sopenharmony_ci			ret = PTR_ERR(new_pdev);
3888c2ecf20Sopenharmony_ci			goto out;
3898c2ecf20Sopenharmony_ci		}
3908c2ecf20Sopenharmony_ci		new_pdev->dev.of_node = child;
3918c2ecf20Sopenharmony_ci		of_dma_configure(&new_pdev->dev, child, true);
3928c2ecf20Sopenharmony_ci		/*
3938c2ecf20Sopenharmony_ci		 * It is assumed that calling of_msi_configure is safe on
3948c2ecf20Sopenharmony_ci		 * platforms with or without MSI support.
3958c2ecf20Sopenharmony_ci		 */
3968c2ecf20Sopenharmony_ci		of_msi_configure(&new_pdev->dev, child);
3978c2ecf20Sopenharmony_ci	}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	kfree(res);
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	return ret;
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ciout:
4048c2ecf20Sopenharmony_ci	of_node_put(child);
4058c2ecf20Sopenharmony_ci	kfree(res);
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	return ret;
4088c2ecf20Sopenharmony_ci}
4098c2ecf20Sopenharmony_ci#endif
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_cistatic int __init hidma_mgmt_init(void)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
4148c2ecf20Sopenharmony_ci	struct device_node *child;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	for_each_matching_node(child, hidma_mgmt_match) {
4178c2ecf20Sopenharmony_ci		/* device tree based firmware here */
4188c2ecf20Sopenharmony_ci		hidma_mgmt_of_populate_channels(child);
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci#endif
4218c2ecf20Sopenharmony_ci	/*
4228c2ecf20Sopenharmony_ci	 * We do not check for return value here, as it is assumed that
4238c2ecf20Sopenharmony_ci	 * platform_driver_register must not fail. The reason for this is that
4248c2ecf20Sopenharmony_ci	 * the (potential) hidma_mgmt_of_populate_channels calls above are not
4258c2ecf20Sopenharmony_ci	 * cleaned up if it does fail, and to do this work is quite
4268c2ecf20Sopenharmony_ci	 * complicated. In particular, various calls of of_address_to_resource,
4278c2ecf20Sopenharmony_ci	 * of_irq_to_resource, platform_device_register_full, of_dma_configure,
4288c2ecf20Sopenharmony_ci	 * and of_msi_configure which then call other functions and so on, must
4298c2ecf20Sopenharmony_ci	 * be cleaned up - this is not a trivial exercise.
4308c2ecf20Sopenharmony_ci	 *
4318c2ecf20Sopenharmony_ci	 * Currently, this module is not intended to be unloaded, and there is
4328c2ecf20Sopenharmony_ci	 * no module_exit function defined which does the needed cleanup. For
4338c2ecf20Sopenharmony_ci	 * this reason, we have to assume success here.
4348c2ecf20Sopenharmony_ci	 */
4358c2ecf20Sopenharmony_ci	platform_driver_register(&hidma_mgmt_driver);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	return 0;
4388c2ecf20Sopenharmony_ci}
4398c2ecf20Sopenharmony_cimodule_init(hidma_mgmt_init);
4408c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
441