Skip to content

Commit 54076cb

Browse files
Eddie Jamesgroeck
authored andcommitted
hwmon (occ): Add sensor attributes and register hwmon device
Setup the sensor attributes for every OCC sensor found by the first poll response. Register the attributes with hwmon. Signed-off-by: Eddie James <eajames@linux.ibm.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net>
1 parent c10e753 commit 54076cb

File tree

2 files changed

+353
-0
lines changed

2 files changed

+353
-0
lines changed

drivers/hwmon/occ/common.c

Lines changed: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
// SPDX-License-Identifier: GPL-2.0
22

33
#include <linux/device.h>
4+
#include <linux/hwmon.h>
45
#include <linux/hwmon-sysfs.h>
56
#include <linux/jiffies.h>
67
#include <linux/kernel.h>
78
#include <linux/math64.h>
89
#include <linux/mutex.h>
10+
#include <linux/sysfs.h>
911
#include <asm/unaligned.h>
1012

1113
#include "common.h"
@@ -641,6 +643,324 @@ static ssize_t occ_show_extended(struct device *dev,
641643
return rc;
642644
}
643645

646+
/*
647+
* Some helper macros to make it easier to define an occ_attribute. Since these
648+
* are dynamically allocated, we shouldn't use the existing kernel macros which
649+
* stringify the name argument.
650+
*/
651+
#define ATTR_OCC(_name, _mode, _show, _store) { \
652+
.attr = { \
653+
.name = _name, \
654+
.mode = VERIFY_OCTAL_PERMISSIONS(_mode), \
655+
}, \
656+
.show = _show, \
657+
.store = _store, \
658+
}
659+
660+
#define SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index) { \
661+
.dev_attr = ATTR_OCC(_name, _mode, _show, _store), \
662+
.index = _index, \
663+
.nr = _nr, \
664+
}
665+
666+
#define OCC_INIT_ATTR(_name, _mode, _show, _store, _nr, _index) \
667+
((struct sensor_device_attribute_2) \
668+
SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index))
669+
670+
/*
671+
* Allocate and instatiate sensor_device_attribute_2s. It's most efficient to
672+
* use our own instead of the built-in hwmon attribute types.
673+
*/
674+
static int occ_setup_sensor_attrs(struct occ *occ)
675+
{
676+
unsigned int i, s, num_attrs = 0;
677+
struct device *dev = occ->bus_dev;
678+
struct occ_sensors *sensors = &occ->sensors;
679+
struct occ_attribute *attr;
680+
struct temp_sensor_2 *temp;
681+
ssize_t (*show_temp)(struct device *, struct device_attribute *,
682+
char *) = occ_show_temp_1;
683+
ssize_t (*show_freq)(struct device *, struct device_attribute *,
684+
char *) = occ_show_freq_1;
685+
ssize_t (*show_power)(struct device *, struct device_attribute *,
686+
char *) = occ_show_power_1;
687+
ssize_t (*show_caps)(struct device *, struct device_attribute *,
688+
char *) = occ_show_caps_1_2;
689+
690+
switch (sensors->temp.version) {
691+
case 1:
692+
num_attrs += (sensors->temp.num_sensors * 2);
693+
break;
694+
case 2:
695+
num_attrs += (sensors->temp.num_sensors * 4);
696+
show_temp = occ_show_temp_2;
697+
break;
698+
default:
699+
sensors->temp.num_sensors = 0;
700+
}
701+
702+
switch (sensors->freq.version) {
703+
case 2:
704+
show_freq = occ_show_freq_2;
705+
/* fall through */
706+
case 1:
707+
num_attrs += (sensors->freq.num_sensors * 2);
708+
break;
709+
default:
710+
sensors->freq.num_sensors = 0;
711+
}
712+
713+
switch (sensors->power.version) {
714+
case 2:
715+
show_power = occ_show_power_2;
716+
/* fall through */
717+
case 1:
718+
num_attrs += (sensors->power.num_sensors * 4);
719+
break;
720+
case 0xA0:
721+
num_attrs += (sensors->power.num_sensors * 16);
722+
show_power = occ_show_power_a0;
723+
break;
724+
default:
725+
sensors->power.num_sensors = 0;
726+
}
727+
728+
switch (sensors->caps.version) {
729+
case 1:
730+
num_attrs += (sensors->caps.num_sensors * 7);
731+
break;
732+
case 3:
733+
show_caps = occ_show_caps_3;
734+
/* fall through */
735+
case 2:
736+
num_attrs += (sensors->caps.num_sensors * 8);
737+
break;
738+
default:
739+
sensors->caps.num_sensors = 0;
740+
}
741+
742+
switch (sensors->extended.version) {
743+
case 1:
744+
num_attrs += (sensors->extended.num_sensors * 3);
745+
break;
746+
default:
747+
sensors->extended.num_sensors = 0;
748+
}
749+
750+
occ->attrs = devm_kzalloc(dev, sizeof(*occ->attrs) * num_attrs,
751+
GFP_KERNEL);
752+
if (!occ->attrs)
753+
return -ENOMEM;
754+
755+
/* null-terminated list */
756+
occ->group.attrs = devm_kzalloc(dev, sizeof(*occ->group.attrs) *
757+
num_attrs + 1, GFP_KERNEL);
758+
if (!occ->group.attrs)
759+
return -ENOMEM;
760+
761+
attr = occ->attrs;
762+
763+
for (i = 0; i < sensors->temp.num_sensors; ++i) {
764+
s = i + 1;
765+
temp = ((struct temp_sensor_2 *)sensors->temp.data) + i;
766+
767+
snprintf(attr->name, sizeof(attr->name), "temp%d_label", s);
768+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL,
769+
0, i);
770+
attr++;
771+
772+
if (sensors->temp.version > 1 &&
773+
temp->fru_type == OCC_FRU_TYPE_VRM) {
774+
snprintf(attr->name, sizeof(attr->name),
775+
"temp%d_alarm", s);
776+
} else {
777+
snprintf(attr->name, sizeof(attr->name),
778+
"temp%d_input", s);
779+
}
780+
781+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL,
782+
1, i);
783+
attr++;
784+
785+
if (sensors->temp.version > 1) {
786+
snprintf(attr->name, sizeof(attr->name),
787+
"temp%d_fru_type", s);
788+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
789+
show_temp, NULL, 2, i);
790+
attr++;
791+
792+
snprintf(attr->name, sizeof(attr->name),
793+
"temp%d_fault", s);
794+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
795+
show_temp, NULL, 3, i);
796+
attr++;
797+
}
798+
}
799+
800+
for (i = 0; i < sensors->freq.num_sensors; ++i) {
801+
s = i + 1;
802+
803+
snprintf(attr->name, sizeof(attr->name), "freq%d_label", s);
804+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL,
805+
0, i);
806+
attr++;
807+
808+
snprintf(attr->name, sizeof(attr->name), "freq%d_input", s);
809+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL,
810+
1, i);
811+
attr++;
812+
}
813+
814+
if (sensors->power.version == 0xA0) {
815+
/*
816+
* Special case for many-attribute power sensor. Split it into
817+
* a sensor number per power type, emulating several sensors.
818+
*/
819+
for (i = 0; i < sensors->power.num_sensors; ++i) {
820+
unsigned int j;
821+
unsigned int nr = 0;
822+
823+
s = (i * 4) + 1;
824+
825+
for (j = 0; j < 4; ++j) {
826+
snprintf(attr->name, sizeof(attr->name),
827+
"power%d_label", s);
828+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
829+
show_power, NULL,
830+
nr++, i);
831+
attr++;
832+
833+
snprintf(attr->name, sizeof(attr->name),
834+
"power%d_average", s);
835+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
836+
show_power, NULL,
837+
nr++, i);
838+
attr++;
839+
840+
snprintf(attr->name, sizeof(attr->name),
841+
"power%d_average_interval", s);
842+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
843+
show_power, NULL,
844+
nr++, i);
845+
attr++;
846+
847+
snprintf(attr->name, sizeof(attr->name),
848+
"power%d_input", s);
849+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
850+
show_power, NULL,
851+
nr++, i);
852+
attr++;
853+
854+
s++;
855+
}
856+
}
857+
} else {
858+
for (i = 0; i < sensors->power.num_sensors; ++i) {
859+
s = i + 1;
860+
861+
snprintf(attr->name, sizeof(attr->name),
862+
"power%d_label", s);
863+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
864+
show_power, NULL, 0, i);
865+
attr++;
866+
867+
snprintf(attr->name, sizeof(attr->name),
868+
"power%d_average", s);
869+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
870+
show_power, NULL, 1, i);
871+
attr++;
872+
873+
snprintf(attr->name, sizeof(attr->name),
874+
"power%d_average_interval", s);
875+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
876+
show_power, NULL, 2, i);
877+
attr++;
878+
879+
snprintf(attr->name, sizeof(attr->name),
880+
"power%d_input", s);
881+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
882+
show_power, NULL, 3, i);
883+
attr++;
884+
}
885+
}
886+
887+
if (sensors->caps.num_sensors >= 1) {
888+
s = sensors->power.num_sensors + 1;
889+
890+
snprintf(attr->name, sizeof(attr->name), "power%d_label", s);
891+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
892+
0, 0);
893+
attr++;
894+
895+
snprintf(attr->name, sizeof(attr->name), "power%d_cap", s);
896+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
897+
1, 0);
898+
attr++;
899+
900+
snprintf(attr->name, sizeof(attr->name), "power%d_input", s);
901+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
902+
2, 0);
903+
attr++;
904+
905+
snprintf(attr->name, sizeof(attr->name),
906+
"power%d_cap_not_redundant", s);
907+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
908+
3, 0);
909+
attr++;
910+
911+
snprintf(attr->name, sizeof(attr->name), "power%d_cap_max", s);
912+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
913+
4, 0);
914+
attr++;
915+
916+
snprintf(attr->name, sizeof(attr->name), "power%d_cap_min", s);
917+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
918+
5, 0);
919+
attr++;
920+
921+
snprintf(attr->name, sizeof(attr->name), "power%d_cap_user",
922+
s);
923+
attr->sensor = OCC_INIT_ATTR(attr->name, 0644, show_caps,
924+
occ_store_caps_user, 6, 0);
925+
attr++;
926+
927+
if (sensors->caps.version > 1) {
928+
snprintf(attr->name, sizeof(attr->name),
929+
"power%d_cap_user_source", s);
930+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
931+
show_caps, NULL, 7, 0);
932+
attr++;
933+
}
934+
}
935+
936+
for (i = 0; i < sensors->extended.num_sensors; ++i) {
937+
s = i + 1;
938+
939+
snprintf(attr->name, sizeof(attr->name), "extn%d_label", s);
940+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
941+
occ_show_extended, NULL, 0, i);
942+
attr++;
943+
944+
snprintf(attr->name, sizeof(attr->name), "extn%d_flags", s);
945+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
946+
occ_show_extended, NULL, 1, i);
947+
attr++;
948+
949+
snprintf(attr->name, sizeof(attr->name), "extn%d_input", s);
950+
attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
951+
occ_show_extended, NULL, 2, i);
952+
attr++;
953+
}
954+
955+
/* put the sensors in the group */
956+
for (i = 0; i < num_attrs; ++i) {
957+
sysfs_attr_init(&occ->attrs[i].sensor.dev_attr.attr);
958+
occ->group.attrs[i] = &occ->attrs[i].sensor.dev_attr.attr;
959+
}
960+
961+
return 0;
962+
}
963+
644964
/* only need to do this once at startup, as OCC won't change sensors on us */
645965
static void occ_parse_poll_response(struct occ *occ)
646966
{
@@ -704,6 +1024,7 @@ int occ_setup(struct occ *occ, const char *name)
7041024
int rc;
7051025

7061026
mutex_init(&occ->lock);
1027+
occ->groups[0] = &occ->group;
7071028

7081029
/* no need to lock */
7091030
rc = occ_poll(occ);
@@ -718,5 +1039,21 @@ int occ_setup(struct occ *occ, const char *name)
7181039

7191040
occ_parse_poll_response(occ);
7201041

1042+
rc = occ_setup_sensor_attrs(occ);
1043+
if (rc) {
1044+
dev_err(occ->bus_dev, "failed to setup sensor attrs: %d\n",
1045+
rc);
1046+
return rc;
1047+
}
1048+
1049+
occ->hwmon = devm_hwmon_device_register_with_groups(occ->bus_dev, name,
1050+
occ, occ->groups);
1051+
if (IS_ERR(occ->hwmon)) {
1052+
rc = PTR_ERR(occ->hwmon);
1053+
dev_err(occ->bus_dev, "failed to register hwmon device: %d\n",
1054+
rc);
1055+
return rc;
1056+
}
1057+
7211058
return 0;
7221059
}

0 commit comments

Comments
 (0)