| 32 | | |
| | 33 | #include "i2c.h" |
| | 34 | #include "compat.h" |
| | 35 | |
| | 36 | /* Many LM78 constants needed below */ |
| | 37 | |
| | 38 | /* Length of ISA address segment */ |
| | 39 | #define LM78_EXTENT 8 |
| | 40 | |
| | 41 | /* Where are the ISA address/data registers relative to the base address */ |
| | 42 | #define LM78_ADDR_REG_OFFSET 5 |
| | 43 | #define LM78_DATA_REG_OFFSET 6 |
| | 44 | |
| | 45 | /* The LM78 registers */ |
| | 46 | #define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2) |
| | 47 | #define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2) |
| | 48 | #define LM78_REG_IN(nr) (0x20 + (nr)) |
| | 49 | |
| | 50 | #define LM78_REG_FAN_MIN(nr) (0x3a + (nr)) |
| | 51 | #define LM78_REG_FAN(nr) (0x27 + (nr)) |
| | 52 | |
| | 53 | #define LM78_REG_TEMP 0x27 |
| | 54 | #define LM78_REG_TEMP_OVER 0x39 |
| | 55 | #define LM78_REG_TEMP_HYST 0x3a |
| | 56 | |
| | 57 | #define LM78_REG_ALARM1 0x41 |
| | 58 | #define LM78_REG_ALARM2 0x42 |
| | 59 | |
| | 60 | #define LM78_REG_VID_FANDIV 0x47 |
| | 61 | |
| | 62 | #define LM78_REG_CONFIG 0x40 |
| | 63 | |
| | 64 | |
| | 65 | /* Conversions */ |
| | 66 | static int lm78_in_conv[7] = {10000, 10000, 10000, 16892, 38000, |
| | 67 | -34768, -15050 }; |
| | 68 | #define IN_TO_REG(val,nr) ((((val) * 100000 / lm78_in_conv[nr]) + 8) / 16) |
| | 69 | #define IN_FROM_REG(val,nr) (((val) * 16 * lm78_in_conv[nr]) / 100000) |
| | 70 | |
| | 71 | #define FAN_TO_REG(val) (((val)==0)?255:((1350000+(val))/((val)*2))) |
| | 72 | #define FAN_FROM_REG(val) (((val)==0)?-1:\ |
| | 73 | ((val)==255)?0:(1350000 + (val))/((val)*2)) |
| | 74 | |
| | 75 | #define TEMP_TO_REG(val) ((val)<0?(val)&0xff:(val)) |
| | 76 | #define TEMP_FROM_REG(val) ((val)>0x80?(val)-0x100:(val)); |
| | 77 | |
| | 78 | #define VID_FROM_REG(val) ((val) == 0x0f?0:350-(val)*10) |
| | 79 | |
| | 80 | #define ALARMS_FROM_REG(val) (val) |
| | 81 | |
| | 82 | #define DIV_FROM_REG(val) (1 >> (val)) |
| | 83 | #define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?1:2) |
| | 84 | |
| | 85 | /* Initial limits */ |
| | 86 | #define LM78_INIT_IN_0 280 |
| | 87 | #define LM78_INIT_IN_1 280 |
| | 88 | #define LM78_INIT_IN_2 330 |
| | 89 | #define LM78_INIT_IN_3 500 |
| | 90 | #define LM78_INIT_IN_4 1200 |
| | 91 | #define LM78_INIT_IN_5 -1200 |
| | 92 | #define LM78_INIT_IN_6 -500 |
| | 93 | |
| | 94 | #define LM78_INIT_IN_PERCENTAGE 100 |
| | 95 | |
| | 96 | #define LM78_INIT_IN_MIN_0 \ |
| | 97 | (LM78_INIT_IN_0 - LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 98 | #define LM78_INIT_IN_MAX_0 \ |
| | 99 | (LM78_INIT_IN_0 + LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 100 | #define LM78_INIT_IN_MIN_1 \ |
| | 101 | (LM78_INIT_IN_1 - LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 102 | #define LM78_INIT_IN_MAX_1 \ |
| | 103 | (LM78_INIT_IN_1 + LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 104 | #define LM78_INIT_IN_MIN_2 \ |
| | 105 | (LM78_INIT_IN_2 - LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 106 | #define LM78_INIT_IN_MAX_2 \ |
| | 107 | (LM78_INIT_IN_2 + LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 108 | #define LM78_INIT_IN_MIN_3 \ |
| | 109 | (LM78_INIT_IN_3 - LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 110 | #define LM78_INIT_IN_MAX_3 \ |
| | 111 | (LM78_INIT_IN_3 + LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 112 | #define LM78_INIT_IN_MIN_4 \ |
| | 113 | (LM78_INIT_IN_4 - LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 114 | #define LM78_INIT_IN_MAX_4 \ |
| | 115 | (LM78_INIT_IN_4 + LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 116 | #define LM78_INIT_IN_MIN_5 \ |
| | 117 | (LM78_INIT_IN_5 - LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 118 | #define LM78_INIT_IN_MAX_5 \ |
| | 119 | (LM78_INIT_IN_5 + LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 120 | #define LM78_INIT_IN_MIN_6 \ |
| | 121 | (LM78_INIT_IN_6 - LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 122 | #define LM78_INIT_IN_MAX_6 \ |
| | 123 | (LM78_INIT_IN_6 + LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100) |
| | 124 | |
| | 125 | #define LM78_INIT_FAN_MIN_1 3000 |
| | 126 | #define LM78_INIT_FAN_MIN_2 3000 |
| | 127 | #define LM78_INIT_FAN_MIN_3 3000 |
| | 128 | |
| | 129 | #define LM78_INIT_TEMP_OVER 60 |
| | 130 | #define LM78_INIT_TEMP_HYST 50 |
| | 244 | /* The /proc/sys entries */ |
| | 245 | /* These files are created for each detected LM78. This is just a template; |
| | 246 | though at first sight, you might think we could use a statically |
| | 247 | allocated list, we need some way to get back to the parent - which |
| | 248 | is done through one of the 'extra' fields which are initialized |
| | 249 | when a new copy is allocated. */ |
| | 250 | static ctl_table lm78_dir_table_template[] = { |
| | 251 | { LM78_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, |
| | 252 | { LM78_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, |
| | 253 | { LM78_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, |
| | 254 | { LM78_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, |
| | 255 | { LM78_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, |
| | 256 | { LM78_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, |
| | 257 | { LM78_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, |
| | 258 | { LM78_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, |
| | 259 | { LM78_SYSCTL_FAN2, "fan1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, |
| | 260 | { LM78_SYSCTL_FAN3, "fan1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, |
| | 261 | { LM78_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, |
| | 262 | { LM78_SYSCTL_VID, "vid", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, |
| | 263 | { LM78_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }, |
| | 264 | |
| | 265 | { LM78_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl } |
| | 266 | }; |
| | 267 | |
| | 268 | |
| 319 | | /* ANYTHING BELOW IS JUST AN EXAMPLE. IGNORE IF YOU WANT TO BASE A DRIVER |
| 320 | | ON THE CODE IN THIS FILE */ |
| 321 | | |
| 322 | | /* Stupid entry in /proc */ |
| 323 | | static int proc_function(char *buf, char **start, off_t offset, int len, |
| 324 | | int unused) |
| 325 | | { |
| | 560 | /* Called when we have found a new LM78. It should set limits, etc. */ |
| | 561 | void lm78_init_client(struct i2c_client *client) |
| | 562 | { |
| | 563 | /* Reset all except Watchdog values and last conversion values |
| | 564 | This sets fan-divs to 2, among others */ |
| | 565 | lm78_write_value(client,LM78_REG_CONFIG,0x80); |
| | 566 | |
| | 567 | lm78_write_value(client,LM78_REG_IN_MIN(0),IN_TO_REG(LM78_INIT_IN_MIN_0,0)); |
| | 568 | lm78_write_value(client,LM78_REG_IN_MAX(0),IN_TO_REG(LM78_INIT_IN_MAX_0,0)); |
| | 569 | lm78_write_value(client,LM78_REG_IN_MIN(1),IN_TO_REG(LM78_INIT_IN_MIN_1,1)); |
| | 570 | lm78_write_value(client,LM78_REG_IN_MAX(1),IN_TO_REG(LM78_INIT_IN_MAX_1,1)); |
| | 571 | lm78_write_value(client,LM78_REG_IN_MIN(2),IN_TO_REG(LM78_INIT_IN_MIN_2,2)); |
| | 572 | lm78_write_value(client,LM78_REG_IN_MAX(2),IN_TO_REG(LM78_INIT_IN_MAX_2,2)); |
| | 573 | lm78_write_value(client,LM78_REG_IN_MIN(3),IN_TO_REG(LM78_INIT_IN_MIN_3,3)); |
| | 574 | lm78_write_value(client,LM78_REG_IN_MAX(3),IN_TO_REG(LM78_INIT_IN_MAX_3,3)); |
| | 575 | lm78_write_value(client,LM78_REG_IN_MIN(4),IN_TO_REG(LM78_INIT_IN_MIN_4,4)); |
| | 576 | lm78_write_value(client,LM78_REG_IN_MAX(4),IN_TO_REG(LM78_INIT_IN_MAX_4,4)); |
| | 577 | lm78_write_value(client,LM78_REG_IN_MIN(5),IN_TO_REG(LM78_INIT_IN_MIN_5,5)); |
| | 578 | lm78_write_value(client,LM78_REG_IN_MAX(5),IN_TO_REG(LM78_INIT_IN_MAX_5,5)); |
| | 579 | lm78_write_value(client,LM78_REG_IN_MIN(6),IN_TO_REG(LM78_INIT_IN_MIN_6,6)); |
| | 580 | lm78_write_value(client,LM78_REG_IN_MAX(6),IN_TO_REG(LM78_INIT_IN_MAX_6,6)); |
| | 581 | lm78_write_value(client,LM78_REG_FAN_MIN(1),FAN_TO_REG(LM78_INIT_FAN_MIN_1)); |
| | 582 | lm78_write_value(client,LM78_REG_FAN_MIN(2),FAN_TO_REG(LM78_INIT_FAN_MIN_2)); |
| | 583 | lm78_write_value(client,LM78_REG_FAN_MIN(3),FAN_TO_REG(LM78_INIT_FAN_MIN_3)); |
| | 584 | lm78_write_value(client,LM78_REG_TEMP_OVER,TEMP_TO_REG(LM78_INIT_TEMP_OVER)); |
| | 585 | lm78_write_value(client,LM78_REG_TEMP_HYST,TEMP_TO_REG(LM78_INIT_TEMP_HYST)); |
| | 586 | |
| | 587 | /* Start monitoring */ |
| | 588 | lm78_write_value(client,LM78_REG_CONFIG, |
| | 589 | (lm78_read_value(client,LM78_REG_CONFIG) & 0xf7) | 0x01); |
| | 590 | |
| | 591 | } |
| | 592 | |
| | 593 | void lm78_update_client(struct i2c_client *client) |
| | 594 | { |
| | 595 | struct lm78_data *data = client->data; |
| 337 | | return len; |
| 338 | | } |
| 339 | | |
| 340 | | /* OK, this is a test entry. Just ignore, it is not important. */ |
| 341 | | static struct proc_dir_entry proc_entry = |
| 342 | | { |
| 343 | | 0,12,"sensors-test", |
| 344 | | S_IFREG | S_IRUGO, 1, 0, 0, |
| 345 | | 0, NULL, |
| 346 | | &proc_function |
| 347 | | }; |
| 348 | | |
| | 608 | for (i = 1; i <= 3; i++) { |
| | 609 | data->fan[i-1] = lm78_read_value(client,LM78_REG_FAN(i)); |
| | 610 | data->fan_min[i-1] = lm78_read_value(client,LM78_REG_FAN_MIN(i)); |
| | 611 | } |
| | 612 | data->temp = lm78_read_value(client,LM78_REG_TEMP); |
| | 613 | data->temp_over = lm78_read_value(client,LM78_REG_TEMP_OVER); |
| | 614 | data->temp_hyst = lm78_read_value(client,LM78_REG_TEMP_HYST); |
| | 615 | i = lm78_read_value(client,LM78_REG_VID_FANDIV); |
| | 616 | data->vid = i & 0x0f; |
| | 617 | data->fan_div[0] = (i >> 4) & 0x03; |
| | 618 | data->fan_div[1] = i >> 6; |
| | 619 | data->alarms = lm78_read_value(client,LM78_REG_ALARM1) + |
| | 620 | (lm78_read_value(client,LM78_REG_ALARM2) >> 8); |
| | 621 | data->last_updated = jiffies; |
| | 622 | data->valid = 1; |
| | 623 | } |
| | 624 | |
| | 625 | up(&data->update_lock); |
| | 626 | } |
| | 627 | |
| | 628 | /* This function is called when /proc/sys/dev/lm78-???/... is accessed */ |
| | 629 | int lm78_proc (ctl_table *ctl, int write, struct file * filp, |
| | 630 | void *buffer, size_t *lenp) |
| | 631 | { |
| | 632 | int nrels,mag; |
| | 633 | long results[7]; |
| | 634 | struct i2c_client *client = ctl -> extra1; |
| | 635 | |
| | 636 | /* If buffer is size 0, or we try to read when not at the start, we |
| | 637 | return nothing. Note that I think writing when not at the start |
| | 638 | does not work either, but anyway, this is straight from the kernel |
| | 639 | sources. */ |
| | 640 | if (!*lenp || (filp->f_pos && !write)) { |
| | 641 | *lenp = 0; |
| | 642 | return 0; |
| | 643 | } |
| | 644 | |
| | 645 | /* How many numbers are found within these files, and how to scale them? */ |
| | 646 | switch (ctl->ctl_name) { |
| | 647 | case LM78_SYSCTL_IN0: case LM78_SYSCTL_IN1: case LM78_SYSCTL_IN2: |
| | 648 | case LM78_SYSCTL_IN3: case LM78_SYSCTL_IN4: case LM78_SYSCTL_IN5: |
| | 649 | case LM78_SYSCTL_IN6: |
| | 650 | nrels=3; |
| | 651 | mag=2; |
| | 652 | break; |
| | 653 | case LM78_SYSCTL_FAN_DIV: case LM78_SYSCTL_TEMP: |
| | 654 | nrels=3; |
| | 655 | mag=0; |
| | 656 | break; |
| | 657 | case LM78_SYSCTL_FAN1: case LM78_SYSCTL_FAN2: case LM78_SYSCTL_FAN3: |
| | 658 | nrels=2; |
| | 659 | mag=0; |
| | 660 | break; |
| | 661 | case LM78_SYSCTL_VID: |
| | 662 | nrels=1; |
| | 663 | mag=2; |
| | 664 | break; |
| | 665 | case LM78_SYSCTL_ALARMS: |
| | 666 | nrels=1; |
| | 667 | mag=0; |
| | 668 | break; |
| | 669 | default: /* Should never be called */ |
| | 670 | return -EINVAL; |
| | 671 | } |
| | 672 | |
| | 673 | /* OK, try writing stuff. */ |
| | 674 | if (write) { |
| | 675 | sensors_parse_reals(&nrels,buffer,*lenp,results,mag); |
| | 676 | if (nrels == 0) |
| | 677 | return 0; |
| | 678 | switch (ctl->ctl_name) { |
| | 679 | case LM78_SYSCTL_IN0: write_in(client,0,nrels,results); break; |
| | 680 | case LM78_SYSCTL_IN1: write_in(client,1,nrels,results); break; |
| | 681 | case LM78_SYSCTL_IN2: write_in(client,2,nrels,results); break; |
| | 682 | case LM78_SYSCTL_IN3: write_in(client,3,nrels,results); break; |
| | 683 | case LM78_SYSCTL_IN4: write_in(client,4,nrels,results); break; |
| | 684 | case LM78_SYSCTL_IN5: write_in(client,5,nrels,results); break; |
| | 685 | case LM78_SYSCTL_IN6: write_in(client,6,nrels,results); break; |
| | 686 | case LM78_SYSCTL_FAN1: write_fan(client,1,nrels,results); break; |
| | 687 | case LM78_SYSCTL_FAN2: write_fan(client,2,nrels,results); break; |
| | 688 | case LM78_SYSCTL_FAN3: write_fan(client,3,nrels,results); break; |
| | 689 | case LM78_SYSCTL_FAN_DIV: write_fan_div(client,nrels,results);break; |
| | 690 | case LM78_SYSCTL_TEMP: write_temp(client,nrels,results);break; |
| | 691 | case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS: break; |
| | 692 | default: /* Should never be called */ *lenp=0; return -EINVAL; break; |
| | 693 | } |
| | 694 | filp->f_pos += *lenp; |
| | 695 | return 0; |
| | 696 | } else { /* read */ |
| | 697 | /* Update all values in LM_Sensor_Data */ |
| | 698 | |
| | 699 | lm78_update_client((struct i2c_client *) (ctl->extra1)); |
| | 700 | |
| | 701 | /* Read the values to print into results */ |
| | 702 | switch (ctl->ctl_name) { |
| | 703 | case LM78_SYSCTL_IN0: read_in(client,0,results);break; |
| | 704 | case LM78_SYSCTL_IN1: read_in(client,1,results);break; |
| | 705 | case LM78_SYSCTL_IN2: read_in(client,2,results);break; |
| | 706 | case LM78_SYSCTL_IN3: read_in(client,3,results);break; |
| | 707 | case LM78_SYSCTL_IN4: read_in(client,4,results);break; |
| | 708 | case LM78_SYSCTL_IN5: read_in(client,5,results);break; |
| | 709 | case LM78_SYSCTL_IN6: read_in(client,6,results);break; |
| | 710 | case LM78_SYSCTL_FAN1: read_fan(client,1,results);break; |
| | 711 | case LM78_SYSCTL_FAN2: read_fan(client,2,results);break; |
| | 712 | case LM78_SYSCTL_FAN3: read_fan(client,3,results);break; |
| | 713 | case LM78_SYSCTL_TEMP: read_temp(client,results);break; |
| | 714 | case LM78_SYSCTL_FAN_DIV: read_fan_div(client,results);break; |
| | 715 | case LM78_SYSCTL_VID: read_vid(client,results);break; |
| | 716 | case LM78_SYSCTL_ALARMS: read_alarms(client,results);break; |
| | 717 | default: /* Should never be called */ return -EINVAL; |
| | 718 | } |
| | 719 | /* OK, print it now */ |
| | 720 | sensors_write_reals(nrels,buffer,lenp,results,mag); |
| | 721 | filp->f_pos += *lenp; |
| | 722 | return 0; |
| | 723 | } |
| | 724 | } |
| | 725 | |
| | 726 | /* This function is called when a sysctl on a lm78 file is done */ |
| | 727 | int lm78_sysctl (ctl_table *table, int *name, int nlen, void *oldval, |
| | 728 | size_t *oldlenp, void *newval, size_t newlen, |
| | 729 | void **context) |
| | 730 | { |
| | 731 | long results[7]; |
| | 732 | int nrels,oldlen; |
| | 733 | struct i2c_client *client = table -> extra1; |
| | 734 | |
| | 735 | /* How many numbers are found within these files, and how to scale them? */ |
| | 736 | switch (table->ctl_name) { |
| | 737 | case LM78_SYSCTL_IN0: case LM78_SYSCTL_IN1: case LM78_SYSCTL_IN2: |
| | 738 | case LM78_SYSCTL_IN3: case LM78_SYSCTL_IN4: case LM78_SYSCTL_IN5: |
| | 739 | case LM78_SYSCTL_IN6: case LM78_SYSCTL_TEMP: case LM78_SYSCTL_FAN_DIV: |
| | 740 | nrels=3; |
| | 741 | break; |
| | 742 | case LM78_SYSCTL_FAN1: case LM78_SYSCTL_FAN2: case LM78_SYSCTL_FAN3: |
| | 743 | nrels=2; |
| | 744 | break; |
| | 745 | case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS: |
| | 746 | nrels=1; |
| | 747 | break; |
| | 748 | default: /* Should never be called */ |
| | 749 | return -EINVAL; |
| | 750 | } |
| | 751 | |
| | 752 | /* Check if we need to output the old values */ |
| | 753 | if (oldval && oldlenp && ! get_user_data(oldlen,oldlenp) && oldlen) { |
| | 754 | |
| | 755 | /* Update all values in LM_Sensor_Data */ |
| | 756 | lm78_update_client((struct i2c_client *) (table->extra1)); |
| | 757 | switch (table->ctl_name) { |
| | 758 | case LM78_SYSCTL_IN0: read_in(client,0,results);break; |
| | 759 | case LM78_SYSCTL_IN1: read_in(client,1,results);break; |
| | 760 | case LM78_SYSCTL_IN2: read_in(client,2,results);break; |
| | 761 | case LM78_SYSCTL_IN3: read_in(client,3,results);break; |
| | 762 | case LM78_SYSCTL_IN4: read_in(client,4,results);break; |
| | 763 | case LM78_SYSCTL_IN5: read_in(client,5,results);break; |
| | 764 | case LM78_SYSCTL_IN6: read_in(client,6,results);break; |
| | 765 | case LM78_SYSCTL_FAN1: read_fan(client,1,results);break; |
| | 766 | case LM78_SYSCTL_FAN2: read_fan(client,2,results);break; |
| | 767 | case LM78_SYSCTL_FAN3: read_fan(client,3,results);break; |
| | 768 | case LM78_SYSCTL_TEMP: read_temp(client,results);break; |
| | 769 | case LM78_SYSCTL_FAN_DIV: read_fan_div(client,results);break; |
| | 770 | case LM78_SYSCTL_VID: read_vid(client,results);break; |
| | 771 | case LM78_SYSCTL_ALARMS: read_alarms(client,results);break; |
| | 772 | default: /* Should never be called */ return -EINVAL; |
| | 773 | } |
| | 775 | /* Note the rounding factor! */ |
| | 776 | if (nrels * sizeof(long) < oldlen) |
| | 777 | oldlen = nrels * sizeof(long); |
| | 778 | oldlen = (oldlen / sizeof(long)) * sizeof(long); |
| | 779 | copy_to_user(oldval,results,oldlen); |
| | 780 | put_user(oldlen,oldlenp); |
| | 781 | } |
| | 782 | |
| | 783 | /* Check to see whether we need to read the new values */ |
| | 784 | if (newval && newlen) { |
| | 785 | if (nrels * sizeof(long) < newlen) |
| | 786 | newlen = nrels * sizeof(long); |
| | 787 | nrels = newlen / sizeof(long); |
| | 788 | newlen = (newlen / sizeof(long)) * sizeof(long); |
| | 789 | copy_from_user(results,newval,newlen); |
| | 790 | |
| | 791 | switch (table->ctl_name) { |
| | 792 | case LM78_SYSCTL_IN0: write_in(client,0,nrels,results); break; |
| | 793 | case LM78_SYSCTL_IN1: write_in(client,1,nrels,results); break; |
| | 794 | case LM78_SYSCTL_IN2: write_in(client,2,nrels,results); break; |
| | 795 | case LM78_SYSCTL_IN3: write_in(client,3,nrels,results); break; |
| | 796 | case LM78_SYSCTL_IN4: write_in(client,4,nrels,results); break; |
| | 797 | case LM78_SYSCTL_IN5: write_in(client,5,nrels,results); break; |
| | 798 | case LM78_SYSCTL_IN6: write_in(client,6,nrels,results); break; |
| | 799 | case LM78_SYSCTL_FAN1: write_fan(client,1,nrels,results); break; |
| | 800 | case LM78_SYSCTL_FAN2: write_fan(client,2,nrels,results); break; |
| | 801 | case LM78_SYSCTL_FAN3: write_fan(client,3,nrels,results); break; |
| | 802 | case LM78_SYSCTL_TEMP: write_temp(client,nrels,results); break; |
| | 803 | case LM78_SYSCTL_FAN_DIV: write_fan_div(client,nrels,results);break; |
| | 804 | case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS: break; |
| | 805 | default: /* Should never be called */ return -EINVAL; break; |
| | 806 | } |
| | 807 | } |
| | 808 | return 1; /* We have done all the work */ |
| | 809 | } |
| | 810 | |
| | 811 | void write_in(struct i2c_client *client, int nr, int nrels, long *results) |
| | 812 | { |
| | 813 | struct lm78_data *data = client->data; |
| | 814 | if (nrels >= 1) { |
| | 815 | data->in_min[nr] = IN_TO_REG(results[0],nr); |
| | 816 | lm78_write_value(client,LM78_REG_IN_MIN(nr),data->in_min[nr]); |
| | 817 | } |
| | 818 | if (nrels >= 2) { |
| | 819 | data->in_max[nr] = IN_TO_REG(results[1],nr); |
| | 820 | lm78_write_value(client,LM78_REG_IN_MAX(nr),data->in_max[nr]); |
| | 821 | } |
| | 822 | } |
| | 823 | |
| | 824 | void read_in(struct i2c_client *client, int nr, long *results) |
| | 825 | { |
| | 826 | struct lm78_data *data = client->data; |
| | 827 | results[0] = IN_FROM_REG(data->in_min[nr],nr); |
| | 828 | results[1] = IN_FROM_REG(data->in_min[nr],nr); |
| | 829 | results[2] = IN_FROM_REG(data->in_min[nr],nr); |
| | 830 | } |
| | 831 | |
| | 832 | void write_fan(struct i2c_client *client, int nr, int nrels, long *results) |
| | 833 | { |
| | 834 | struct lm78_data *data = client->data; |
| | 835 | if (nrels >= 1) { |
| | 836 | data->fan_min[nr-1] = FAN_TO_REG(results[0]); |
| | 837 | lm78_write_value(client,LM78_REG_FAN_MIN(nr),data->fan_min[nr-1]); |
| | 838 | } |
| | 839 | } |
| | 840 | |
| | 841 | void read_fan(struct i2c_client *client, int nr, long *results) |
| | 842 | { |
| | 843 | struct lm78_data *data = client->data; |
| | 844 | results[0] = FAN_FROM_REG(data->fan_min[nr-1]); |
| | 845 | results[1] = FAN_FROM_REG(data->fan[nr-1]); |
| | 846 | } |
| | 847 | |
| | 848 | void write_temp(struct i2c_client *client, int nrels, long *results) |
| | 849 | { |
| | 850 | struct lm78_data *data = client->data; |
| | 851 | if (nrels >= 1) { |
| | 852 | data->temp_over = TEMP_TO_REG(results[0]); |
| | 853 | lm78_write_value(client,LM78_REG_TEMP_OVER,data->temp_over); |
| | 854 | } |
| | 855 | if (nrels >= 2) { |
| | 856 | data->temp_hyst = TEMP_TO_REG(results[0]); |
| | 857 | lm78_write_value(client,LM78_REG_TEMP_HYST,data->temp_hyst); |
| | 858 | } |
| | 859 | } |
| | 860 | |
| | 861 | void read_temp(struct i2c_client *client, long *results) |
| | 862 | { |
| | 863 | struct lm78_data *data = client->data; |
| | 864 | results[0] = TEMP_FROM_REG(data->temp_over); |
| | 865 | results[1] = TEMP_FROM_REG(data->temp_hyst); |
| | 866 | results[2] = TEMP_FROM_REG(data->temp); |
| | 867 | } |
| | 868 | |
| | 869 | void read_vid(struct i2c_client *client, long *results) |
| | 870 | { |
| | 871 | struct lm78_data *data = client->data; |
| | 872 | results[0] = VID_FROM_REG(data->vid); |
| | 873 | } |
| | 874 | |
| | 875 | void read_alarms(struct i2c_client *client, long *results) |
| | 876 | { |
| | 877 | struct lm78_data *data = client->data; |
| | 878 | results[0] = ALARMS_FROM_REG(data->alarms); |
| | 879 | } |
| | 880 | |
| | 881 | void read_fan_div(struct i2c_client *client, long *results) |
| | 882 | { |
| | 883 | struct lm78_data *data = client->data; |
| | 884 | results[0] = DIV_FROM_REG(data->fan_div[0]); |
| | 885 | results[1] = DIV_FROM_REG(data->fan_div[1]); |
| | 886 | } |
| | 887 | |
| | 888 | void write_fan_div(struct i2c_client *client, int nrels, long *results) |
| | 889 | { |
| | 890 | struct lm78_data *data = client->data; |
| | 891 | if (nrels >= 2) |
| | 892 | data->fan_div[1] = DIV_TO_REG(results[1]); |
| | 893 | if (nrels >= 1) { |
| | 894 | data->fan_div[0] = DIV_TO_REG(results[0]); |
| | 895 | lm78_write_value(client,LM78_REG_VID_FANDIV, |
| | 896 | (data->fan_div[0] >> 4) | (data->fan_div[1] >> 6)); |
| | 897 | } |
| | 898 | } |
| | 899 | |