OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [hwmon/] [i5k_amb.c] - Blame information for rev 62

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 62 marcus.erl
/*
2
 * A hwmon driver for the Intel 5000 series chipset FB-DIMM AMB
3
 * temperature sensors
4
 * Copyright (C) 2007 IBM
5
 *
6
 * Author: Darrick J. Wong <djwong@us.ibm.com>
7
 *
8
 * This program is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 2 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * This program is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
 */
22
 
23
#include <linux/module.h>
24
#include <linux/jiffies.h>
25
#include <linux/hwmon.h>
26
#include <linux/hwmon-sysfs.h>
27
#include <linux/err.h>
28
#include <linux/mutex.h>
29
#include <linux/delay.h>
30
#include <linux/log2.h>
31
#include <linux/pci.h>
32
#include <linux/platform_device.h>
33
 
34
#define DRVNAME "i5k_amb"
35
 
36
#define I5K_REG_AMB_BASE_ADDR           0x48
37
#define I5K_REG_AMB_LEN_ADDR            0x50
38
#define I5K_REG_CHAN0_PRESENCE_ADDR     0x64
39
#define I5K_REG_CHAN1_PRESENCE_ADDR     0x66
40
 
41
#define AMB_REG_TEMP_MIN_ADDR           0x80
42
#define AMB_REG_TEMP_MID_ADDR           0x81
43
#define AMB_REG_TEMP_MAX_ADDR           0x82
44
#define AMB_REG_TEMP_STATUS_ADDR        0x84
45
#define AMB_REG_TEMP_ADDR               0x85
46
 
47
#define AMB_CONFIG_SIZE                 2048
48
#define AMB_FUNC_3_OFFSET               768
49
 
50
static unsigned long amb_reg_temp_status(unsigned int amb)
51
{
52
        return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_STATUS_ADDR +
53
               AMB_CONFIG_SIZE * amb;
54
}
55
 
56
static unsigned long amb_reg_temp_min(unsigned int amb)
57
{
58
        return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MIN_ADDR +
59
               AMB_CONFIG_SIZE * amb;
60
}
61
 
62
static unsigned long amb_reg_temp_mid(unsigned int amb)
63
{
64
        return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MID_ADDR +
65
               AMB_CONFIG_SIZE * amb;
66
}
67
 
68
static unsigned long amb_reg_temp_max(unsigned int amb)
69
{
70
        return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MAX_ADDR +
71
               AMB_CONFIG_SIZE * amb;
72
}
73
 
74
static unsigned long amb_reg_temp(unsigned int amb)
75
{
76
        return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_ADDR +
77
               AMB_CONFIG_SIZE * amb;
78
}
79
 
80
#define MAX_MEM_CHANNELS                4
81
#define MAX_AMBS_PER_CHANNEL            16
82
#define MAX_AMBS                        (MAX_MEM_CHANNELS * \
83
                                         MAX_AMBS_PER_CHANNEL)
84
/*
85
 * Ugly hack: For some reason the highest bit is set if there
86
 * are _any_ DIMMs in the channel.  Attempting to read from
87
 * this "high-order" AMB results in a memory bus error, so
88
 * for now we'll just ignore that top bit, even though that
89
 * might prevent us from seeing the 16th DIMM in the channel.
90
 */
91
#define REAL_MAX_AMBS_PER_CHANNEL       15
92
#define KNOBS_PER_AMB                   5
93
 
94
static unsigned long amb_num_from_reg(unsigned int byte_num, unsigned int bit)
95
{
96
        return byte_num * MAX_AMBS_PER_CHANNEL + bit;
97
}
98
 
99
#define AMB_SYSFS_NAME_LEN              16
100
struct i5k_device_attribute {
101
        struct sensor_device_attribute s_attr;
102
        char name[AMB_SYSFS_NAME_LEN];
103
};
104
 
105
struct i5k_amb_data {
106
        struct device *hwmon_dev;
107
 
108
        unsigned long amb_base;
109
        unsigned long amb_len;
110
        u16 amb_present[MAX_MEM_CHANNELS];
111
        void __iomem *amb_mmio;
112
        struct i5k_device_attribute *attrs;
113
        unsigned int num_attrs;
114
};
115
 
