1 | // SPDX-License-Identifier: MIT |
2 | /* |
3 | * Copyright © 2022 Intel Corporation |
4 | */ |
5 | |
6 | #include <drm/drm_device.h> |
7 | #include <linux/device.h> |
8 | #include <linux/kobject.h> |
9 | #include <linux/printk.h> |
10 | #include <linux/sysfs.h> |
11 | |
12 | #include "i915_drv.h" |
13 | #include "i915_sysfs.h" |
14 | #include "intel_gt.h" |
15 | #include "intel_gt_print.h" |
16 | #include "intel_gt_sysfs.h" |
17 | #include "intel_gt_sysfs_pm.h" |
18 | #include "intel_gt_types.h" |
19 | #include "intel_rc6.h" |
20 | |
21 | bool is_object_gt(struct kobject *kobj) |
22 | { |
23 | return !strncmp(kobj->name, "gt" , 2); |
24 | } |
25 | |
26 | struct intel_gt *intel_gt_sysfs_get_drvdata(struct kobject *kobj, |
27 | const char *name) |
28 | { |
29 | /* |
30 | * We are interested at knowing from where the interface |
31 | * has been called, whether it's called from gt/ or from |
32 | * the parent directory. |
33 | * From the interface position it depends also the value of |
34 | * the private data. |
35 | * If the interface is called from gt/ then private data is |
36 | * of the "struct intel_gt *" type, otherwise it's * a |
37 | * "struct drm_i915_private *" type. |
38 | */ |
39 | if (!is_object_gt(kobj)) { |
40 | struct device *dev = kobj_to_dev(kobj); |
41 | struct drm_i915_private *i915 = kdev_minor_to_i915(kdev: dev); |
42 | |
43 | return to_gt(i915); |
44 | } |
45 | |
46 | return kobj_to_gt(kobj); |
47 | } |
48 | |
49 | static struct kobject *gt_get_parent_obj(struct intel_gt *gt) |
50 | { |
51 | return >->i915->drm.primary->kdev->kobj; |
52 | } |
53 | |
54 | static ssize_t id_show(struct kobject *kobj, |
55 | struct kobj_attribute *attr, |
56 | char *buf) |
57 | { |
58 | struct intel_gt *gt = intel_gt_sysfs_get_drvdata(kobj, name: attr->attr.name); |
59 | |
60 | return sysfs_emit(buf, fmt: "%u\n" , gt->info.id); |
61 | } |
62 | static struct kobj_attribute attr_id = __ATTR_RO(id); |
63 | |
64 | static struct attribute *id_attrs[] = { |
65 | &attr_id.attr, |
66 | NULL, |
67 | }; |
68 | ATTRIBUTE_GROUPS(id); |
69 | |
70 | /* A kobject needs a release() method even if it does nothing */ |
71 | static void kobj_gt_release(struct kobject *kobj) |
72 | { |
73 | } |
74 | |
75 | static const struct kobj_type kobj_gt_type = { |
76 | .release = kobj_gt_release, |
77 | .sysfs_ops = &kobj_sysfs_ops, |
78 | .default_groups = id_groups, |
79 | }; |
80 | |
81 | void intel_gt_sysfs_register(struct intel_gt *gt) |
82 | { |
83 | /* |
84 | * We need to make things right with the |
85 | * ABI compatibility. The files were originally |
86 | * generated under the parent directory. |
87 | * |
88 | * We generate the files only for gt 0 |
89 | * to avoid duplicates. |
90 | */ |
91 | if (gt_is_root(gt)) |
92 | intel_gt_sysfs_pm_init(gt, kobj: gt_get_parent_obj(gt)); |
93 | |
94 | /* init and xfer ownership to sysfs tree */ |
95 | if (kobject_init_and_add(kobj: >->sysfs_gt, ktype: &kobj_gt_type, |
96 | parent: gt->i915->sysfs_gt, fmt: "gt%d" , gt->info.id)) |
97 | goto exit_fail; |
98 | |
99 | gt->sysfs_defaults = kobject_create_and_add(name: ".defaults" , parent: >->sysfs_gt); |
100 | if (!gt->sysfs_defaults) |
101 | goto exit_fail; |
102 | |
103 | intel_gt_sysfs_pm_init(gt, kobj: >->sysfs_gt); |
104 | |
105 | return; |
106 | |
107 | exit_fail: |
108 | kobject_put(kobj: >->sysfs_gt); |
109 | gt_warn(gt, "failed to initialize sysfs root\n" ); |
110 | } |
111 | |
112 | void intel_gt_sysfs_unregister(struct intel_gt *gt) |
113 | { |
114 | kobject_put(kobj: gt->sysfs_defaults); |
115 | kobject_put(kobj: >->sysfs_gt); |
116 | } |
117 | |