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