116
static ssize_t show_name(struct device *dev, struct device_attribute *devattr,
117
                         char *buf)
118
{
119
        return sprintf(buf, "%s\n", DRVNAME);
120
}
121
 
122
 
123
static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
124
 
125
static struct platform_device *amb_pdev;
126
 
127
static u8 amb_read_byte(struct i5k_amb_data *data, unsigned long offset)
128
{
129
        return ioread8(data->amb_mmio + offset);
130
}
131
 
132
static void amb_write_byte(struct i5k_amb_data *data, unsigned long offset,
133
                           u8 val)
134
{
135
        iowrite8(val, data->amb_mmio + offset);
136
}
137
 
138
static ssize_t show_amb_alarm(struct device *dev,
139
                             struct device_attribute *devattr,
140
                             char *buf)
141
{
142
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
143
        struct i5k_amb_data *data = dev_get_drvdata(dev);
144
 
145
        if (!(amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x20) &&
146
             (amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x8))
147
                return sprintf(buf, "1\n");
148
        else
149
                return sprintf(buf, "0\n");
150
}
151
 
152
static ssize_t store_amb_min(struct device *dev,
153
                             struct device_attribute *devattr,
154
                             const char *buf,
155
                             size_t count)
156
{
157
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
158
        struct i5k_amb_data *data = dev_get_drvdata(dev);
159
        unsigned long temp = simple_strtoul(buf, NULL, 10) / 500;
160
 
161
        if (temp > 255)
162
                temp = 255;
163
 
164
        amb_write_byte(data, amb_reg_temp_min(attr->index), temp);
165
        return count;
166
}
167
 
168
static ssize_t store_amb_mid(struct device *dev,
169
                             struct device_attribute *devattr,
170
                             const char *buf,
171
                             size_t count)
172
{
173
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
174
        struct i5k_amb_data *data = dev_get_drvdata(dev);
175
        unsigned long temp = simple_strtoul(buf, NULL, 10) / 500;
176
 
177
        if (temp > 255)
178
                temp = 255;
179
 
180
        amb_write_byte(data, amb_reg_temp_mid(attr->index), temp);
181
        return count;
182
}
183
 
184
static ssize_t store_amb_max(struct device *dev,
185
                             struct device_attribute *devattr,
186
                             const char *buf,
187
                             size_t count)
188
{
189
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
190
        struct i5k_amb_data *data = dev_get_drvdata(dev);
191
        unsigned long temp = simple_strtoul(buf, NULL, 10) / 500;
192
 
193
        if (temp > 255)
194
                temp = 255;
195
 
196
        amb_write_byte(data, amb_reg_temp_max(attr->index), temp);
197
        return count;
198
}
199
 
200
static ssize_t show_amb_min(struct device *dev,
201
                             struct device_attribute *devattr,
202
                             char *buf)
203
{
204
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
205
        struct i5k_amb_data *data = dev_get_drvdata(dev);
206
        return sprintf(buf, "%d\n",
207
                500 * amb_read_byte(data, amb_reg_temp_min(attr->index)));
208
}
209
 
210
static ssize_t show_amb_mid(struct device *dev,
211
                             struct device_attribute *devattr,
212
                             char *buf)
213
{
214
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
215
        struct i5k_amb_data *data = dev_get_drvdata(dev);
216
        return sprintf(buf, "%d\n",
217
                500 * amb_read_byte(data, amb_reg_temp_mid(attr->index)));
218
}
219
 
220
static ssize_t show_amb_max(struct device *dev,
221
                             struct device_attribute *devattr,
222
                             char *buf)
223
{
224
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
225
        struct i5k_amb_data *data = dev_get_drvdata(dev);
226
        return sprintf(buf, "%d\n",
227
                500 * amb_read_byte(data, amb_reg_temp_max(attr->index)));
228
}
229
 
