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