1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * nct6775-i2c - I2C driver for the hardware monitoring functionality of |
4 | * Nuvoton NCT677x Super-I/O chips |
5 | * |
6 | * Copyright (C) 2022 Zev Weiss <zev@bewilderbeest.net> |
7 | * |
8 | * This driver interacts with the chip via it's "back door" i2c interface, as |
9 | * is often exposed to a BMC. Because the host may still be operating the |
10 | * chip via the ("front door") LPC interface, this driver cannot assume that |
11 | * it actually has full control of the chip, and in particular must avoid |
12 | * making any changes that could confuse the host's LPC usage of it. It thus |
13 | * operates in a strictly read-only fashion, with the only exception being the |
14 | * bank-select register (which seems, thankfully, to be replicated for the i2c |
15 | * interface so it doesn't affect the LPC interface). |
16 | */ |
17 | |
18 | #include <linux/module.h> |
19 | #include <linux/init.h> |
20 | #include <linux/i2c.h> |
21 | #include <linux/hwmon.h> |
22 | #include <linux/hwmon-sysfs.h> |
23 | #include <linux/err.h> |
24 | #include <linux/of.h> |
25 | #include <linux/regmap.h> |
26 | #include "nct6775.h" |
27 | |
28 | static int nct6775_i2c_read(void *ctx, unsigned int reg, unsigned int *val) |
29 | { |
30 | int ret; |
31 | u32 tmp; |
32 | u8 bank = reg >> 8; |
33 | struct nct6775_data *data = ctx; |
34 | struct i2c_client *client = data->driver_data; |
35 | |
36 | if (bank != data->bank) { |
37 | ret = i2c_smbus_write_byte_data(client, NCT6775_REG_BANK, value: bank); |
38 | if (ret) |
39 | return ret; |
40 | data->bank = bank; |
41 | } |
42 | |
43 | ret = i2c_smbus_read_byte_data(client, command: reg & 0xff); |
44 | if (ret < 0) |
45 | return ret; |
46 | tmp = ret; |
47 | |
48 | if (nct6775_reg_is_word_sized(data, reg)) { |
49 | ret = i2c_smbus_read_byte_data(client, command: (reg & 0xff) + 1); |
50 | if (ret < 0) |
51 | return ret; |
52 | tmp = (tmp << 8) | ret; |
53 | } |
54 | |
55 | *val = tmp; |
56 | return 0; |
57 | } |
58 | |
59 | /* |
60 | * The write operation is a dummy so as not to disturb anything being done |
61 | * with the chip via LPC. |
62 | */ |
63 | static int nct6775_i2c_write(void *ctx, unsigned int reg, unsigned int value) |
64 | { |
65 | struct nct6775_data *data = ctx; |
66 | struct i2c_client *client = data->driver_data; |
67 | |
68 | dev_dbg(&client->dev, "skipping attempted write: %02x -> %03x\n" , value, reg); |
69 | |
70 | /* |
71 | * This is a lie, but writing anything but the bank-select register is |
72 | * something this driver shouldn't be doing. |
73 | */ |
74 | return 0; |
75 | } |
76 | |
77 | static const struct of_device_id __maybe_unused nct6775_i2c_of_match[] = { |
78 | { .compatible = "nuvoton,nct6106" , .data = (void *)nct6106, }, |
79 | { .compatible = "nuvoton,nct6116" , .data = (void *)nct6116, }, |
80 | { .compatible = "nuvoton,nct6775" , .data = (void *)nct6775, }, |
81 | { .compatible = "nuvoton,nct6776" , .data = (void *)nct6776, }, |
82 | { .compatible = "nuvoton,nct6779" , .data = (void *)nct6779, }, |
83 | { .compatible = "nuvoton,nct6791" , .data = (void *)nct6791, }, |
84 | { .compatible = "nuvoton,nct6792" , .data = (void *)nct6792, }, |
85 | { .compatible = "nuvoton,nct6793" , .data = (void *)nct6793, }, |
86 | { .compatible = "nuvoton,nct6795" , .data = (void *)nct6795, }, |
87 | { .compatible = "nuvoton,nct6796" , .data = (void *)nct6796, }, |
88 | { .compatible = "nuvoton,nct6797" , .data = (void *)nct6797, }, |
89 | { .compatible = "nuvoton,nct6798" , .data = (void *)nct6798, }, |
90 | { .compatible = "nuvoton,nct6799" , .data = (void *)nct6799, }, |
91 | { }, |
92 | }; |
93 | MODULE_DEVICE_TABLE(of, nct6775_i2c_of_match); |
94 | |
95 | static const struct i2c_device_id nct6775_i2c_id[] = { |
96 | { "nct6106" , nct6106 }, |
97 | { "nct6116" , nct6116 }, |
98 | { "nct6775" , nct6775 }, |
99 | { "nct6776" , nct6776 }, |
100 | { "nct6779" , nct6779 }, |
101 | { "nct6791" , nct6791 }, |
102 | { "nct6792" , nct6792 }, |
103 | { "nct6793" , nct6793 }, |
104 | { "nct6795" , nct6795 }, |
105 | { "nct6796" , nct6796 }, |
106 | { "nct6797" , nct6797 }, |
107 | { "nct6798" , nct6798 }, |
108 | { "nct6799" , nct6799 }, |
109 | { } |
110 | }; |
111 | MODULE_DEVICE_TABLE(i2c, nct6775_i2c_id); |
112 | |
113 | static int nct6775_i2c_probe_init(struct nct6775_data *data) |
114 | { |
115 | u32 tsi_channel_mask; |
116 | struct i2c_client *client = data->driver_data; |
117 | |
118 | /* |
119 | * The i2c interface doesn't provide access to the control registers |
120 | * needed to determine the presence of other fans, but fans 1 and 2 |
121 | * are (in principle) always there. |
122 | * |
123 | * In practice this is perhaps a little silly, because the system |
124 | * using this driver is mostly likely a BMC, and hence probably has |
125 | * totally separate fan tachs & pwms of its own that are actually |
126 | * controlling/monitoring the fans -- these are thus unlikely to be |
127 | * doing anything actually useful. |
128 | */ |
129 | data->has_fan = 0x03; |
130 | data->has_fan_min = 0x03; |
131 | data->has_pwm = 0x03; |
132 | |
133 | /* |
134 | * Because on a BMC this driver may be bound very shortly after power |
135 | * is first applied to the device, the automatic TSI channel detection |
136 | * in nct6775_probe() (which has already been run at this point) may |
137 | * not find anything if a channel hasn't yet produced a temperature |
138 | * reading. Augment whatever was found via autodetection (if |
139 | * anything) with the channels DT says should be active. |
140 | */ |
141 | if (!of_property_read_u32(np: client->dev.of_node, propname: "nuvoton,tsi-channel-mask" , |
142 | out_value: &tsi_channel_mask)) |
143 | data->have_tsi_temp |= tsi_channel_mask & GENMASK(NUM_TSI_TEMP - 1, 0); |
144 | |
145 | return 0; |
146 | } |
147 | |
148 | static const struct regmap_config nct6775_i2c_regmap_config = { |
149 | .reg_bits = 16, |
150 | .val_bits = 16, |
151 | .reg_read = nct6775_i2c_read, |
152 | .reg_write = nct6775_i2c_write, |
153 | }; |
154 | |
155 | static int nct6775_i2c_probe(struct i2c_client *client) |
156 | { |
157 | struct nct6775_data *data; |
158 | struct device *dev = &client->dev; |
159 | |
160 | data = devm_kzalloc(dev: &client->dev, size: sizeof(*data), GFP_KERNEL); |
161 | if (!data) |
162 | return -ENOMEM; |
163 | |
164 | data->kind = (enum kinds)(uintptr_t)i2c_get_match_data(client); |
165 | data->read_only = true; |
166 | data->driver_data = client; |
167 | data->driver_init = nct6775_i2c_probe_init; |
168 | |
169 | return nct6775_probe(dev, data, regmapcfg: &nct6775_i2c_regmap_config); |
170 | } |
171 | |
172 | static struct i2c_driver nct6775_i2c_driver = { |
173 | .class = I2C_CLASS_HWMON, |
174 | .driver = { |
175 | .name = "nct6775-i2c" , |
176 | .of_match_table = of_match_ptr(nct6775_i2c_of_match), |
177 | }, |
178 | .probe = nct6775_i2c_probe, |
179 | .id_table = nct6775_i2c_id, |
180 | }; |
181 | |
182 | module_i2c_driver(nct6775_i2c_driver); |
183 | |
184 | MODULE_AUTHOR("Zev Weiss <zev@bewilderbeest.net>" ); |
185 | MODULE_DESCRIPTION("I2C driver for NCT6775F and compatible chips" ); |
186 | MODULE_LICENSE("GPL" ); |
187 | MODULE_IMPORT_NS(HWMON_NCT6775); |
188 | |