230
static ssize_t show_amb_temp(struct device *dev,
231
                             struct device_attribute *devattr,
232
                             char *buf)
233
{
234
        struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
235
        struct i5k_amb_data *data = dev_get_drvdata(dev);
236
        return sprintf(buf, "%d\n",
237
                500 * amb_read_byte(data, amb_reg_temp(attr->index)));
238
}
239
 
240
static int __devinit i5k_amb_hwmon_init(struct platform_device *pdev)
241
{
242
        int i, j, k, d = 0;
243
        u16 c;
244
        int res = 0;
245
        int num_ambs = 0;
246
        struct i5k_amb_data *data = platform_get_drvdata(pdev);
247
 
248
        /* Count the number of AMBs found */
249
        /* ignore the high-order bit, see "Ugly hack" comment above */
250
        for (i = 0; i < MAX_MEM_CHANNELS; i++)
251
                num_ambs += hweight16(data->amb_present[i] & 0x7fff);
252
 
253
        /* Set up sysfs stuff */
254
        data->attrs = kzalloc(sizeof(*data->attrs) * num_ambs * KNOBS_PER_AMB,
255
                                GFP_KERNEL);
256
        if (!data->attrs)
257
                return -ENOMEM;
258
        data->num_attrs = 0;
259
 
260
        for (i = 0; i < MAX_MEM_CHANNELS; i++) {
261
                c = data->amb_present[i];
262
                for (j = 0; j < REAL_MAX_AMBS_PER_CHANNEL; j++, c >>= 1) {
263
                        struct i5k_device_attribute *iattr;
264
 
265
                        k = amb_num_from_reg(i, j);
266
                        if (!(c & 0x1))
267
                                continue;
268
                        d++;
269
 
270
                        /* Temperature sysfs knob */
271
                        iattr = data->attrs + data->num_attrs;
272
                        snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
273
                                 "temp%d_input", d);
274
                        iattr->s_attr.dev_attr.attr.name = iattr->name;
275
                        iattr->s_attr.dev_attr.attr.mode = S_IRUGO;
276
                        iattr->s_attr.dev_attr.show = show_amb_temp;
277
                        iattr->s_attr.index = k;
278
                        res = device_create_file(&pdev->dev,
279
                                                 &iattr->s_attr.dev_attr);
280
                        if (res)
281
                                goto exit_remove;
282
                        data->num_attrs++;
283
 
284
                        /* Temperature min sysfs knob */
285
                        iattr = data->attrs + data->num_attrs;
286
                        snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
287
                                 "temp%d_min", d);
288
                        iattr->s_attr.dev_attr.attr.name = iattr->name;
289
                        iattr->s_attr.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
290
                        iattr->s_attr.dev_attr.show = show_amb_min;
291
                        iattr->s_attr.dev_attr.store = store_amb_min;
292
                        iattr->s_attr.index = k;
293
                        res = device_create_file(&pdev->dev,
294
                                                 &iattr->s_attr.dev_attr);
295
                        if (res)
296
                                goto exit_remove;
297
                        data->num_attrs++;
298
 
299
                        /* Temperature mid sysfs knob */
300
                        iattr = data->attrs + data->num_attrs;
301
                        snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
302
                                 "temp%d_mid", d);
303
                        iattr->s_attr.dev_attr.attr.name = iattr->name;
304
                        iattr->s_attr.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
305
                        iattr->s_attr.dev_attr.show = show_amb_mid;
306
                        iattr->s_attr.dev_attr.store = store_amb_mid;
307
                        iattr->s_attr.index = k;
308
                        res = device_create_file(&pdev->dev,
309
                                                 &iattr->s_attr.dev_attr);
310
                        if (res)
311
                                goto exit_remove;
312
                        data->num_attrs++;
313
 
314
                        /* Temperature max sysfs knob */
315
                        iattr = data->attrs + data->num_attrs;
316
                        snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
317
                                 "temp%d_max", d);
318
                        iattr->s_attr.dev_attr.attr.name = iattr->name;
319
                        iattr->s_attr.dev_attr.attr.mode = S_IWUSR | S_IRUGO;
