Commit | Line | Data |
---|---|---|
edd2a4d6 JW |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* | |
3 | * Copyright 2019 Inspur Corp. | |
4 | */ | |
5 | ||
6 | #include <linux/debugfs.h> | |
7 | #include <linux/device.h> | |
8 | #include <linux/fs.h> | |
9 | #include <linux/i2c.h> | |
10 | #include <linux/module.h> | |
11 | #include <linux/pmbus.h> | |
12 | #include <linux/hwmon-sysfs.h> | |
13 | ||
14 | #include "pmbus.h" | |
15 | ||
16 | #define IPSPS_REG_VENDOR_ID 0x99 | |
17 | #define IPSPS_REG_MODEL 0x9A | |
18 | #define IPSPS_REG_FW_VERSION 0x9B | |
19 | #define IPSPS_REG_PN 0x9C | |
20 | #define IPSPS_REG_SN 0x9E | |
21 | #define IPSPS_REG_HW_VERSION 0xB0 | |
22 | #define IPSPS_REG_MODE 0xFC | |
23 | ||
24 | #define MODE_ACTIVE 0x55 | |
25 | #define MODE_STANDBY 0x0E | |
26 | #define MODE_REDUNDANCY 0x00 | |
27 | ||
28 | #define MODE_ACTIVE_STRING "active" | |
29 | #define MODE_STANDBY_STRING "standby" | |
30 | #define MODE_REDUNDANCY_STRING "redundancy" | |
31 | ||
32 | enum ipsps_index { | |
33 | vendor, | |
34 | model, | |
35 | fw_version, | |
36 | part_number, | |
37 | serial_number, | |
38 | hw_version, | |
39 | mode, | |
40 | num_regs, | |
41 | }; | |
42 | ||
43 | static const u8 ipsps_regs[num_regs] = { | |
44 | [vendor] = IPSPS_REG_VENDOR_ID, | |
45 | [model] = IPSPS_REG_MODEL, | |
46 | [fw_version] = IPSPS_REG_FW_VERSION, | |
47 | [part_number] = IPSPS_REG_PN, | |
48 | [serial_number] = IPSPS_REG_SN, | |
49 | [hw_version] = IPSPS_REG_HW_VERSION, | |
50 | [mode] = IPSPS_REG_MODE, | |
51 | }; | |
52 | ||
53 | static ssize_t ipsps_string_show(struct device *dev, | |
54 | struct device_attribute *devattr, | |
55 | char *buf) | |
56 | { | |
57 | u8 reg; | |
58 | int rc; | |
59 | char *p; | |
60 | char data[I2C_SMBUS_BLOCK_MAX + 1]; | |
61 | struct i2c_client *client = to_i2c_client(dev->parent); | |
62 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | |
63 | ||
64 | reg = ipsps_regs[attr->index]; | |
65 | rc = i2c_smbus_read_block_data(client, reg, data); | |
66 | if (rc < 0) | |
67 | return rc; | |
68 | ||
69 | /* filled with printable characters, ending with # */ | |
70 | p = memscan(data, '#', rc); | |
71 | *p = '\0'; | |
72 | ||
1f4d4af4 | 73 | return sysfs_emit(buf, "%s\n", data); |
edd2a4d6 JW |
74 | } |
75 | ||
76 | static ssize_t ipsps_fw_version_show(struct device *dev, | |
77 | struct device_attribute *devattr, | |
78 | char *buf) | |
79 | { | |
80 | u8 reg; | |
81 | int rc; | |
82 | u8 data[I2C_SMBUS_BLOCK_MAX] = { 0 }; | |
83 | struct i2c_client *client = to_i2c_client(dev->parent); | |
84 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | |
85 | ||
86 | reg = ipsps_regs[attr->index]; | |
87 | rc = i2c_smbus_read_block_data(client, reg, data); | |
88 | if (rc < 0) | |
89 | return rc; | |
90 | ||
91 | if (rc != 6) | |
92 | return -EPROTO; | |
93 | ||
1f4d4af4 GR |
94 | return sysfs_emit(buf, "%u.%02u%u-%u.%02u\n", |
95 | data[1], data[2]/* < 100 */, data[3]/*< 10*/, | |
96 | data[4], data[5]/* < 100 */); | |
edd2a4d6 JW |
97 | } |
98 | ||
99 | static ssize_t ipsps_mode_show(struct device *dev, | |
100 | struct device_attribute *devattr, char *buf) | |
101 | { | |
102 | u8 reg; | |
103 | int rc; | |
104 | struct i2c_client *client = to_i2c_client(dev->parent); | |
105 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | |
106 | ||
107 | reg = ipsps_regs[attr->index]; | |
108 | rc = i2c_smbus_read_byte_data(client, reg); | |
109 | if (rc < 0) | |
110 | return rc; | |
111 | ||
112 | switch (rc) { | |
113 | case MODE_ACTIVE: | |
1f4d4af4 GR |
114 | return sysfs_emit(buf, "[%s] %s %s\n", |
115 | MODE_ACTIVE_STRING, | |
116 | MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); | |
edd2a4d6 | 117 | case MODE_STANDBY: |
1f4d4af4 GR |
118 | return sysfs_emit(buf, "%s [%s] %s\n", |
119 | MODE_ACTIVE_STRING, | |
120 | MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); | |
edd2a4d6 | 121 | case MODE_REDUNDANCY: |
1f4d4af4 GR |
122 | return sysfs_emit(buf, "%s %s [%s]\n", |
123 | MODE_ACTIVE_STRING, | |
124 | MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); | |
edd2a4d6 | 125 | default: |
1f4d4af4 | 126 | return sysfs_emit(buf, "unspecified\n"); |
edd2a4d6 JW |
127 | } |
128 | } | |
129 | ||
130 | static ssize_t ipsps_mode_store(struct device *dev, | |
131 | struct device_attribute *devattr, | |
132 | const char *buf, size_t count) | |
133 | { | |
134 | u8 reg; | |
135 | int rc; | |
136 | struct i2c_client *client = to_i2c_client(dev->parent); | |
137 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | |
138 | ||
139 | reg = ipsps_regs[attr->index]; | |
140 | if (sysfs_streq(MODE_STANDBY_STRING, buf)) { | |
141 | rc = i2c_smbus_write_byte_data(client, reg, | |
142 | MODE_STANDBY); | |
143 | if (rc < 0) | |
144 | return rc; | |
145 | return count; | |
146 | } else if (sysfs_streq(MODE_ACTIVE_STRING, buf)) { | |
147 | rc = i2c_smbus_write_byte_data(client, reg, | |
148 | MODE_ACTIVE); | |
149 | if (rc < 0) | |
150 | return rc; | |
151 | return count; | |
152 | } | |
153 | ||
154 | return -EINVAL; | |
155 | } | |
156 | ||
157 | static SENSOR_DEVICE_ATTR_RO(vendor, ipsps_string, vendor); | |
158 | static SENSOR_DEVICE_ATTR_RO(model, ipsps_string, model); | |
159 | static SENSOR_DEVICE_ATTR_RO(part_number, ipsps_string, part_number); | |
160 | static SENSOR_DEVICE_ATTR_RO(serial_number, ipsps_string, serial_number); | |
161 | static SENSOR_DEVICE_ATTR_RO(hw_version, ipsps_string, hw_version); | |
162 | static SENSOR_DEVICE_ATTR_RO(fw_version, ipsps_fw_version, fw_version); | |
163 | static SENSOR_DEVICE_ATTR_RW(mode, ipsps_mode, mode); | |
164 | ||
165 | static struct attribute *ipsps_attrs[] = { | |
166 | &sensor_dev_attr_vendor.dev_attr.attr, | |
167 | &sensor_dev_attr_model.dev_attr.attr, | |
168 | &sensor_dev_attr_part_number.dev_attr.attr, | |
169 | &sensor_dev_attr_serial_number.dev_attr.attr, | |
170 | &sensor_dev_attr_hw_version.dev_attr.attr, | |
171 | &sensor_dev_attr_fw_version.dev_attr.attr, | |
172 | &sensor_dev_attr_mode.dev_attr.attr, | |
173 | NULL, | |
174 | }; | |
175 | ||
176 | ATTRIBUTE_GROUPS(ipsps); | |
177 | ||
178 | static struct pmbus_driver_info ipsps_info = { | |
179 | .pages = 1, | |
180 | .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | | |
181 | PMBUS_HAVE_IIN | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | | |
182 | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | | |
183 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT | | |
184 | PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | | |
185 | PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12, | |
186 | .groups = ipsps_groups, | |
187 | }; | |
188 | ||
189 | static struct pmbus_platform_data ipsps_pdata = { | |
190 | .flags = PMBUS_SKIP_STATUS_CHECK, | |
191 | }; | |
192 | ||
dd431939 | 193 | static int ipsps_probe(struct i2c_client *client) |
edd2a4d6 JW |
194 | { |
195 | client->dev.platform_data = &ipsps_pdata; | |
dd431939 | 196 | return pmbus_do_probe(client, &ipsps_info); |
edd2a4d6 JW |
197 | } |
198 | ||
199 | static const struct i2c_device_id ipsps_id[] = { | |
d8a66f36 | 200 | { "ipsps1" }, |
edd2a4d6 JW |
201 | {} |
202 | }; | |
203 | MODULE_DEVICE_TABLE(i2c, ipsps_id); | |
204 | ||
205 | #ifdef CONFIG_OF | |
206 | static const struct of_device_id ipsps_of_match[] = { | |
207 | { .compatible = "inspur,ipsps1" }, | |
208 | {} | |
209 | }; | |
210 | MODULE_DEVICE_TABLE(of, ipsps_of_match); | |
211 | #endif | |
212 | ||
213 | static struct i2c_driver ipsps_driver = { | |
214 | .driver = { | |
215 | .name = "inspur-ipsps", | |
216 | .of_match_table = of_match_ptr(ipsps_of_match), | |
217 | }, | |
1975d167 | 218 | .probe = ipsps_probe, |
edd2a4d6 JW |
219 | .id_table = ipsps_id, |
220 | }; | |
221 | ||
222 | module_i2c_driver(ipsps_driver); | |
223 | ||
224 | MODULE_AUTHOR("John Wang"); | |
225 | MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies"); | |
226 | MODULE_LICENSE("GPL"); | |
b94ca77e | 227 | MODULE_IMPORT_NS(PMBUS); |