1d62a7d41SVinod Koul // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
2d62a7d41SVinod Koul // Copyright(c) 2015-17 Intel Corporation.
3d62a7d41SVinod Koul 
4d62a7d41SVinod Koul /*
5d62a7d41SVinod Koul  * SDW Intel Init Routines
6d62a7d41SVinod Koul  *
7d62a7d41SVinod Koul  * Initializes and creates SDW devices based on ACPI and Hardware values
8d62a7d41SVinod Koul  */
9d62a7d41SVinod Koul 
10d62a7d41SVinod Koul #include <linux/acpi.h>
11d62a7d41SVinod Koul #include <linux/platform_device.h>
12d62a7d41SVinod Koul #include <linux/soundwire/sdw_intel.h>
13d62a7d41SVinod Koul #include "intel.h"
14d62a7d41SVinod Koul 
15d62a7d41SVinod Koul #define SDW_MAX_LINKS		4
16d62a7d41SVinod Koul #define SDW_SHIM_LCAP		0x0
17d62a7d41SVinod Koul #define SDW_SHIM_BASE		0x2C000
18d62a7d41SVinod Koul #define SDW_ALH_BASE		0x2C800
19d62a7d41SVinod Koul #define SDW_LINK_BASE		0x30000
20d62a7d41SVinod Koul #define SDW_LINK_SIZE		0x10000
21d62a7d41SVinod Koul 
22d62a7d41SVinod Koul struct sdw_link_data {
23d62a7d41SVinod Koul 	struct sdw_intel_link_res res;
24d62a7d41SVinod Koul 	struct platform_device *pdev;
25d62a7d41SVinod Koul };
26d62a7d41SVinod Koul 
27d62a7d41SVinod Koul struct sdw_intel_ctx {
28d62a7d41SVinod Koul 	int count;
29d62a7d41SVinod Koul 	struct sdw_link_data *links;
30d62a7d41SVinod Koul };
31d62a7d41SVinod Koul 
32d62a7d41SVinod Koul static int sdw_intel_cleanup_pdev(struct sdw_intel_ctx *ctx)
33d62a7d41SVinod Koul {
34d62a7d41SVinod Koul 	struct sdw_link_data *link = ctx->links;
35d62a7d41SVinod Koul 	int i;
36d62a7d41SVinod Koul 
37d62a7d41SVinod Koul 	if (!link)
38d62a7d41SVinod Koul 		return 0;
39d62a7d41SVinod Koul 
40d62a7d41SVinod Koul 	for (i = 0; i < ctx->count; i++) {
41d62a7d41SVinod Koul 		if (link->pdev)
42d62a7d41SVinod Koul 			platform_device_unregister(link->pdev);
43d62a7d41SVinod Koul 		link++;
44d62a7d41SVinod Koul 	}
45d62a7d41SVinod Koul 
46d62a7d41SVinod Koul 	kfree(ctx->links);
47d62a7d41SVinod Koul 	ctx->links = NULL;
48d62a7d41SVinod Koul 
49d62a7d41SVinod Koul 	return 0;
50d62a7d41SVinod Koul }
51d62a7d41SVinod Koul 
52d62a7d41SVinod Koul static struct sdw_intel_ctx
53d62a7d41SVinod Koul *sdw_intel_add_controller(struct sdw_intel_res *res)
54d62a7d41SVinod Koul {
55d62a7d41SVinod Koul 	struct platform_device_info pdevinfo;
56d62a7d41SVinod Koul 	struct platform_device *pdev;
57d62a7d41SVinod Koul 	struct sdw_link_data *link;
58d62a7d41SVinod Koul 	struct sdw_intel_ctx *ctx;
59d62a7d41SVinod Koul 	struct acpi_device *adev;
60d62a7d41SVinod Koul 	int ret, i;
61d62a7d41SVinod Koul 	u8 count;
62d62a7d41SVinod Koul 	u32 caps;
63d62a7d41SVinod Koul 
64d62a7d41SVinod Koul 	if (acpi_bus_get_device(res->handle, &adev))
65d62a7d41SVinod Koul 		return NULL;
66d62a7d41SVinod Koul 
67d62a7d41SVinod Koul 	/* Found controller, find links supported */
68d62a7d41SVinod Koul 	count = 0;
69d62a7d41SVinod Koul 	ret = fwnode_property_read_u8_array(acpi_fwnode_handle(adev),
70d62a7d41SVinod Koul 				  "mipi-sdw-master-count", &count, 1);
71d62a7d41SVinod Koul 
72d62a7d41SVinod Koul 	/* Don't fail on error, continue and use hw value */
73d62a7d41SVinod Koul 	if (ret) {
74d62a7d41SVinod Koul 		dev_err(&adev->dev,
75d62a7d41SVinod Koul 			"Failed to read mipi-sdw-master-count: %d\n", ret);
76d62a7d41SVinod Koul 		count = SDW_MAX_LINKS;
77d62a7d41SVinod Koul 	}
78d62a7d41SVinod Koul 
79d62a7d41SVinod Koul 	/* Check SNDWLCAP.LCOUNT */
80d62a7d41SVinod Koul 	caps = ioread32(res->mmio_base + SDW_SHIM_BASE + SDW_SHIM_LCAP);
81d62a7d41SVinod Koul 
82d62a7d41SVinod Koul 	/* Check HW supported vs property value and use min of two */
83d62a7d41SVinod Koul 	count = min_t(u8, caps, count);
84d62a7d41SVinod Koul 
85d62a7d41SVinod Koul 	/* Check count is within bounds */
86d62a7d41SVinod Koul 	if (count > SDW_MAX_LINKS) {
87d62a7d41SVinod Koul 		dev_err(&adev->dev, "Link count %d exceeds max %d\n",
88d62a7d41SVinod Koul 						count, SDW_MAX_LINKS);
89d62a7d41SVinod Koul 		return NULL;
90d62a7d41SVinod Koul 	}
91d62a7d41SVinod Koul 
92d62a7d41SVinod Koul 	dev_dbg(&adev->dev, "Creating %d SDW Link devices\n", count);
93d62a7d41SVinod Koul 
94d62a7d41SVinod Koul 	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
95d62a7d41SVinod Koul 	if (!ctx)
96d62a7d41SVinod Koul 		return NULL;
97d62a7d41SVinod Koul 
98d62a7d41SVinod Koul 	ctx->count = count;
99d62a7d41SVinod Koul 	ctx->links = kcalloc(ctx->count, sizeof(*ctx->links), GFP_KERNEL);
100d62a7d41SVinod Koul 	if (!ctx->links)
101d62a7d41SVinod Koul 		goto link_err;
102d62a7d41SVinod Koul 
103d62a7d41SVinod Koul 	link = ctx->links;
104d62a7d41SVinod Koul 
105d62a7d41SVinod Koul 	/* Create SDW Master devices */
106d62a7d41SVinod Koul 	for (i = 0; i < count; i++) {
107d62a7d41SVinod Koul 
108d62a7d41SVinod Koul 		link->res.irq = res->irq;
109d62a7d41SVinod Koul 		link->res.registers = res->mmio_base + SDW_LINK_BASE
110d62a7d41SVinod Koul 					+ (SDW_LINK_SIZE * i);
111d62a7d41SVinod Koul 		link->res.shim = res->mmio_base + SDW_SHIM_BASE;
112d62a7d41SVinod Koul 		link->res.alh = res->mmio_base + SDW_ALH_BASE;
113d62a7d41SVinod Koul 
114c46302ecSVinod Koul 		link->res.ops = res->ops;
115c46302ecSVinod Koul 		link->res.arg = res->arg;
116c46302ecSVinod Koul 
117d62a7d41SVinod Koul 		memset(&pdevinfo, 0, sizeof(pdevinfo));
118d62a7d41SVinod Koul 
119d62a7d41SVinod Koul 		pdevinfo.parent = res->parent;
120d62a7d41SVinod Koul 		pdevinfo.name = "int-sdw";
121d62a7d41SVinod Koul 		pdevinfo.id = i;
122d62a7d41SVinod Koul 		pdevinfo.fwnode = acpi_fwnode_handle(adev);
123d62a7d41SVinod Koul 		pdevinfo.data = &link->res;
124d62a7d41SVinod Koul 		pdevinfo.size_data = sizeof(link->res);
125d62a7d41SVinod Koul 
126d62a7d41SVinod Koul 		pdev = platform_device_register_full(&pdevinfo);
127d62a7d41SVinod Koul 		if (IS_ERR(pdev)) {
128d62a7d41SVinod Koul 			dev_err(&adev->dev,
129d62a7d41SVinod Koul 				"platform device creation failed: %ld\n",
130d62a7d41SVinod Koul 				PTR_ERR(pdev));
131d62a7d41SVinod Koul 			goto pdev_err;
132d62a7d41SVinod Koul 		}
133d62a7d41SVinod Koul 
134d62a7d41SVinod Koul 		link->pdev = pdev;
135d62a7d41SVinod Koul 		link++;
136d62a7d41SVinod Koul 	}
137d62a7d41SVinod Koul 
138d62a7d41SVinod Koul 	return ctx;
139d62a7d41SVinod Koul 
140d62a7d41SVinod Koul pdev_err:
141d62a7d41SVinod Koul 	sdw_intel_cleanup_pdev(ctx);
142d62a7d41SVinod Koul link_err:
143d62a7d41SVinod Koul 	kfree(ctx);
144d62a7d41SVinod Koul 	return NULL;
145d62a7d41SVinod Koul }
146d62a7d41SVinod Koul 
147d62a7d41SVinod Koul static acpi_status sdw_intel_acpi_cb(acpi_handle handle, u32 level,
148d62a7d41SVinod Koul 					void *cdata, void **return_value)
149d62a7d41SVinod Koul {
150d62a7d41SVinod Koul 	struct sdw_intel_res *res = cdata;
151d62a7d41SVinod Koul 	struct acpi_device *adev;
152d62a7d41SVinod Koul 
153d62a7d41SVinod Koul 	if (acpi_bus_get_device(handle, &adev)) {
154d62a7d41SVinod Koul 		dev_err(&adev->dev, "Couldn't find ACPI handle\n");
155d62a7d41SVinod Koul 		return AE_NOT_FOUND;
156d62a7d41SVinod Koul 	}
157d62a7d41SVinod Koul 
158d62a7d41SVinod Koul 	res->handle = handle;
159d62a7d41SVinod Koul 	return AE_OK;
160d62a7d41SVinod Koul }
161d62a7d41SVinod Koul 
162d62a7d41SVinod Koul /**
163d62a7d41SVinod Koul  * sdw_intel_init() - SoundWire Intel init routine
164d62a7d41SVinod Koul  * @parent_handle: ACPI parent handle
165d62a7d41SVinod Koul  * @res: resource data
166d62a7d41SVinod Koul  *
167d62a7d41SVinod Koul  * This scans the namespace and creates SoundWire link controller devices
168d62a7d41SVinod Koul  * based on the info queried.
169d62a7d41SVinod Koul  */
170d62a7d41SVinod Koul void *sdw_intel_init(acpi_handle *parent_handle, struct sdw_intel_res *res)
171d62a7d41SVinod Koul {
172d62a7d41SVinod Koul 	acpi_status status;
173d62a7d41SVinod Koul 
174d62a7d41SVinod Koul 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE,
175d62a7d41SVinod Koul 					parent_handle, 1,
176d62a7d41SVinod Koul 					sdw_intel_acpi_cb,
177d62a7d41SVinod Koul 					NULL, res, NULL);
178d62a7d41SVinod Koul 	if (ACPI_FAILURE(status))
179d62a7d41SVinod Koul 		return NULL;
180d62a7d41SVinod Koul 
181d62a7d41SVinod Koul 	return sdw_intel_add_controller(res);
182d62a7d41SVinod Koul }
183d62a7d41SVinod Koul EXPORT_SYMBOL(sdw_intel_init);
184d62a7d41SVinod Koul 
185d62a7d41SVinod Koul /**
186d62a7d41SVinod Koul  * sdw_intel_exit() - SoundWire Intel exit
187d62a7d41SVinod Koul  * @arg: callback context
188d62a7d41SVinod Koul  *
189d62a7d41SVinod Koul  * Delete the controller instances created and cleanup
190d62a7d41SVinod Koul  */
191d62a7d41SVinod Koul void sdw_intel_exit(void *arg)
192d62a7d41SVinod Koul {
193d62a7d41SVinod Koul 	struct sdw_intel_ctx *ctx = arg;
194d62a7d41SVinod Koul 
195d62a7d41SVinod Koul 	sdw_intel_cleanup_pdev(ctx);
196d62a7d41SVinod Koul 	kfree(ctx);
197d62a7d41SVinod Koul }
198d62a7d41SVinod Koul EXPORT_SYMBOL(sdw_intel_exit);
199d62a7d41SVinod Koul 
200d62a7d41SVinod Koul MODULE_LICENSE("Dual BSD/GPL");
201d62a7d41SVinod Koul MODULE_DESCRIPTION("Intel Soundwire Init Library");
202