320
                        iattr->s_attr.dev_attr.show = show_amb_max;
321
                        iattr->s_attr.dev_attr.store = store_amb_max;
322
                        iattr->s_attr.index = k;
323
                        res = device_create_file(&pdev->dev,
324
                                                 &iattr->s_attr.dev_attr);
325
                        if (res)
326
                                goto exit_remove;
327
                        data->num_attrs++;
328
 
329
                        /* Temperature alarm sysfs knob */
330
                        iattr = data->attrs + data->num_attrs;
331
                        snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
332
                                 "temp%d_alarm", d);
333
                        iattr->s_attr.dev_attr.attr.name = iattr->name;
334
                        iattr->s_attr.dev_attr.attr.mode = S_IRUGO;
335
                        iattr->s_attr.dev_attr.show = show_amb_alarm;
336
                        iattr->s_attr.index = k;
337
                        res = device_create_file(&pdev->dev,
338
                                                 &iattr->s_attr.dev_attr);
339
                        if (res)
340
                                goto exit_remove;
341
                        data->num_attrs++;
342
                }
343
        }
344
 
345
        res = device_create_file(&pdev->dev, &dev_attr_name);
346
        if (res)
347
                goto exit_remove;
348
 
349
        data->hwmon_dev = hwmon_device_register(&pdev->dev);
350
        if (IS_ERR(data->hwmon_dev)) {
351
                res = PTR_ERR(data->hwmon_dev);
352
                goto exit_remove;
353
        }
354
 
355
        return res;
356
 
357
exit_remove:
358
        device_remove_file(&pdev->dev, &dev_attr_name);
359
        for (i = 0; i < data->num_attrs; i++)
360
                device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr);
361
        kfree(data->attrs);
362
 
363
        return res;
364
}
365
 
366
static int __devinit i5k_amb_add(void)
367
{
368
        int res = -ENODEV;
369
 
370
        /* only ever going to be one of these */
371
        amb_pdev = platform_device_alloc(DRVNAME, 0);
372
        if (!amb_pdev)
373
                return -ENOMEM;
374
 
375
        res = platform_device_add(amb_pdev);
376
        if (res)
377
                goto err;
378
        return 0;
379
 
380
err:
381
        platform_device_put(amb_pdev);
382
        return res;
383
}
384
 
385
static int __devinit i5k_find_amb_registers(struct i5k_amb_data *data)
386
{
387
        struct pci_dev *pcidev;
388
        u32 val32;
389
        int res = -ENODEV;
390
 
391
        /* Find AMB register memory space */
392
        pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
393
                                PCI_DEVICE_ID_INTEL_5000_ERR,
394
                                NULL);
395
        if (!pcidev)
396
                return -ENODEV;
397
 
398
        if (pci_read_config_dword(pcidev, I5K_REG_AMB_BASE_ADDR, &val32))
399
                goto out;
400
        data->amb_base = val32;
401
 
402
        if (pci_read_config_dword(pcidev, I5K_REG_AMB_LEN_ADDR, &val32))
403
                goto out;
404
        data->amb_len = val32;
405
 
406
        /* Is it big enough? */
407
        if (data->amb_len < AMB_CONFIG_SIZE * MAX_AMBS) {
408
                dev_err(&pcidev->dev, "AMB region too small!\n");
409
                goto out;
410
        }
411
 
412
        res = 0;
413
out:
414
        pci_dev_put(pcidev);
415
        return res;
416
}
417
 
418
static int __devinit i5k_channel_probe(u16 *amb_present, unsigned long dev_id)
419
{
420
        struct pci_dev *pcidev;
421
        u16 val16;
422
        int res = -ENODEV;
423
 
424
        /* Copy the DIMM presence map for these two channels */
425
        pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_id, NULL);
426
        if (!pcidev)
427
                return -ENODEV;
428
 
429
        if (pci_read_config_word(pcidev, I5K_REG_CHAN0_PRESENCE_ADDR, &val16))
430
                goto out;
431
        amb_present[0] = val16;
