Commit | Line | Data |
---|---|---|
477dfdcc XY |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * DFL device driver for EMIF private feature | |
4 | * | |
5 | * Copyright (C) 2020 Intel Corporation, Inc. | |
6 | * | |
7 | */ | |
8 | #include <linux/bitfield.h> | |
9 | #include <linux/dfl.h> | |
10 | #include <linux/errno.h> | |
11 | #include <linux/io.h> | |
12 | #include <linux/iopoll.h> | |
13 | #include <linux/io-64-nonatomic-lo-hi.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/spinlock.h> | |
17 | #include <linux/types.h> | |
18 | ||
19 | #define FME_FEATURE_ID_EMIF 0x9 | |
20 | ||
21 | #define EMIF_STAT 0x8 | |
22 | #define EMIF_STAT_INIT_DONE_SFT 0 | |
23 | #define EMIF_STAT_CALC_FAIL_SFT 8 | |
24 | #define EMIF_STAT_CLEAR_BUSY_SFT 16 | |
25 | #define EMIF_CTRL 0x10 | |
26 | #define EMIF_CTRL_CLEAR_EN_SFT 0 | |
27 | #define EMIF_CTRL_CLEAR_EN_MSK GENMASK_ULL(3, 0) | |
28 | ||
29 | #define EMIF_POLL_INVL 10000 /* us */ | |
30 | #define EMIF_POLL_TIMEOUT 5000000 /* us */ | |
31 | ||
32 | struct dfl_emif { | |
33 | struct device *dev; | |
34 | void __iomem *base; | |
35 | spinlock_t lock; /* Serialises access to EMIF_CTRL reg */ | |
36 | }; | |
37 | ||
38 | struct emif_attr { | |
39 | struct device_attribute attr; | |
40 | u32 shift; | |
41 | u32 index; | |
42 | }; | |
43 | ||
44 | #define to_emif_attr(dev_attr) \ | |
45 | container_of(dev_attr, struct emif_attr, attr) | |
46 | ||
47 | static ssize_t emif_state_show(struct device *dev, | |
48 | struct device_attribute *attr, char *buf) | |
49 | { | |
50 | struct emif_attr *eattr = to_emif_attr(attr); | |
51 | struct dfl_emif *de = dev_get_drvdata(dev); | |
52 | u64 val; | |
53 | ||
54 | val = readq(de->base + EMIF_STAT); | |
55 | ||
56 | return sysfs_emit(buf, "%u\n", | |
57 | !!(val & BIT_ULL(eattr->shift + eattr->index))); | |
58 | } | |
59 | ||
60 | static ssize_t emif_clear_store(struct device *dev, | |
61 | struct device_attribute *attr, | |
62 | const char *buf, size_t count) | |
63 | { | |
64 | struct emif_attr *eattr = to_emif_attr(attr); | |
65 | struct dfl_emif *de = dev_get_drvdata(dev); | |
66 | u64 clear_busy_msk, clear_en_msk, val; | |
67 | void __iomem *base = de->base; | |
68 | ||
69 | if (!sysfs_streq(buf, "1")) | |
70 | return -EINVAL; | |
71 | ||
72 | clear_busy_msk = BIT_ULL(EMIF_STAT_CLEAR_BUSY_SFT + eattr->index); | |
73 | clear_en_msk = BIT_ULL(EMIF_CTRL_CLEAR_EN_SFT + eattr->index); | |
74 | ||
75 | spin_lock(&de->lock); | |
76 | /* The CLEAR_EN field is WO, but other fields are RW */ | |
77 | val = readq(base + EMIF_CTRL); | |
78 | val &= ~EMIF_CTRL_CLEAR_EN_MSK; | |
79 | val |= clear_en_msk; | |
80 | writeq(val, base + EMIF_CTRL); | |
81 | spin_unlock(&de->lock); | |
82 | ||
83 | if (readq_poll_timeout(base + EMIF_STAT, val, | |
84 | !(val & clear_busy_msk), | |
85 | EMIF_POLL_INVL, EMIF_POLL_TIMEOUT)) { | |
86 | dev_err(de->dev, "timeout, fail to clear\n"); | |
87 | return -ETIMEDOUT; | |
88 | } | |
89 | ||
90 | return count; | |
91 | } | |
92 | ||
93 | #define emif_state_attr(_name, _shift, _index) \ | |
94 | static struct emif_attr emif_attr_##inf##_index##_##_name = \ | |
95 | { .attr = __ATTR(inf##_index##_##_name, 0444, \ | |
96 | emif_state_show, NULL), \ | |
97 | .shift = (_shift), .index = (_index) } | |
98 | ||
99 | #define emif_clear_attr(_index) \ | |
100 | static struct emif_attr emif_attr_##inf##_index##_clear = \ | |
101 | { .attr = __ATTR(inf##_index##_clear, 0200, \ | |
102 | NULL, emif_clear_store), \ | |
103 | .index = (_index) } | |
104 | ||
105 | emif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 0); | |
106 | emif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 1); | |
107 | emif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 2); | |
108 | emif_state_attr(init_done, EMIF_STAT_INIT_DONE_SFT, 3); | |
109 | ||
110 | emif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 0); | |
111 | emif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 1); | |
112 | emif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 2); | |
113 | emif_state_attr(cal_fail, EMIF_STAT_CALC_FAIL_SFT, 3); | |
114 | ||
115 | emif_clear_attr(0); | |
116 | emif_clear_attr(1); | |
117 | emif_clear_attr(2); | |
118 | emif_clear_attr(3); | |
119 | ||
120 | static struct attribute *dfl_emif_attrs[] = { | |
121 | &emif_attr_inf0_init_done.attr.attr, | |
122 | &emif_attr_inf0_cal_fail.attr.attr, | |
123 | &emif_attr_inf0_clear.attr.attr, | |
124 | ||
125 | &emif_attr_inf1_init_done.attr.attr, | |
126 | &emif_attr_inf1_cal_fail.attr.attr, | |
127 | &emif_attr_inf1_clear.attr.attr, | |
128 | ||
129 | &emif_attr_inf2_init_done.attr.attr, | |
130 | &emif_attr_inf2_cal_fail.attr.attr, | |
131 | &emif_attr_inf2_clear.attr.attr, | |
132 | ||
133 | &emif_attr_inf3_init_done.attr.attr, | |
134 | &emif_attr_inf3_cal_fail.attr.attr, | |
135 | &emif_attr_inf3_clear.attr.attr, | |
136 | ||
137 | NULL, | |
138 | }; | |
139 | ||
140 | static umode_t dfl_emif_visible(struct kobject *kobj, | |
141 | struct attribute *attr, int n) | |
142 | { | |
143 | struct dfl_emif *de = dev_get_drvdata(kobj_to_dev(kobj)); | |
144 | struct emif_attr *eattr = container_of(attr, struct emif_attr, | |
145 | attr.attr); | |
146 | u64 val; | |
147 | ||
148 | /* | |
149 | * This device supports upto 4 memory interfaces, but not all | |
150 | * interfaces are used on different platforms. The read out value of | |
151 | * CLEAN_EN field (which is a bitmap) could tell how many interfaces | |
152 | * are available. | |
153 | */ | |
154 | val = FIELD_GET(EMIF_CTRL_CLEAR_EN_MSK, readq(de->base + EMIF_CTRL)); | |
155 | ||
156 | return (val & BIT_ULL(eattr->index)) ? attr->mode : 0; | |
157 | } | |
158 | ||
159 | static const struct attribute_group dfl_emif_group = { | |
160 | .is_visible = dfl_emif_visible, | |
161 | .attrs = dfl_emif_attrs, | |
162 | }; | |
163 | ||
164 | static const struct attribute_group *dfl_emif_groups[] = { | |
165 | &dfl_emif_group, | |
166 | NULL, | |
167 | }; | |
168 | ||
169 | static int dfl_emif_probe(struct dfl_device *ddev) | |
170 | { | |
171 | struct device *dev = &ddev->dev; | |
172 | struct dfl_emif *de; | |
173 | ||
174 | de = devm_kzalloc(dev, sizeof(*de), GFP_KERNEL); | |
175 | if (!de) | |
176 | return -ENOMEM; | |
177 | ||
178 | de->base = devm_ioremap_resource(dev, &ddev->mmio_res); | |
179 | if (IS_ERR(de->base)) | |
180 | return PTR_ERR(de->base); | |
181 | ||
182 | de->dev = dev; | |
183 | spin_lock_init(&de->lock); | |
184 | dev_set_drvdata(dev, de); | |
185 | ||
186 | return 0; | |
187 | } | |
188 | ||
189 | static const struct dfl_device_id dfl_emif_ids[] = { | |
190 | { FME_ID, FME_FEATURE_ID_EMIF }, | |
191 | { } | |
192 | }; | |
193 | MODULE_DEVICE_TABLE(dfl, dfl_emif_ids); | |
194 | ||
195 | static struct dfl_driver dfl_emif_driver = { | |
196 | .drv = { | |
197 | .name = "dfl-emif", | |
198 | .dev_groups = dfl_emif_groups, | |
199 | }, | |
200 | .id_table = dfl_emif_ids, | |
201 | .probe = dfl_emif_probe, | |
202 | }; | |
203 | module_dfl_driver(dfl_emif_driver); | |
204 | ||
205 | MODULE_DESCRIPTION("DFL EMIF driver"); | |
206 | MODULE_AUTHOR("Intel Corporation"); | |
207 | MODULE_LICENSE("GPL v2"); |