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, command: reg, values: data); |
66 | if (rc < 0) |
67 | return rc; |
68 | |
69 | /* filled with printable characters, ending with # */ |
70 | p = memscan(p: data, c: '#', size: rc); |
71 | *p = '\0'; |
72 | |
73 | return sysfs_emit(buf, fmt: "%s\n" , data); |
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, command: reg, values: data); |
88 | if (rc < 0) |
89 | return rc; |
90 | |
91 | if (rc != 6) |
92 | return -EPROTO; |
93 | |
94 | return sysfs_emit(buf, fmt: "%u.%02u%u-%u.%02u\n" , |
95 | data[1], data[2]/* < 100 */, data[3]/*< 10*/, |
96 | data[4], data[5]/* < 100 */); |
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, command: reg); |
109 | if (rc < 0) |
110 | return rc; |
111 | |
112 | switch (rc) { |
113 | case MODE_ACTIVE: |
114 | return sysfs_emit(buf, fmt: "[%s] %s %s\n" , |
115 | MODE_ACTIVE_STRING, |
116 | MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); |
117 | case MODE_STANDBY: |
118 | return sysfs_emit(buf, fmt: "%s [%s] %s\n" , |
119 | MODE_ACTIVE_STRING, |
120 | MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); |
121 | case MODE_REDUNDANCY: |
122 | return sysfs_emit(buf, fmt: "%s %s [%s]\n" , |
123 | MODE_ACTIVE_STRING, |
124 | MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); |
125 | default: |
126 | return sysfs_emit(buf, fmt: "unspecified\n" ); |
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, s2: buf)) { |
141 | rc = i2c_smbus_write_byte_data(client, command: reg, |
142 | MODE_STANDBY); |
143 | if (rc < 0) |
144 | return rc; |
145 | return count; |
146 | } else if (sysfs_streq(MODE_ACTIVE_STRING, s2: buf)) { |
147 | rc = i2c_smbus_write_byte_data(client, command: 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 | |
193 | static int ipsps_probe(struct i2c_client *client) |
194 | { |
195 | client->dev.platform_data = &ipsps_pdata; |
196 | return pmbus_do_probe(client, info: &ipsps_info); |
197 | } |
198 | |
199 | static const struct i2c_device_id ipsps_id[] = { |
200 | { "ipsps1" , 0 }, |
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 | }, |
218 | .probe = ipsps_probe, |
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" ); |
227 | MODULE_IMPORT_NS(PMBUS); |
228 | |