432
 
433
        if (pci_read_config_word(pcidev, I5K_REG_CHAN1_PRESENCE_ADDR, &val16))
434
                goto out;
435
        amb_present[1] = val16;
436
 
437
        res = 0;
438
 
439
out:
440
        pci_dev_put(pcidev);
441
        return res;
442
}
443
 
444
static int __devinit i5k_amb_probe(struct platform_device *pdev)
445
{
446
        struct i5k_amb_data *data;
447
        struct resource *reso;
448
        int res = -ENODEV;
449
 
450
        data = kzalloc(sizeof(*data), GFP_KERNEL);
451
        if (!data)
452
                return -ENOMEM;
453
 
454
        /* Figure out where the AMB registers live */
455
        res = i5k_find_amb_registers(data);
456
        if (res)
457
                goto err;
458
 
459
        /* Copy the DIMM presence map for the first two channels */
460
        res = i5k_channel_probe(&data->amb_present[0],
461
                                PCI_DEVICE_ID_INTEL_5000_FBD0);
462
        if (res)
463
                goto err;
464
 
465
        /* Copy the DIMM presence map for the optional second two channels */
466
        i5k_channel_probe(&data->amb_present[2],
467
                          PCI_DEVICE_ID_INTEL_5000_FBD1);
468
 
469
        /* Set up resource regions */
470
        reso = request_mem_region(data->amb_base, data->amb_len, DRVNAME);
471
        if (!reso) {
472
                res = -EBUSY;
473
                goto err;
474
        }
475
 
476
        data->amb_mmio = ioremap_nocache(data->amb_base, data->amb_len);
477
        if (!data->amb_mmio) {
478
                res = -EBUSY;
479
                goto err_map_failed;
480
        }
481
 
482
        platform_set_drvdata(pdev, data);
483
 
484
        res = i5k_amb_hwmon_init(pdev);
485
        if (res)
486
                goto err_init_failed;
487
 
488
        return res;
489
 
490
err_init_failed:
491
        iounmap(data->amb_mmio);
492
        platform_set_drvdata(pdev, NULL);
493
err_map_failed:
494
        release_mem_region(data->amb_base, data->amb_len);
495
err:
496
        kfree(data);
497
        return res;
498
}
499
 
500
static int __devexit i5k_amb_remove(struct platform_device *pdev)
501
{
502
        int i;
503
        struct i5k_amb_data *data = platform_get_drvdata(pdev);
504
 
505
        hwmon_device_unregister(data->hwmon_dev);
506
        device_remove_file(&pdev->dev, &dev_attr_name);
507
        for (i = 0; i < data->num_attrs; i++)
508
                device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr);
509
        kfree(data->attrs);
510
        iounmap(data->amb_mmio);
511
        release_mem_region(data->amb_base, data->amb_len);
512
        platform_set_drvdata(pdev, NULL);
513
        kfree(data);
514
        return 0;
515
}
516
 
517
static struct platform_driver i5k_amb_driver = {
518
        .driver = {
519
                .owner = THIS_MODULE,
520
                .name = DRVNAME,
521
        },
522
        .probe = i5k_amb_probe,
523
        .remove = __devexit_p(i5k_amb_remove),
524
};
525
 
526
static int __init i5k_amb_init(void)
527
{
528
        int res;
529
 
530
        res = platform_driver_register(&i5k_amb_driver);
531
        if (res)
532
                return res;
533
 
534
        res = i5k_amb_add();
535
        if (res)
536
                platform_driver_unregister(&i5k_amb_driver);
537
 
538
        return res;
539
}
540
 
541
static void __exit i5k_amb_exit(void)
542
{
543
        platform_device_unregister(amb_pdev);
544
        platform_driver_unregister(&i5k_amb_driver);
545
}
546
 
547
MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>");
548
MODULE_DESCRIPTION("Intel 5000 chipset FB-DIMM AMB temperature sensor");
549
MODULE_LICENSE("GPL");
550
 
551
module_init(i5k_amb_init);
552
module_exit(i5k_amb_exit);

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.