1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35#include <linux/bitops.h>
36#include <linux/device.h>
37#include <linux/hwmon.h>
38#include <linux/hwmon-sysfs.h>
39#include <linux/i2c.h>
40#include <linux/interrupt.h>
41#include <linux/io.h>
42#include <linux/module.h>
43#include <linux/platform_data/mlxcpld-hotplug.h>
44#include <linux/platform_device.h>
45#include <linux/spinlock.h>
46#include <linux/wait.h>
47#include <linux/workqueue.h>
48
49
50#define MLXCPLD_HOTPLUG_EVENT_OFF 1
51#define MLXCPLD_HOTPLUG_MASK_OFF 2
52#define MLXCPLD_HOTPLUG_AGGR_MASK_OFF 1
53
54#define MLXCPLD_HOTPLUG_ATTRS_NUM 8
55
56
57
58
59
60
61
62enum mlxcpld_hotplug_attr_type {
63 MLXCPLD_HOTPLUG_ATTR_TYPE_PSU,
64 MLXCPLD_HOTPLUG_ATTR_TYPE_PWR,
65 MLXCPLD_HOTPLUG_ATTR_TYPE_FAN,
66};
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85struct mlxcpld_hotplug_priv_data {
86 int irq;
87 struct platform_device *pdev;
88 struct mlxcpld_hotplug_platform_data *plat;
89 struct device *hwmon;
90 struct attribute *mlxcpld_hotplug_attr[MLXCPLD_HOTPLUG_ATTRS_NUM + 1];
91 struct sensor_device_attribute_2
92 mlxcpld_hotplug_dev_attr[MLXCPLD_HOTPLUG_ATTRS_NUM];
93 struct attribute_group group;
94 const struct attribute_group *groups[2];
95 struct delayed_work dwork;
96 spinlock_t lock;
97 u8 aggr_cache;
98 u8 psu_cache;
99 u8 pwr_cache;
100 u8 fan_cache;
101};
102
103static ssize_t mlxcpld_hotplug_attr_show(struct device *dev,
104 struct device_attribute *attr,
105 char *buf)
106{
107 struct platform_device *pdev = to_platform_device(dev);
108 struct mlxcpld_hotplug_priv_data *priv = platform_get_drvdata(pdev);
109 int index = to_sensor_dev_attr_2(attr)->index;
110 int nr = to_sensor_dev_attr_2(attr)->nr;
111 u8 reg_val = 0;
112
113 switch (nr) {
114 case MLXCPLD_HOTPLUG_ATTR_TYPE_PSU:
115
116 reg_val = !!!(inb(priv->plat->psu_reg_offset) & BIT(index));
117 break;
118
119 case MLXCPLD_HOTPLUG_ATTR_TYPE_PWR:
120
121 reg_val = !!(inb(priv->plat->pwr_reg_offset) & BIT(index %
122 priv->plat->pwr_count));
123 break;
124
125 case MLXCPLD_HOTPLUG_ATTR_TYPE_FAN:
126
127 reg_val = !!!(inb(priv->plat->fan_reg_offset) & BIT(index %
128 priv->plat->fan_count));
129 break;
130 }
131
132 return sprintf(buf, "%u\n", reg_val);
133}
134
135#define PRIV_ATTR(i) priv->mlxcpld_hotplug_attr[i]
136#define PRIV_DEV_ATTR(i) priv->mlxcpld_hotplug_dev_attr[i]
137static int mlxcpld_hotplug_attr_init(struct mlxcpld_hotplug_priv_data *priv)
138{
139 int num_attrs = priv->plat->psu_count + priv->plat->pwr_count +
140 priv->plat->fan_count;
141 int i;
142
143 priv->group.attrs = devm_kzalloc(&priv->pdev->dev, num_attrs *
144 sizeof(struct attribute *),
145 GFP_KERNEL);
146 if (!priv->group.attrs)
147 return -ENOMEM;
148
149 for (i = 0; i < num_attrs; i++) {
150 PRIV_ATTR(i) = &PRIV_DEV_ATTR(i).dev_attr.attr;
151
152 if (i < priv->plat->psu_count) {
153 PRIV_ATTR(i)->name = devm_kasprintf(&priv->pdev->dev,
154 GFP_KERNEL, "psu%u", i + 1);
155 PRIV_DEV_ATTR(i).nr = MLXCPLD_HOTPLUG_ATTR_TYPE_PSU;
156 } else if (i < priv->plat->psu_count + priv->plat->pwr_count) {
157 PRIV_ATTR(i)->name = devm_kasprintf(&priv->pdev->dev,
158 GFP_KERNEL, "pwr%u", i %
159 priv->plat->pwr_count + 1);
160 PRIV_DEV_ATTR(i).nr = MLXCPLD_HOTPLUG_ATTR_TYPE_PWR;
161 } else {
162 PRIV_ATTR(i)->name = devm_kasprintf(&priv->pdev->dev,
163 GFP_KERNEL, "fan%u", i %
164 priv->plat->fan_count + 1);
165 PRIV_DEV_ATTR(i).nr = MLXCPLD_HOTPLUG_ATTR_TYPE_FAN;
166 }
167
168 if (!PRIV_ATTR(i)->name) {
169 dev_err(&priv->pdev->dev, "Memory allocation failed for sysfs attribute %d.\n",
170 i + 1);
171 return -ENOMEM;
172 }
173
174 PRIV_DEV_ATTR(i).dev_attr.attr.name = PRIV_ATTR(i)->name;
175 PRIV_DEV_ATTR(i).dev_attr.attr.mode = S_IRUGO;
176 PRIV_DEV_ATTR(i).dev_attr.show = mlxcpld_hotplug_attr_show;
177 PRIV_DEV_ATTR(i).index = i;
178 sysfs_attr_init(&PRIV_DEV_ATTR(i).dev_attr.attr);
179 }
180
181 priv->group.attrs = priv->mlxcpld_hotplug_attr;
182 priv->groups[0] = &priv->group;
183 priv->groups[1] = NULL;
184
185 return 0;
186}
187
188static int mlxcpld_hotplug_device_create(struct device *dev,
189 struct mlxcpld_hotplug_device *item)
190{
191 item->adapter = i2c_get_adapter(item->bus);
192 if (!item->adapter) {
193 dev_err(dev, "Failed to get adapter for bus %d\n",
194 item->bus);
195 return -EFAULT;
196 }
197
198 item->client = i2c_new_device(item->adapter, &item->brdinfo);
199 if (!item->client) {
200 dev_err(dev, "Failed to create client %s at bus %d at addr 0x%02x\n",
201 item->brdinfo.type, item->bus, item->brdinfo.addr);
202 i2c_put_adapter(item->adapter);
203 item->adapter = NULL;
204 return -EFAULT;
205 }
206
207 return 0;
208}
209
210static void mlxcpld_hotplug_device_destroy(struct mlxcpld_hotplug_device *item)
211{
212 if (item->client) {
213 i2c_unregister_device(item->client);
214 item->client = NULL;
215 }
216
217 if (item->adapter) {
218 i2c_put_adapter(item->adapter);
219 item->adapter = NULL;
220 }
221}
222
223static inline void
224mlxcpld_hotplug_work_helper(struct device *dev,
225 struct mlxcpld_hotplug_device *item, u8 is_inverse,
226 u16 offset, u8 mask, u8 *cache)
227{
228 u8 val, asserted;
229 int bit;
230
231
232 outb(0, offset + MLXCPLD_HOTPLUG_MASK_OFF);
233
234 val = inb(offset) & mask;
235 asserted = *cache ^ val;
236 *cache = val;
237
238
239
240
241
242
243
244
245 if (unlikely(!item)) {
246 dev_err(dev, "False signal is received: register at offset 0x%02x, mask 0x%02x.\n",
247 offset, mask);
248 return;
249 }
250
251 for_each_set_bit(bit, (unsigned long *)&asserted, 8) {
252 if (val & BIT(bit)) {
253 if (is_inverse)
254 mlxcpld_hotplug_device_destroy(item + bit);
255 else
256 mlxcpld_hotplug_device_create(dev, item + bit);
257 } else {
258 if (is_inverse)
259 mlxcpld_hotplug_device_create(dev, item + bit);
260 else
261 mlxcpld_hotplug_device_destroy(item + bit);
262 }
263 }
264
265
266 outb(0, offset + MLXCPLD_HOTPLUG_EVENT_OFF);
267
268 outb(mask, offset + MLXCPLD_HOTPLUG_MASK_OFF);
269}
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292static void mlxcpld_hotplug_work_handler(struct work_struct *work)
293{
294 struct mlxcpld_hotplug_priv_data *priv = container_of(work,
295 struct mlxcpld_hotplug_priv_data, dwork.work);
296 u8 val, aggr_asserted;
297 unsigned long flags;
298
299
300 outb(0, priv->plat->top_aggr_offset + MLXCPLD_HOTPLUG_AGGR_MASK_OFF);
301
302 val = inb(priv->plat->top_aggr_offset) & priv->plat->top_aggr_mask;
303 aggr_asserted = priv->aggr_cache ^ val;
304 priv->aggr_cache = val;
305
306
307 if (aggr_asserted & priv->plat->top_aggr_psu_mask)
308 mlxcpld_hotplug_work_helper(&priv->pdev->dev, priv->plat->psu,
309 1, priv->plat->psu_reg_offset,
310 priv->plat->psu_mask,
311 &priv->psu_cache);
312
313
314 if (aggr_asserted & priv->plat->top_aggr_pwr_mask)
315 mlxcpld_hotplug_work_helper(&priv->pdev->dev, priv->plat->pwr,
316 0, priv->plat->pwr_reg_offset,
317 priv->plat->pwr_mask,
318 &priv->pwr_cache);
319
320
321 if (aggr_asserted & priv->plat->top_aggr_fan_mask)
322 mlxcpld_hotplug_work_helper(&priv->pdev->dev, priv->plat->fan,
323 1, priv->plat->fan_reg_offset,
324 priv->plat->fan_mask,
325 &priv->fan_cache);
326
327 if (aggr_asserted) {
328 spin_lock_irqsave(&priv->lock, flags);
329
330
331
332
333
334
335
336
337
338
339
340 cancel_delayed_work(&priv->dwork);
341 schedule_delayed_work(&priv->dwork, 0);
342
343 spin_unlock_irqrestore(&priv->lock, flags);
344
345 return;
346 }
347
348
349 outb(priv->plat->top_aggr_mask, priv->plat->top_aggr_offset +
350 MLXCPLD_HOTPLUG_AGGR_MASK_OFF);
351}
352
353static void mlxcpld_hotplug_set_irq(struct mlxcpld_hotplug_priv_data *priv)
354{
355
356 outb(0, priv->plat->psu_reg_offset + MLXCPLD_HOTPLUG_EVENT_OFF);
357
358 priv->psu_cache = priv->plat->psu_mask;
359 outb(priv->plat->psu_mask, priv->plat->psu_reg_offset +
360 MLXCPLD_HOTPLUG_MASK_OFF);
361
362
363 outb(0, priv->plat->pwr_reg_offset + MLXCPLD_HOTPLUG_EVENT_OFF);
364
365 outb(priv->plat->pwr_mask, priv->plat->pwr_reg_offset +
366 MLXCPLD_HOTPLUG_MASK_OFF);
367
368
369 outb(0, priv->plat->fan_reg_offset + MLXCPLD_HOTPLUG_EVENT_OFF);
370
371 priv->fan_cache = priv->plat->fan_mask;
372 outb(priv->plat->fan_mask, priv->plat->fan_reg_offset +
373 MLXCPLD_HOTPLUG_MASK_OFF);
374
375
376 outb(priv->plat->top_aggr_mask, priv->plat->top_aggr_offset +
377 MLXCPLD_HOTPLUG_AGGR_MASK_OFF);
378
379
380 mlxcpld_hotplug_work_handler(&priv->dwork.work);
381
382 enable_irq(priv->irq);
383}
384
385static void mlxcpld_hotplug_unset_irq(struct mlxcpld_hotplug_priv_data *priv)
386{
387 int i;
388
389 disable_irq(priv->irq);
390 cancel_delayed_work_sync(&priv->dwork);
391
392
393 outb(0, priv->plat->top_aggr_offset + MLXCPLD_HOTPLUG_AGGR_MASK_OFF);
394
395
396 outb(0, priv->plat->psu_reg_offset + MLXCPLD_HOTPLUG_MASK_OFF);
397
398 outb(0, priv->plat->psu_reg_offset + MLXCPLD_HOTPLUG_EVENT_OFF);
399
400
401 outb(0, priv->plat->pwr_reg_offset + MLXCPLD_HOTPLUG_MASK_OFF);
402
403 outb(0, priv->plat->pwr_reg_offset + MLXCPLD_HOTPLUG_EVENT_OFF);
404
405
406 outb(0, priv->plat->fan_reg_offset + MLXCPLD_HOTPLUG_MASK_OFF);
407
408 outb(0, priv->plat->fan_reg_offset + MLXCPLD_HOTPLUG_EVENT_OFF);
409
410
411 for (i = 0; i < priv->plat->psu_count; i++)
412 mlxcpld_hotplug_device_destroy(priv->plat->psu + i);
413
414 for (i = 0; i < priv->plat->pwr_count; i++)
415 mlxcpld_hotplug_device_destroy(priv->plat->pwr + i);
416
417 for (i = 0; i < priv->plat->fan_count; i++)
418 mlxcpld_hotplug_device_destroy(priv->plat->fan + i);
419}
420
421static irqreturn_t mlxcpld_hotplug_irq_handler(int irq, void *dev)
422{
423 struct mlxcpld_hotplug_priv_data *priv =
424 (struct mlxcpld_hotplug_priv_data *)dev;
425
426
427 schedule_delayed_work(&priv->dwork, 0);
428
429 return IRQ_HANDLED;
430}
431
432static int mlxcpld_hotplug_probe(struct platform_device *pdev)
433{
434 struct mlxcpld_hotplug_platform_data *pdata;
435 struct mlxcpld_hotplug_priv_data *priv;
436 int err;
437
438 pdata = dev_get_platdata(&pdev->dev);
439 if (!pdata) {
440 dev_err(&pdev->dev, "Failed to get platform data.\n");
441 return -EINVAL;
442 }
443
444 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
445 if (!priv)
446 return -ENOMEM;
447
448 priv->pdev = pdev;
449 priv->plat = pdata;
450
451 priv->irq = platform_get_irq(pdev, 0);
452 if (priv->irq < 0) {
453 dev_err(&pdev->dev, "Failed to get platform irq: %d\n",
454 priv->irq);
455 return priv->irq;
456 }
457
458 err = devm_request_irq(&pdev->dev, priv->irq,
459 mlxcpld_hotplug_irq_handler, 0, pdev->name,
460 priv);
461 if (err) {
462 dev_err(&pdev->dev, "Failed to request irq: %d\n", err);
463 return err;
464 }
465 disable_irq(priv->irq);
466
467 INIT_DELAYED_WORK(&priv->dwork, mlxcpld_hotplug_work_handler);
468 spin_lock_init(&priv->lock);
469
470 err = mlxcpld_hotplug_attr_init(priv);
471 if (err) {
472 dev_err(&pdev->dev, "Failed to allocate attributes: %d\n", err);
473 return err;
474 }
475
476 priv->hwmon = devm_hwmon_device_register_with_groups(&pdev->dev,
477 "mlxcpld_hotplug", priv, priv->groups);
478 if (IS_ERR(priv->hwmon)) {
479 dev_err(&pdev->dev, "Failed to register hwmon device %ld\n",
480 PTR_ERR(priv->hwmon));
481 return PTR_ERR(priv->hwmon);
482 }
483
484 platform_set_drvdata(pdev, priv);
485
486
487 mlxcpld_hotplug_set_irq(priv);
488
489 return 0;
490}
491
492static int mlxcpld_hotplug_remove(struct platform_device *pdev)
493{
494 struct mlxcpld_hotplug_priv_data *priv = platform_get_drvdata(pdev);
495
496
497 mlxcpld_hotplug_unset_irq(priv);
498
499 return 0;
500}
501
502static struct platform_driver mlxcpld_hotplug_driver = {
503 .driver = {
504 .name = "mlxcpld-hotplug",
505 },
506 .probe = mlxcpld_hotplug_probe,
507 .remove = mlxcpld_hotplug_remove,
508};
509
510module_platform_driver(mlxcpld_hotplug_driver);
511
512MODULE_AUTHOR("Vadim Pasternak <vadimp@mellanox.com>");
513MODULE_DESCRIPTION("Mellanox CPLD hotplug platform driver");
514MODULE_LICENSE("Dual BSD/GPL");
515MODULE_ALIAS("platform:mlxcpld-hotplug");
516