Merge branch 'i2c/for-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa...
[linux-2.6-microblaze.git] / drivers / hwmon / pmbus / bpa-rs600.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Hardware monitoring driver for BluTek BPA-RS600 Power Supplies
4  *
5  * Copyright 2021 Allied Telesis Labs
6  */
7
8 #include <linux/i2c.h>
9 #include <linux/init.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/pmbus.h>
13 #include "pmbus.h"
14
15 #define BPARS600_MFR_VIN_MIN    0xa0
16 #define BPARS600_MFR_VIN_MAX    0xa1
17 #define BPARS600_MFR_IIN_MAX    0xa2
18 #define BPARS600_MFR_PIN_MAX    0xa3
19 #define BPARS600_MFR_VOUT_MIN   0xa4
20 #define BPARS600_MFR_VOUT_MAX   0xa5
21 #define BPARS600_MFR_IOUT_MAX   0xa6
22 #define BPARS600_MFR_POUT_MAX   0xa7
23
24 static int bpa_rs600_read_byte_data(struct i2c_client *client, int page, int reg)
25 {
26         int ret;
27
28         if (page > 0)
29                 return -ENXIO;
30
31         switch (reg) {
32         case PMBUS_FAN_CONFIG_12:
33                 /*
34                  * Two fans are reported in PMBUS_FAN_CONFIG_12 but there is
35                  * only one fan in the module. Mask out the FAN2 bits.
36                  */
37                 ret = pmbus_read_byte_data(client, 0, PMBUS_FAN_CONFIG_12);
38                 if (ret >= 0)
39                         ret &= ~(PB_FAN_2_INSTALLED | PB_FAN_2_PULSE_MASK);
40                 break;
41         default:
42                 ret = -ENODATA;
43                 break;
44         }
45
46         return ret;
47 }
48
49 static int bpa_rs600_read_word_data(struct i2c_client *client, int page, int phase, int reg)
50 {
51         int ret;
52
53         if (page > 0)
54                 return -ENXIO;
55
56         switch (reg) {
57         case PMBUS_VIN_UV_WARN_LIMIT:
58                 ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VIN_MIN);
59                 break;
60         case PMBUS_VIN_OV_WARN_LIMIT:
61                 ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VIN_MAX);
62                 break;
63         case PMBUS_VOUT_UV_WARN_LIMIT:
64                 ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VOUT_MIN);
65                 break;
66         case PMBUS_VOUT_OV_WARN_LIMIT:
67                 ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_VOUT_MAX);
68                 break;
69         case PMBUS_IIN_OC_WARN_LIMIT:
70                 ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_IIN_MAX);
71                 break;
72         case PMBUS_IOUT_OC_WARN_LIMIT:
73                 ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_IOUT_MAX);
74                 break;
75         case PMBUS_PIN_OP_WARN_LIMIT:
76                 ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_PIN_MAX);
77                 break;
78         case PMBUS_POUT_OP_WARN_LIMIT:
79                 ret = pmbus_read_word_data(client, 0, 0xff, BPARS600_MFR_POUT_MAX);
80                 break;
81         case PMBUS_VIN_UV_FAULT_LIMIT:
82         case PMBUS_VIN_OV_FAULT_LIMIT:
83         case PMBUS_VOUT_UV_FAULT_LIMIT:
84         case PMBUS_VOUT_OV_FAULT_LIMIT:
85                 /* These commands return data but it is invalid/un-documented */
86                 ret = -ENXIO;
87                 break;
88         default:
89                 if (reg >= PMBUS_VIRT_BASE)
90                         ret = -ENXIO;
91                 else
92                         ret = -ENODATA;
93                 break;
94         }
95
96         return ret;
97 }
98
99 static struct pmbus_driver_info bpa_rs600_info = {
100         .pages = 1,
101         .format[PSC_VOLTAGE_IN] = linear,
102         .format[PSC_VOLTAGE_OUT] = linear,
103         .format[PSC_CURRENT_IN] = linear,
104         .format[PSC_CURRENT_OUT] = linear,
105         .format[PSC_POWER] = linear,
106         .format[PSC_TEMPERATURE] = linear,
107         .format[PSC_FAN] = linear,
108         .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT |
109                 PMBUS_HAVE_IIN | PMBUS_HAVE_IOUT |
110                 PMBUS_HAVE_PIN | PMBUS_HAVE_POUT |
111                 PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 |
112                 PMBUS_HAVE_FAN12 |
113                 PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT |
114                 PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP |
115                 PMBUS_HAVE_STATUS_FAN12,
116         .read_byte_data = bpa_rs600_read_byte_data,
117         .read_word_data = bpa_rs600_read_word_data,
118 };
119
120 static int bpa_rs600_probe(struct i2c_client *client)
121 {
122         struct device *dev = &client->dev;
123         u8 buf[I2C_SMBUS_BLOCK_MAX + 1];
124         int ret;
125
126         if (!i2c_check_functionality(client->adapter,
127                                      I2C_FUNC_SMBUS_READ_BYTE_DATA
128                                      | I2C_FUNC_SMBUS_READ_WORD_DATA
129                                      | I2C_FUNC_SMBUS_READ_BLOCK_DATA))
130                 return -ENODEV;
131
132         ret = i2c_smbus_read_block_data(client, PMBUS_MFR_MODEL, buf);
133         if (ret < 0) {
134                 dev_err(dev, "Failed to read Manufacturer Model\n");
135                 return ret;
136         }
137
138         if (strncmp(buf, "BPA-RS600", 8)) {
139                 buf[ret] = '\0';
140                 dev_err(dev, "Unsupported Manufacturer Model '%s'\n", buf);
141                 return -ENODEV;
142         }
143
144         return pmbus_do_probe(client, &bpa_rs600_info);
145 }
146
147 static const struct i2c_device_id bpa_rs600_id[] = {
148         { "bpars600", 0 },
149         {},
150 };
151 MODULE_DEVICE_TABLE(i2c, bpa_rs600_id);
152
153 static const struct of_device_id __maybe_unused bpa_rs600_of_match[] = {
154         { .compatible = "blutek,bpa-rs600" },
155         {},
156 };
157 MODULE_DEVICE_TABLE(of, bpa_rs600_of_match);
158
159 static struct i2c_driver bpa_rs600_driver = {
160         .driver = {
161                 .name = "bpa-rs600",
162                 .of_match_table = of_match_ptr(bpa_rs600_of_match),
163         },
164         .probe_new = bpa_rs600_probe,
165         .id_table = bpa_rs600_id,
166 };
167
168 module_i2c_driver(bpa_rs600_driver);
169
170 MODULE_AUTHOR("Chris Packham");
171 MODULE_DESCRIPTION("PMBus driver for BluTek BPA-RS600");
172 MODULE_LICENSE("GPL");
173 MODULE_IMPORT_NS(PMBUS);