Commit | Line | Data |
---|---|---|
52857e68 IW |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // Copyright (c) 2018-2021 Intel Corporation | |
3 | ||
6b8145b0 | 4 | #include <linux/bitfield.h> |
52857e68 | 5 | #include <linux/peci.h> |
93e1821c | 6 | #include <linux/peci-cpu.h> |
52857e68 IW |
7 | #include <linux/slab.h> |
8 | ||
9 | #include "internal.h" | |
10 | ||
11 | /* | |
12 | * PECI device can be removed using sysfs, but the removal can also happen as | |
13 | * a result of controller being removed. | |
14 | * Mutex is used to protect PECI device from being double-deleted. | |
15 | */ | |
16 | static DEFINE_MUTEX(peci_device_del_lock); | |
17 | ||
6b8145b0 IW |
18 | #define REVISION_NUM_MASK GENMASK(15, 8) |
19 | static int peci_get_revision(struct peci_device *device, u8 *revision) | |
20 | { | |
21 | struct peci_request *req; | |
22 | u64 dib; | |
23 | ||
24 | req = peci_xfer_get_dib(device); | |
25 | if (IS_ERR(req)) | |
26 | return PTR_ERR(req); | |
27 | ||
28 | /* | |
29 | * PECI device may be in a state where it is unable to return a proper | |
30 | * DIB, in which case it returns 0 as DIB value. | |
31 | * Let's treat this as an error to avoid carrying on with the detection | |
32 | * using invalid revision. | |
33 | */ | |
34 | dib = peci_request_dib_read(req); | |
35 | if (dib == 0) { | |
36 | peci_request_free(req); | |
37 | return -EIO; | |
38 | } | |
39 | ||
40 | *revision = FIELD_GET(REVISION_NUM_MASK, dib); | |
41 | ||
42 | peci_request_free(req); | |
43 | ||
44 | return 0; | |
45 | } | |
46 | ||
47 | static int peci_get_cpu_id(struct peci_device *device, u32 *cpu_id) | |
48 | { | |
49 | struct peci_request *req; | |
50 | int ret; | |
51 | ||
52 | req = peci_xfer_pkg_cfg_readl(device, PECI_PCS_PKG_ID, PECI_PKG_ID_CPU_ID); | |
53 | if (IS_ERR(req)) | |
54 | return PTR_ERR(req); | |
55 | ||
56 | ret = peci_request_status(req); | |
57 | if (ret) | |
58 | goto out_req_free; | |
59 | ||
60 | *cpu_id = peci_request_data_readl(req); | |
61 | out_req_free: | |
62 | peci_request_free(req); | |
63 | ||
64 | return ret; | |
65 | } | |
66 | ||
67 | static unsigned int peci_x86_cpu_family(unsigned int sig) | |
68 | { | |
69 | unsigned int x86; | |
70 | ||
71 | x86 = (sig >> 8) & 0xf; | |
72 | ||
73 | if (x86 == 0xf) | |
74 | x86 += (sig >> 20) & 0xff; | |
75 | ||
76 | return x86; | |
77 | } | |
78 | ||
79 | static unsigned int peci_x86_cpu_model(unsigned int sig) | |
80 | { | |
81 | unsigned int fam, model; | |
82 | ||
83 | fam = peci_x86_cpu_family(sig); | |
84 | ||
85 | model = (sig >> 4) & 0xf; | |
86 | ||
87 | if (fam >= 0x6) | |
88 | model += ((sig >> 16) & 0xf) << 4; | |
89 | ||
90 | return model; | |
91 | } | |
92 | ||
93 | static int peci_device_info_init(struct peci_device *device) | |
94 | { | |
95 | u8 revision; | |
96 | u32 cpu_id; | |
97 | int ret; | |
98 | ||
99 | ret = peci_get_cpu_id(device, &cpu_id); | |
100 | if (ret) | |
101 | return ret; | |
102 | ||
103 | device->info.family = peci_x86_cpu_family(cpu_id); | |
104 | device->info.model = peci_x86_cpu_model(cpu_id); | |
105 | ||
106 | ret = peci_get_revision(device, &revision); | |
107 | if (ret) | |
108 | return ret; | |
109 | device->info.peci_revision = revision; | |
110 | ||
111 | device->info.socket_id = device->addr - PECI_BASE_ADDR; | |
112 | ||
113 | return 0; | |
114 | } | |
115 | ||
52857e68 IW |
116 | static int peci_detect(struct peci_controller *controller, u8 addr) |
117 | { | |
118 | /* | |
119 | * PECI Ping is a command encoded by tx_len = 0, rx_len = 0. | |
120 | * We expect correct Write FCS if the device at the target address | |
121 | * is able to respond. | |
122 | */ | |
123 | struct peci_request req = { 0 }; | |
124 | int ret; | |
125 | ||
126 | mutex_lock(&controller->bus_lock); | |
127 | ret = controller->ops->xfer(controller, addr, &req); | |
128 | mutex_unlock(&controller->bus_lock); | |
129 | ||
130 | return ret; | |
131 | } | |
132 | ||
133 | static bool peci_addr_valid(u8 addr) | |
134 | { | |
135 | return addr >= PECI_BASE_ADDR && addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX; | |
136 | } | |
137 | ||
138 | static int peci_dev_exists(struct device *dev, void *data) | |
139 | { | |
140 | struct peci_device *device = to_peci_device(dev); | |
141 | u8 *addr = data; | |
142 | ||
143 | if (device->addr == *addr) | |
144 | return -EBUSY; | |
145 | ||
146 | return 0; | |
147 | } | |
148 | ||
149 | int peci_device_create(struct peci_controller *controller, u8 addr) | |
150 | { | |
151 | struct peci_device *device; | |
152 | int ret; | |
153 | ||
154 | if (!peci_addr_valid(addr)) | |
155 | return -EINVAL; | |
156 | ||
157 | /* Check if we have already detected this device before. */ | |
158 | ret = device_for_each_child(&controller->dev, &addr, peci_dev_exists); | |
159 | if (ret) | |
160 | return 0; | |
161 | ||
162 | ret = peci_detect(controller, addr); | |
163 | if (ret) { | |
164 | /* | |
165 | * Device not present or host state doesn't allow successful | |
166 | * detection at this time. | |
167 | */ | |
168 | if (ret == -EIO || ret == -ETIMEDOUT) | |
169 | return 0; | |
170 | ||
171 | return ret; | |
172 | } | |
173 | ||
174 | device = kzalloc(sizeof(*device), GFP_KERNEL); | |
175 | if (!device) | |
176 | return -ENOMEM; | |
177 | ||
178 | device_initialize(&device->dev); | |
179 | ||
180 | device->addr = addr; | |
181 | device->dev.parent = &controller->dev; | |
182 | device->dev.bus = &peci_bus_type; | |
183 | device->dev.type = &peci_device_type; | |
184 | ||
6b8145b0 IW |
185 | ret = peci_device_info_init(device); |
186 | if (ret) | |
187 | goto err_put; | |
188 | ||
52857e68 IW |
189 | ret = dev_set_name(&device->dev, "%d-%02x", controller->id, device->addr); |
190 | if (ret) | |
191 | goto err_put; | |
192 | ||
193 | ret = device_add(&device->dev); | |
194 | if (ret) | |
195 | goto err_put; | |
196 | ||
197 | return 0; | |
198 | ||
199 | err_put: | |
200 | put_device(&device->dev); | |
201 | ||
202 | return ret; | |
203 | } | |
204 | ||
205 | void peci_device_destroy(struct peci_device *device) | |
206 | { | |
207 | mutex_lock(&peci_device_del_lock); | |
208 | if (!device->deleted) { | |
209 | device_unregister(&device->dev); | |
210 | device->deleted = true; | |
211 | } | |
212 | mutex_unlock(&peci_device_del_lock); | |
213 | } | |
214 | ||
6b8145b0 IW |
215 | int __peci_driver_register(struct peci_driver *driver, struct module *owner, |
216 | const char *mod_name) | |
217 | { | |
218 | driver->driver.bus = &peci_bus_type; | |
219 | driver->driver.owner = owner; | |
220 | driver->driver.mod_name = mod_name; | |
221 | ||
222 | if (!driver->probe) { | |
223 | pr_err("peci: trying to register driver without probe callback\n"); | |
224 | return -EINVAL; | |
225 | } | |
226 | ||
227 | if (!driver->id_table) { | |
228 | pr_err("peci: trying to register driver without device id table\n"); | |
229 | return -EINVAL; | |
230 | } | |
231 | ||
232 | return driver_register(&driver->driver); | |
233 | } | |
234 | EXPORT_SYMBOL_NS_GPL(__peci_driver_register, PECI); | |
235 | ||
236 | void peci_driver_unregister(struct peci_driver *driver) | |
237 | { | |
238 | driver_unregister(&driver->driver); | |
239 | } | |
240 | EXPORT_SYMBOL_NS_GPL(peci_driver_unregister, PECI); | |
241 | ||
52857e68 IW |
242 | static void peci_device_release(struct device *dev) |
243 | { | |
244 | struct peci_device *device = to_peci_device(dev); | |
245 | ||
246 | kfree(device); | |
247 | } | |
248 | ||
249 | struct device_type peci_device_type = { | |
42bed52b | 250 | .groups = peci_device_groups, |
52857e68 IW |
251 | .release = peci_device_release, |
252 | }; |