|
7 | 7 | #include <linux/errno.h>
|
8 | 8 | #include <linux/slab.h>
|
9 | 9 | #include <linux/init.h>
|
| 10 | +#include <linux/idr.h> |
10 | 11 | #include <linux/slimbus.h>
|
| 12 | +#include "slimbus.h" |
| 13 | + |
| 14 | +static DEFINE_IDA(ctrl_ida); |
11 | 15 |
|
12 | 16 | static const struct slim_device_id *slim_match(const struct slim_device_id *id,
|
13 | 17 | const struct slim_device *sbdev)
|
@@ -92,6 +96,308 @@ void slim_driver_unregister(struct slim_driver *drv)
|
92 | 96 | }
|
93 | 97 | EXPORT_SYMBOL_GPL(slim_driver_unregister);
|
94 | 98 |
|
| 99 | +static void slim_dev_release(struct device *dev) |
| 100 | +{ |
| 101 | + struct slim_device *sbdev = to_slim_device(dev); |
| 102 | + |
| 103 | + kfree(sbdev); |
| 104 | +} |
| 105 | + |
| 106 | +static int slim_add_device(struct slim_controller *ctrl, |
| 107 | + struct slim_device *sbdev, |
| 108 | + struct device_node *node) |
| 109 | +{ |
| 110 | + sbdev->dev.bus = &slimbus_bus; |
| 111 | + sbdev->dev.parent = ctrl->dev; |
| 112 | + sbdev->dev.release = slim_dev_release; |
| 113 | + sbdev->dev.driver = NULL; |
| 114 | + sbdev->ctrl = ctrl; |
| 115 | + |
| 116 | + dev_set_name(&sbdev->dev, "%x:%x:%x:%x", |
| 117 | + sbdev->e_addr.manf_id, |
| 118 | + sbdev->e_addr.prod_code, |
| 119 | + sbdev->e_addr.dev_index, |
| 120 | + sbdev->e_addr.instance); |
| 121 | + |
| 122 | + return device_register(&sbdev->dev); |
| 123 | +} |
| 124 | + |
| 125 | +static struct slim_device *slim_alloc_device(struct slim_controller *ctrl, |
| 126 | + struct slim_eaddr *eaddr, |
| 127 | + struct device_node *node) |
| 128 | +{ |
| 129 | + struct slim_device *sbdev; |
| 130 | + int ret; |
| 131 | + |
| 132 | + sbdev = kzalloc(sizeof(*sbdev), GFP_KERNEL); |
| 133 | + if (!sbdev) |
| 134 | + return NULL; |
| 135 | + |
| 136 | + sbdev->e_addr = *eaddr; |
| 137 | + ret = slim_add_device(ctrl, sbdev, node); |
| 138 | + if (ret) { |
| 139 | + kfree(sbdev); |
| 140 | + return NULL; |
| 141 | + } |
| 142 | + |
| 143 | + return sbdev; |
| 144 | +} |
| 145 | + |
| 146 | +/* |
| 147 | + * slim_register_controller() - Controller bring-up and registration. |
| 148 | + * |
| 149 | + * @ctrl: Controller to be registered. |
| 150 | + * |
| 151 | + * A controller is registered with the framework using this API. |
| 152 | + * If devices on a controller were registered before controller, |
| 153 | + * this will make sure that they get probed when controller is up |
| 154 | + */ |
| 155 | +int slim_register_controller(struct slim_controller *ctrl) |
| 156 | +{ |
| 157 | + int id; |
| 158 | + |
| 159 | + id = ida_simple_get(&ctrl_ida, 0, 0, GFP_KERNEL); |
| 160 | + if (id < 0) |
| 161 | + return id; |
| 162 | + |
| 163 | + ctrl->id = id; |
| 164 | + |
| 165 | + if (!ctrl->min_cg) |
| 166 | + ctrl->min_cg = SLIM_MIN_CLK_GEAR; |
| 167 | + if (!ctrl->max_cg) |
| 168 | + ctrl->max_cg = SLIM_MAX_CLK_GEAR; |
| 169 | + |
| 170 | + ida_init(&ctrl->laddr_ida); |
| 171 | + idr_init(&ctrl->tid_idr); |
| 172 | + mutex_init(&ctrl->lock); |
| 173 | + |
| 174 | + dev_dbg(ctrl->dev, "Bus [%s] registered:dev:%p\n", |
| 175 | + ctrl->name, ctrl->dev); |
| 176 | + |
| 177 | + return 0; |
| 178 | +} |
| 179 | +EXPORT_SYMBOL_GPL(slim_register_controller); |
| 180 | + |
| 181 | +/* slim_remove_device: Remove the effect of slim_add_device() */ |
| 182 | +static void slim_remove_device(struct slim_device *sbdev) |
| 183 | +{ |
| 184 | + device_unregister(&sbdev->dev); |
| 185 | +} |
| 186 | + |
| 187 | +static int slim_ctrl_remove_device(struct device *dev, void *null) |
| 188 | +{ |
| 189 | + slim_remove_device(to_slim_device(dev)); |
| 190 | + return 0; |
| 191 | +} |
| 192 | + |
| 193 | +/** |
| 194 | + * slim_unregister_controller() - Controller tear-down. |
| 195 | + * |
| 196 | + * @ctrl: Controller to tear-down. |
| 197 | + */ |
| 198 | +int slim_unregister_controller(struct slim_controller *ctrl) |
| 199 | +{ |
| 200 | + /* Remove all clients */ |
| 201 | + device_for_each_child(ctrl->dev, NULL, slim_ctrl_remove_device); |
| 202 | + ida_simple_remove(&ctrl_ida, ctrl->id); |
| 203 | + |
| 204 | + return 0; |
| 205 | +} |
| 206 | +EXPORT_SYMBOL_GPL(slim_unregister_controller); |
| 207 | + |
| 208 | +static void slim_device_update_status(struct slim_device *sbdev, |
| 209 | + enum slim_device_status status) |
| 210 | +{ |
| 211 | + struct slim_driver *sbdrv; |
| 212 | + |
| 213 | + if (sbdev->status == status) |
| 214 | + return; |
| 215 | + |
| 216 | + sbdev->status = status; |
| 217 | + if (!sbdev->dev.driver) |
| 218 | + return; |
| 219 | + |
| 220 | + sbdrv = to_slim_driver(sbdev->dev.driver); |
| 221 | + if (sbdrv->device_status) |
| 222 | + sbdrv->device_status(sbdev, sbdev->status); |
| 223 | +} |
| 224 | + |
| 225 | +/** |
| 226 | + * slim_report_absent() - Controller calls this function when a device |
| 227 | + * reports absent, OR when the device cannot be communicated with |
| 228 | + * |
| 229 | + * @sbdev: Device that cannot be reached, or sent report absent |
| 230 | + */ |
| 231 | +void slim_report_absent(struct slim_device *sbdev) |
| 232 | +{ |
| 233 | + struct slim_controller *ctrl = sbdev->ctrl; |
| 234 | + |
| 235 | + if (!ctrl) |
| 236 | + return; |
| 237 | + |
| 238 | + /* invalidate logical addresses */ |
| 239 | + mutex_lock(&ctrl->lock); |
| 240 | + sbdev->is_laddr_valid = false; |
| 241 | + mutex_unlock(&ctrl->lock); |
| 242 | + |
| 243 | + ida_simple_remove(&ctrl->laddr_ida, sbdev->laddr); |
| 244 | + slim_device_update_status(sbdev, SLIM_DEVICE_STATUS_DOWN); |
| 245 | +} |
| 246 | +EXPORT_SYMBOL_GPL(slim_report_absent); |
| 247 | + |
| 248 | +static bool slim_eaddr_equal(struct slim_eaddr *a, struct slim_eaddr *b) |
| 249 | +{ |
| 250 | + return (a->manf_id == b->manf_id && |
| 251 | + a->prod_code == b->prod_code && |
| 252 | + a->dev_index == b->dev_index && |
| 253 | + a->instance == b->instance); |
| 254 | +} |
| 255 | + |
| 256 | +static int slim_match_dev(struct device *dev, void *data) |
| 257 | +{ |
| 258 | + struct slim_eaddr *e_addr = data; |
| 259 | + struct slim_device *sbdev = to_slim_device(dev); |
| 260 | + |
| 261 | + return slim_eaddr_equal(&sbdev->e_addr, e_addr); |
| 262 | +} |
| 263 | + |
| 264 | +static struct slim_device *find_slim_device(struct slim_controller *ctrl, |
| 265 | + struct slim_eaddr *eaddr) |
| 266 | +{ |
| 267 | + struct slim_device *sbdev; |
| 268 | + struct device *dev; |
| 269 | + |
| 270 | + dev = device_find_child(ctrl->dev, eaddr, slim_match_dev); |
| 271 | + if (dev) { |
| 272 | + sbdev = to_slim_device(dev); |
| 273 | + return sbdev; |
| 274 | + } |
| 275 | + |
| 276 | + return NULL; |
| 277 | +} |
| 278 | + |
| 279 | +/** |
| 280 | + * slim_get_device() - get handle to a device. |
| 281 | + * |
| 282 | + * @ctrl: Controller on which this device will be added/queried |
| 283 | + * @e_addr: Enumeration address of the device to be queried |
| 284 | + * |
| 285 | + * Return: pointer to a device if it has already reported. Creates a new |
| 286 | + * device and returns pointer to it if the device has not yet enumerated. |
| 287 | + */ |
| 288 | +struct slim_device *slim_get_device(struct slim_controller *ctrl, |
| 289 | + struct slim_eaddr *e_addr) |
| 290 | +{ |
| 291 | + struct slim_device *sbdev; |
| 292 | + |
| 293 | + sbdev = find_slim_device(ctrl, e_addr); |
| 294 | + if (!sbdev) { |
| 295 | + sbdev = slim_alloc_device(ctrl, e_addr, NULL); |
| 296 | + if (!sbdev) |
| 297 | + return ERR_PTR(-ENOMEM); |
| 298 | + } |
| 299 | + |
| 300 | + return sbdev; |
| 301 | +} |
| 302 | +EXPORT_SYMBOL_GPL(slim_get_device); |
| 303 | + |
| 304 | +static int slim_device_alloc_laddr(struct slim_device *sbdev, |
| 305 | + bool report_present) |
| 306 | +{ |
| 307 | + struct slim_controller *ctrl = sbdev->ctrl; |
| 308 | + u8 laddr; |
| 309 | + int ret; |
| 310 | + |
| 311 | + mutex_lock(&ctrl->lock); |
| 312 | + if (ctrl->get_laddr) { |
| 313 | + ret = ctrl->get_laddr(ctrl, &sbdev->e_addr, &laddr); |
| 314 | + if (ret < 0) |
| 315 | + goto err; |
| 316 | + } else if (report_present) { |
| 317 | + ret = ida_simple_get(&ctrl->laddr_ida, |
| 318 | + 0, SLIM_LA_MANAGER - 1, GFP_KERNEL); |
| 319 | + if (ret < 0) |
| 320 | + goto err; |
| 321 | + |
| 322 | + laddr = ret; |
| 323 | + } else { |
| 324 | + ret = -EINVAL; |
| 325 | + goto err; |
| 326 | + } |
| 327 | + |
| 328 | + if (ctrl->set_laddr) { |
| 329 | + ret = ctrl->set_laddr(ctrl, &sbdev->e_addr, laddr); |
| 330 | + if (ret) { |
| 331 | + ret = -EINVAL; |
| 332 | + goto err; |
| 333 | + } |
| 334 | + } |
| 335 | + |
| 336 | + sbdev->laddr = laddr; |
| 337 | + sbdev->is_laddr_valid = true; |
| 338 | + |
| 339 | + slim_device_update_status(sbdev, SLIM_DEVICE_STATUS_UP); |
| 340 | + |
| 341 | + dev_dbg(ctrl->dev, "setting slimbus l-addr:%x, ea:%x,%x,%x,%x\n", |
| 342 | + laddr, sbdev->e_addr.manf_id, sbdev->e_addr.prod_code, |
| 343 | + sbdev->e_addr.dev_index, sbdev->e_addr.instance); |
| 344 | + |
| 345 | +err: |
| 346 | + mutex_unlock(&ctrl->lock); |
| 347 | + return ret; |
| 348 | + |
| 349 | +} |
| 350 | + |
| 351 | +/** |
| 352 | + * slim_device_report_present() - Report enumerated device. |
| 353 | + * |
| 354 | + * @ctrl: Controller with which device is enumerated. |
| 355 | + * @e_addr: Enumeration address of the device. |
| 356 | + * @laddr: Return logical address (if valid flag is false) |
| 357 | + * |
| 358 | + * Called by controller in response to REPORT_PRESENT. Framework will assign |
| 359 | + * a logical address to this enumeration address. |
| 360 | + * Function returns -EXFULL to indicate that all logical addresses are already |
| 361 | + * taken. |
| 362 | + */ |
| 363 | +int slim_device_report_present(struct slim_controller *ctrl, |
| 364 | + struct slim_eaddr *e_addr, u8 *laddr) |
| 365 | +{ |
| 366 | + struct slim_device *sbdev; |
| 367 | + int ret; |
| 368 | + |
| 369 | + sbdev = slim_get_device(ctrl, e_addr); |
| 370 | + if (IS_ERR(sbdev)) |
| 371 | + return -ENODEV; |
| 372 | + |
| 373 | + if (sbdev->is_laddr_valid) { |
| 374 | + *laddr = sbdev->laddr; |
| 375 | + return 0; |
| 376 | + } |
| 377 | + |
| 378 | + ret = slim_device_alloc_laddr(sbdev, true); |
| 379 | + |
| 380 | + return ret; |
| 381 | +} |
| 382 | +EXPORT_SYMBOL_GPL(slim_device_report_present); |
| 383 | + |
| 384 | +/** |
| 385 | + * slim_get_logical_addr() - get/allocate logical address of a SLIMbus device. |
| 386 | + * |
| 387 | + * @sbdev: client handle requesting the address. |
| 388 | + * |
| 389 | + * Return: zero if a logical address is valid or a new logical address |
| 390 | + * has been assigned. error code in case of error. |
| 391 | + */ |
| 392 | +int slim_get_logical_addr(struct slim_device *sbdev) |
| 393 | +{ |
| 394 | + if (!sbdev->is_laddr_valid) |
| 395 | + return slim_device_alloc_laddr(sbdev, false); |
| 396 | + |
| 397 | + return 0; |
| 398 | +} |
| 399 | +EXPORT_SYMBOL_GPL(slim_get_logical_addr); |
| 400 | + |
95 | 401 | static void __exit slimbus_exit(void)
|
96 | 402 | {
|
97 | 403 | bus_unregister(&slimbus_bus);
|
|
0 commit comments