Commit 920fa1ffce48d36d7e7bc54eb38c58313bc0a667
Committed by
Linus Torvalds
1 parent
cfa3b24c38
hwmon: driver for SMM665 Six-Channel Active DC Output Controller/Monitor
This driver adds support for the monitoring features of the Summit Microelectronics SMM665 Six-Channel Active DC Output Controller/Monitor. Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk> Cc: Jean Delvare <khali@linux-fr.org> Cc: Hans de Goede <hdegoede@redhat.com> Cc: Mark Brown <broonie@opensource.wolfsonmicro.com> Cc: Samuel Ortiz <sameo@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Showing 5 changed files with 923 additions and 0 deletions Side-by-side Diff
Documentation/hwmon/smm665
1 | +Kernel driver smm665 | |
2 | +==================== | |
3 | + | |
4 | +Supported chips: | |
5 | + * Summit Microelectronics SMM465 | |
6 | + Prefix: 'smm465' | |
7 | + Addresses scanned: - | |
8 | + Datasheet: | |
9 | + http://www.summitmicro.com/prod_select/summary/SMM465/SMM465DS.pdf | |
10 | + * Summit Microelectronics SMM665, SMM665B | |
11 | + Prefix: 'smm665' | |
12 | + Addresses scanned: - | |
13 | + Datasheet: | |
14 | + http://www.summitmicro.com/prod_select/summary/SMM665/SMM665B_2089_20.pdf | |
15 | + * Summit Microelectronics SMM665C | |
16 | + Prefix: 'smm665c' | |
17 | + Addresses scanned: - | |
18 | + Datasheet: | |
19 | + http://www.summitmicro.com/prod_select/summary/SMM665C/SMM665C_2125.pdf | |
20 | + * Summit Microelectronics SMM764 | |
21 | + Prefix: 'smm764' | |
22 | + Addresses scanned: - | |
23 | + Datasheet: | |
24 | + http://www.summitmicro.com/prod_select/summary/SMM764/SMM764_2098.pdf | |
25 | + * Summit Microelectronics SMM766, SMM766B | |
26 | + Prefix: 'smm766' | |
27 | + Addresses scanned: - | |
28 | + Datasheets: | |
29 | + http://www.summitmicro.com/prod_select/summary/SMM766/SMM766_2086.pdf | |
30 | + http://www.summitmicro.com/prod_select/summary/SMM766B/SMM766B_2122.pdf | |
31 | + | |
32 | +Author: Guenter Roeck <guenter.roeck@ericsson.com> | |
33 | + | |
34 | + | |
35 | +Module Parameters | |
36 | +----------------- | |
37 | + | |
38 | +* vref: int | |
39 | + Default: 1250 (mV) | |
40 | + Reference voltage on VREF_ADC pin in mV. It should not be necessary to set | |
41 | + this parameter unless a non-default reference voltage is used. | |
42 | + | |
43 | + | |
44 | +Description | |
45 | +----------- | |
46 | + | |
47 | +[From datasheet] The SMM665 is an Active DC Output power supply Controller | |
48 | +that monitors, margins and cascade sequences power. The part monitors six | |
49 | +power supply channels as well as VDD, 12V input, two general-purpose analog | |
50 | +inputs and an internal temperature sensor using a 10-bit ADC. | |
51 | + | |
52 | +Each monitored channel has its own high and low limits, plus a critical | |
53 | +limit. | |
54 | + | |
55 | +Support for SMM465, SMM764, and SMM766 has been implemented but is untested. | |
56 | + | |
57 | + | |
58 | +Usage Notes | |
59 | +----------- | |
60 | + | |
61 | +This driver does not probe for devices, since there is no register which | |
62 | +can be safely used to identify the chip. You will have to instantiate | |
63 | +the devices explicitly. When instantiating the device, you have to specify | |
64 | +its configuration register address. | |
65 | + | |
66 | +Example: the following will load the driver for an SMM665 at address 0x57 | |
67 | +on I2C bus #1: | |
68 | +$ modprobe smm665 | |
69 | +$ echo smm665 0x57 > /sys/bus/i2c/devices/i2c-1/new_device | |
70 | + | |
71 | + | |
72 | +Sysfs entries | |
73 | +------------- | |
74 | + | |
75 | +This driver uses the values in the datasheet to convert ADC register values | |
76 | +into the values specified in the sysfs-interface document. All attributes are | |
77 | +read only. | |
78 | + | |
79 | +Min, max, lcrit, and crit values are used by the chip to trigger external signals | |
80 | +and/or other activity. Triggered signals can include HEALTHY, RST, Power Off, | |
81 | +or Fault depending on the chip configuration. The driver reports values as lcrit | |
82 | +or crit if exceeding the limits triggers RST, Power Off, or Fault, and as min or | |
83 | +max otherwise. For details please see the SMM665 datasheet. | |
84 | + | |
85 | +For SMM465 and SMM764, values for Channel E and F are reported but undefined. | |
86 | + | |
87 | +in1_input 12V input voltage (mV) | |
88 | +in2_input 3.3V (VDD) input voltage (mV) | |
89 | +in3_input Channel A voltage (mV) | |
90 | +in4_input Channel B voltage (mV) | |
91 | +in5_input Channel C voltage (mV) | |
92 | +in6_input Channel D voltage (mV) | |
93 | +in7_input Channel E voltage (mV) | |
94 | +in8_input Channel F voltage (mV) | |
95 | +in9_input AIN1 voltage (mV) | |
96 | +in10_input AIN2 voltage (mV) | |
97 | + | |
98 | +in1_min 12v input minimum voltage (mV) | |
99 | +in2_min 3.3V (VDD) input minimum voltage (mV) | |
100 | +in3_min Channel A minimum voltage (mV) | |
101 | +in4_min Channel B minimum voltage (mV) | |
102 | +in5_min Channel C minimum voltage (mV) | |
103 | +in6_min Channel D minimum voltage (mV) | |
104 | +in7_min Channel E minimum voltage (mV) | |
105 | +in8_min Channel F minimum voltage (mV) | |
106 | +in9_min AIN1 minimum voltage (mV) | |
107 | +in10_min AIN2 minimum voltage (mV) | |
108 | + | |
109 | +in1_max 12v input maximum voltage (mV) | |
110 | +in2_max 3.3V (VDD) input maximum voltage (mV) | |
111 | +in3_max Channel A maximum voltage (mV) | |
112 | +in4_max Channel B maximum voltage (mV) | |
113 | +in5_max Channel C maximum voltage (mV) | |
114 | +in6_max Channel D maximum voltage (mV) | |
115 | +in7_max Channel E maximum voltage (mV) | |
116 | +in8_max Channel F maximum voltage (mV) | |
117 | +in9_max AIN1 maximum voltage (mV) | |
118 | +in10_max AIN2 maximum voltage (mV) | |
119 | + | |
120 | +in1_lcrit 12v input critical minimum voltage (mV) | |
121 | +in2_lcrit 3.3V (VDD) input critical minimum voltage (mV) | |
122 | +in3_lcrit Channel A critical minimum voltage (mV) | |
123 | +in4_lcrit Channel B critical minimum voltage (mV) | |
124 | +in5_lcrit Channel C critical minimum voltage (mV) | |
125 | +in6_lcrit Channel D critical minimum voltage (mV) | |
126 | +in7_lcrit Channel E critical minimum voltage (mV) | |
127 | +in8_lcrit Channel F critical minimum voltage (mV) | |
128 | +in9_lcrit AIN1 critical minimum voltage (mV) | |
129 | +in10_lcrit AIN2 critical minimum voltage (mV) | |
130 | + | |
131 | +in1_crit 12v input critical maximum voltage (mV) | |
132 | +in2_crit 3.3V (VDD) input critical maximum voltage (mV) | |
133 | +in3_crit Channel A critical maximum voltage (mV) | |
134 | +in4_crit Channel B critical maximum voltage (mV) | |
135 | +in5_crit Channel C critical maximum voltage (mV) | |
136 | +in6_crit Channel D critical maximum voltage (mV) | |
137 | +in7_crit Channel E critical maximum voltage (mV) | |
138 | +in8_crit Channel F critical maximum voltage (mV) | |
139 | +in9_crit AIN1 critical maximum voltage (mV) | |
140 | +in10_crit AIN2 critical maximum voltage (mV) | |
141 | + | |
142 | +in1_crit_alarm 12v input critical alarm | |
143 | +in2_crit_alarm 3.3V (VDD) input critical alarm | |
144 | +in3_crit_alarm Channel A critical alarm | |
145 | +in4_crit_alarm Channel B critical alarm | |
146 | +in5_crit_alarm Channel C critical alarm | |
147 | +in6_crit_alarm Channel D critical alarm | |
148 | +in7_crit_alarm Channel E critical alarm | |
149 | +in8_crit_alarm Channel F critical alarm | |
150 | +in9_crit_alarm AIN1 critical alarm | |
151 | +in10_crit_alarm AIN2 critical alarm | |
152 | + | |
153 | +temp1_input Chip tempererature | |
154 | +temp1_min Mimimum chip tempererature | |
155 | +temp1_max Maximum chip tempererature | |
156 | +temp1_crit Critical chip tempererature | |
157 | +temp1_crit_alarm Temperature critical alarm |
MAINTAINERS
... | ... | @@ -5251,6 +5251,13 @@ |
5251 | 5251 | S: Odd Fixes |
5252 | 5252 | F: drivers/net/smc91x.* |
5253 | 5253 | |
5254 | +SMM665 HARDWARE MONITOR DRIVER | |
5255 | +M: Guenter Roeck <linux@roeck-us.net> | |
5256 | +L: lm-sensors@lm-sensors.org | |
5257 | +S: Maintained | |
5258 | +F: Documentation/hwmon/smm665 | |
5259 | +F: drivers/hwmon/smm665.c | |
5260 | + | |
5254 | 5261 | SMSC47B397 HARDWARE MONITOR DRIVER |
5255 | 5262 | M: "Mark M. Hoffman" <mhoffman@lightlink.com> |
5256 | 5263 | L: lm-sensors@lm-sensors.org |
drivers/hwmon/Kconfig
... | ... | @@ -756,6 +756,21 @@ |
756 | 756 | This driver can also be built as a module. If so, the module |
757 | 757 | will be called sis5595. |
758 | 758 | |
759 | +config SENSORS_SMM665 | |
760 | + tristate "Summit Microelectronics SMM665" | |
761 | + depends on I2C && EXPERIMENTAL | |
762 | + default n | |
763 | + help | |
764 | + If you say yes here you get support for the hardware monitoring | |
765 | + features of the Summit Microelectronics SMM665/SMM665B Six-Channel | |
766 | + Active DC Output Controller / Monitor. | |
767 | + | |
768 | + Other supported chips are SMM465, SMM665C, SMM764, and SMM766. | |
769 | + Support for those chips is untested. | |
770 | + | |
771 | + This driver can also be built as a module. If so, the module will | |
772 | + be called smm665. | |
773 | + | |
759 | 774 | config SENSORS_DME1737 |
760 | 775 | tristate "SMSC DME1737, SCH311x and compatibles" |
761 | 776 | depends on I2C && EXPERIMENTAL |
drivers/hwmon/Makefile
... | ... | @@ -88,6 +88,7 @@ |
88 | 88 | obj-$(CONFIG_SENSORS_S3C) += s3c-hwmon.o |
89 | 89 | obj-$(CONFIG_SENSORS_SHT15) += sht15.o |
90 | 90 | obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o |
91 | +obj-$(CONFIG_SENSORS_SMM665) += smm665.o | |
91 | 92 | obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o |
92 | 93 | obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o |
93 | 94 | obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o |
drivers/hwmon/smm665.c
1 | +/* | |
2 | + * Driver for SMM665 Power Controller / Monitor | |
3 | + * | |
4 | + * Copyright (C) 2010 Ericsson AB. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify | |
7 | + * it under the terms of the GNU General Public License as published by | |
8 | + * the Free Software Foundation; version 2 of the License. | |
9 | + * | |
10 | + * This driver should also work for SMM465, SMM764, and SMM766, but is untested | |
11 | + * for those chips. Only monitoring functionality is implemented. | |
12 | + * | |
13 | + * Datasheets: | |
14 | + * http://www.summitmicro.com/prod_select/summary/SMM665/SMM665B_2089_20.pdf | |
15 | + * http://www.summitmicro.com/prod_select/summary/SMM766B/SMM766B_2122.pdf | |
16 | + */ | |
17 | + | |
18 | +#include <linux/kernel.h> | |
19 | +#include <linux/module.h> | |
20 | +#include <linux/init.h> | |
21 | +#include <linux/err.h> | |
22 | +#include <linux/slab.h> | |
23 | +#include <linux/i2c.h> | |
24 | +#include <linux/hwmon.h> | |
25 | +#include <linux/hwmon-sysfs.h> | |
26 | +#include <linux/delay.h> | |
27 | + | |
28 | +/* Internal reference voltage (VREF, x 1000 */ | |
29 | +#define SMM665_VREF_ADC_X1000 1250 | |
30 | + | |
31 | +/* module parameters */ | |
32 | +static int vref = SMM665_VREF_ADC_X1000; | |
33 | +module_param(vref, int, 0); | |
34 | +MODULE_PARM_DESC(vref, "Reference voltage in mV"); | |
35 | + | |
36 | +enum chips { smm465, smm665, smm665c, smm764, smm766 }; | |
37 | + | |
38 | +/* | |
39 | + * ADC channel addresses | |
40 | + */ | |
41 | +#define SMM665_MISC16_ADC_DATA_A 0x00 | |
42 | +#define SMM665_MISC16_ADC_DATA_B 0x01 | |
43 | +#define SMM665_MISC16_ADC_DATA_C 0x02 | |
44 | +#define SMM665_MISC16_ADC_DATA_D 0x03 | |
45 | +#define SMM665_MISC16_ADC_DATA_E 0x04 | |
46 | +#define SMM665_MISC16_ADC_DATA_F 0x05 | |
47 | +#define SMM665_MISC16_ADC_DATA_VDD 0x06 | |
48 | +#define SMM665_MISC16_ADC_DATA_12V 0x07 | |
49 | +#define SMM665_MISC16_ADC_DATA_INT_TEMP 0x08 | |
50 | +#define SMM665_MISC16_ADC_DATA_AIN1 0x09 | |
51 | +#define SMM665_MISC16_ADC_DATA_AIN2 0x0a | |
52 | + | |
53 | +/* | |
54 | + * Command registers | |
55 | + */ | |
56 | +#define SMM665_MISC8_CMD_STS 0x80 | |
57 | +#define SMM665_MISC8_STATUS1 0x81 | |
58 | +#define SMM665_MISC8_STATUSS2 0x82 | |
59 | +#define SMM665_MISC8_IO_POLARITY 0x83 | |
60 | +#define SMM665_MISC8_PUP_POLARITY 0x84 | |
61 | +#define SMM665_MISC8_ADOC_STATUS1 0x85 | |
62 | +#define SMM665_MISC8_ADOC_STATUS2 0x86 | |
63 | +#define SMM665_MISC8_WRITE_PROT 0x87 | |
64 | +#define SMM665_MISC8_STS_TRACK 0x88 | |
65 | + | |
66 | +/* | |
67 | + * Configuration registers and register groups | |
68 | + */ | |
69 | +#define SMM665_ADOC_ENABLE 0x0d | |
70 | +#define SMM665_LIMIT_BASE 0x80 /* First limit register */ | |
71 | + | |
72 | +/* | |
73 | + * Limit register bit masks | |
74 | + */ | |
75 | +#define SMM665_TRIGGER_RST 0x8000 | |
76 | +#define SMM665_TRIGGER_HEALTHY 0x4000 | |
77 | +#define SMM665_TRIGGER_POWEROFF 0x2000 | |
78 | +#define SMM665_TRIGGER_SHUTDOWN 0x1000 | |
79 | +#define SMM665_ADC_MASK 0x03ff | |
80 | + | |
81 | +#define smm665_is_critical(lim) ((lim) & (SMM665_TRIGGER_RST \ | |
82 | + | SMM665_TRIGGER_POWEROFF \ | |
83 | + | SMM665_TRIGGER_SHUTDOWN)) | |
84 | +/* | |
85 | + * Fault register bit definitions | |
86 | + * Values are merged from status registers 1/2, | |
87 | + * with status register 1 providing the upper 8 bits. | |
88 | + */ | |
89 | +#define SMM665_FAULT_A 0x0001 | |
90 | +#define SMM665_FAULT_B 0x0002 | |
91 | +#define SMM665_FAULT_C 0x0004 | |
92 | +#define SMM665_FAULT_D 0x0008 | |
93 | +#define SMM665_FAULT_E 0x0010 | |
94 | +#define SMM665_FAULT_F 0x0020 | |
95 | +#define SMM665_FAULT_VDD 0x0040 | |
96 | +#define SMM665_FAULT_12V 0x0080 | |
97 | +#define SMM665_FAULT_TEMP 0x0100 | |
98 | +#define SMM665_FAULT_AIN1 0x0200 | |
99 | +#define SMM665_FAULT_AIN2 0x0400 | |
100 | + | |
101 | +/* | |
102 | + * I2C Register addresses | |
103 | + * | |
104 | + * The configuration register needs to be the configured base register. | |
105 | + * The command/status register address is derived from it. | |
106 | + */ | |
107 | +#define SMM665_REGMASK 0x78 | |
108 | +#define SMM665_CMDREG_BASE 0x48 | |
109 | +#define SMM665_CONFREG_BASE 0x50 | |
110 | + | |
111 | +/* | |
112 | + * Equations given by chip manufacturer to calculate voltage/temperature values | |
113 | + * vref = Reference voltage on VREF_ADC pin (module parameter) | |
114 | + * adc = 10bit ADC value read back from registers | |
115 | + */ | |
116 | + | |
117 | +/* Voltage A-F and VDD */ | |
118 | +#define SMM665_VMON_ADC_TO_VOLTS(adc) ((adc) * vref / 256) | |
119 | + | |
120 | +/* Voltage 12VIN */ | |
121 | +#define SMM665_12VIN_ADC_TO_VOLTS(adc) ((adc) * vref * 3 / 256) | |
122 | + | |
123 | +/* Voltage AIN1, AIN2 */ | |
124 | +#define SMM665_AIN_ADC_TO_VOLTS(adc) ((adc) * vref / 512) | |
125 | + | |
126 | +/* Temp Sensor */ | |
127 | +#define SMM665_TEMP_ADC_TO_CELSIUS(adc) ((adc) <= 511) ? \ | |
128 | + ((int)(adc) * 1000 / 4) : \ | |
129 | + (((int)(adc) - 0x400) * 1000 / 4) | |
130 | + | |
131 | +#define SMM665_NUM_ADC 11 | |
132 | + | |
133 | +/* | |
134 | + * Chip dependent ADC conversion time, in uS | |
135 | + */ | |
136 | +#define SMM665_ADC_WAIT_SMM665 70 | |
137 | +#define SMM665_ADC_WAIT_SMM766 185 | |
138 | + | |
139 | +struct smm665_data { | |
140 | + enum chips type; | |
141 | + int conversion_time; /* ADC conversion time */ | |
142 | + struct device *hwmon_dev; | |
143 | + struct mutex update_lock; | |
144 | + bool valid; | |
145 | + unsigned long last_updated; /* in jiffies */ | |
146 | + u16 adc[SMM665_NUM_ADC]; /* adc values (raw) */ | |
147 | + u16 faults; /* fault status */ | |
148 | + /* The following values are in mV */ | |
149 | + int critical_min_limit[SMM665_NUM_ADC]; | |
150 | + int alarm_min_limit[SMM665_NUM_ADC]; | |
151 | + int critical_max_limit[SMM665_NUM_ADC]; | |
152 | + int alarm_max_limit[SMM665_NUM_ADC]; | |
153 | + struct i2c_client *cmdreg; | |
154 | +}; | |
155 | + | |
156 | +/* | |
157 | + * smm665_read16() | |
158 | + * | |
159 | + * Read 16 bit value from <reg>, <reg+1>. Upper 8 bits are in <reg>. | |
160 | + */ | |
161 | +static int smm665_read16(struct i2c_client *client, int reg) | |
162 | +{ | |
163 | + int rv, val; | |
164 | + | |
165 | + rv = i2c_smbus_read_byte_data(client, reg); | |
166 | + if (rv < 0) | |
167 | + return rv; | |
168 | + val = rv << 8; | |
169 | + rv = i2c_smbus_read_byte_data(client, reg + 1); | |
170 | + if (rv < 0) | |
171 | + return rv; | |
172 | + val |= rv; | |
173 | + return val; | |
174 | +} | |
175 | + | |
176 | +/* | |
177 | + * Read adc value. | |
178 | + */ | |
179 | +static int smm665_read_adc(struct smm665_data *data, int adc) | |
180 | +{ | |
181 | + struct i2c_client *client = data->cmdreg; | |
182 | + int rv; | |
183 | + int radc; | |
184 | + | |
185 | + /* | |
186 | + * Algorithm for reading ADC, per SMM665 datasheet | |
187 | + * | |
188 | + * {[S][addr][W][Ack]} {[offset][Ack]} {[S][addr][R][Nack]} | |
189 | + * [wait conversion time] | |
190 | + * {[S][addr][R][Ack]} {[datahi][Ack]} {[datalo][Ack][P]} | |
191 | + * | |
192 | + * To implement the first part of this exchange, | |
193 | + * do a full read transaction and expect a failure/Nack. | |
194 | + * This sets up the address pointer on the SMM665 | |
195 | + * and starts the ADC conversion. | |
196 | + * Then do a two-byte read transaction. | |
197 | + */ | |
198 | + rv = i2c_smbus_read_byte_data(client, adc << 3); | |
199 | + if (rv != -ENXIO) { | |
200 | + /* | |
201 | + * We expect ENXIO to reflect NACK | |
202 | + * (per Documentation/i2c/fault-codes). | |
203 | + * Everything else is an error. | |
204 | + */ | |
205 | + dev_dbg(&client->dev, | |
206 | + "Unexpected return code %d when setting ADC index", rv); | |
207 | + return (rv < 0) ? rv : -EIO; | |
208 | + } | |
209 | + | |
210 | + udelay(data->conversion_time); | |
211 | + | |
212 | + /* | |
213 | + * Now read two bytes. | |
214 | + * | |
215 | + * Neither i2c_smbus_read_byte() nor | |
216 | + * i2c_smbus_read_block_data() worked here, | |
217 | + * so use i2c_smbus_read_word_data() instead. | |
218 | + * We could also try to use i2c_master_recv(), | |
219 | + * but that is not always supported. | |
220 | + */ | |
221 | + rv = i2c_smbus_read_word_data(client, 0); | |
222 | + if (rv < 0) { | |
223 | + dev_dbg(&client->dev, "Failed to read ADC value: error %d", rv); | |
224 | + return -1; | |
225 | + } | |
226 | + /* | |
227 | + * Validate/verify readback adc channel (in bit 11..14). | |
228 | + * High byte is in lower 8 bit of rv, so only shift by 3. | |
229 | + */ | |
230 | + radc = (rv >> 3) & 0x0f; | |
231 | + if (radc != adc) { | |
232 | + dev_dbg(&client->dev, "Unexpected RADC: Expected %d got %d", | |
233 | + adc, radc); | |
234 | + return -EIO; | |
235 | + } | |
236 | + /* | |
237 | + * Chip replies with H/L, while SMBus expects L/H. | |
238 | + * Thus, byte order is reversed, and we have to swap | |
239 | + * the result. | |
240 | + */ | |
241 | + rv = swab16(rv) & SMM665_ADC_MASK; | |
242 | + | |
243 | + return rv; | |
244 | +} | |
245 | + | |
246 | +static struct smm665_data *smm665_update_device(struct device *dev) | |
247 | +{ | |
248 | + struct i2c_client *client = to_i2c_client(dev); | |
249 | + struct smm665_data *data = i2c_get_clientdata(client); | |
250 | + struct smm665_data *ret = data; | |
251 | + | |
252 | + mutex_lock(&data->update_lock); | |
253 | + | |
254 | + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { | |
255 | + int i, val; | |
256 | + | |
257 | + /* | |
258 | + * read status registers | |
259 | + */ | |
260 | + val = smm665_read16(client, SMM665_MISC8_STATUS1); | |
261 | + if (unlikely(val < 0)) { | |
262 | + ret = ERR_PTR(val); | |
263 | + goto abort; | |
264 | + } | |
265 | + data->faults = val; | |
266 | + | |
267 | + /* Read adc registers */ | |
268 | + for (i = 0; i < SMM665_NUM_ADC; i++) { | |
269 | + val = smm665_read_adc(data, i); | |
270 | + if (unlikely(val < 0)) { | |
271 | + ret = ERR_PTR(val); | |
272 | + goto abort; | |
273 | + } | |
274 | + data->adc[i] = val; | |
275 | + } | |
276 | + data->last_updated = jiffies; | |
277 | + data->valid = 1; | |
278 | + } | |
279 | +abort: | |
280 | + mutex_unlock(&data->update_lock); | |
281 | + return ret; | |
282 | +} | |
283 | + | |
284 | +/* Return converted value from given adc */ | |
285 | +static int smm665_convert(u16 adcval, int index) | |
286 | +{ | |
287 | + int val = 0; | |
288 | + | |
289 | + switch (index) { | |
290 | + case SMM665_MISC16_ADC_DATA_12V: | |
291 | + val = SMM665_12VIN_ADC_TO_VOLTS(adcval & SMM665_ADC_MASK); | |
292 | + break; | |
293 | + | |
294 | + case SMM665_MISC16_ADC_DATA_VDD: | |
295 | + case SMM665_MISC16_ADC_DATA_A: | |
296 | + case SMM665_MISC16_ADC_DATA_B: | |
297 | + case SMM665_MISC16_ADC_DATA_C: | |
298 | + case SMM665_MISC16_ADC_DATA_D: | |
299 | + case SMM665_MISC16_ADC_DATA_E: | |
300 | + case SMM665_MISC16_ADC_DATA_F: | |
301 | + val = SMM665_VMON_ADC_TO_VOLTS(adcval & SMM665_ADC_MASK); | |
302 | + break; | |
303 | + | |
304 | + case SMM665_MISC16_ADC_DATA_AIN1: | |
305 | + case SMM665_MISC16_ADC_DATA_AIN2: | |
306 | + val = SMM665_AIN_ADC_TO_VOLTS(adcval & SMM665_ADC_MASK); | |
307 | + break; | |
308 | + | |
309 | + case SMM665_MISC16_ADC_DATA_INT_TEMP: | |
310 | + val = SMM665_TEMP_ADC_TO_CELSIUS(adcval & SMM665_ADC_MASK); | |
311 | + break; | |
312 | + | |
313 | + default: | |
314 | + /* If we get here, the developer messed up */ | |
315 | + WARN_ON_ONCE(1); | |
316 | + break; | |
317 | + } | |
318 | + | |
319 | + return val; | |
320 | +} | |
321 | + | |
322 | +static int smm665_get_min(struct device *dev, int index) | |
323 | +{ | |
324 | + struct i2c_client *client = to_i2c_client(dev); | |
325 | + struct smm665_data *data = i2c_get_clientdata(client); | |
326 | + | |
327 | + return data->alarm_min_limit[index]; | |
328 | +} | |
329 | + | |
330 | +static int smm665_get_max(struct device *dev, int index) | |
331 | +{ | |
332 | + struct i2c_client *client = to_i2c_client(dev); | |
333 | + struct smm665_data *data = i2c_get_clientdata(client); | |
334 | + | |
335 | + return data->alarm_max_limit[index]; | |
336 | +} | |
337 | + | |
338 | +static int smm665_get_lcrit(struct device *dev, int index) | |
339 | +{ | |
340 | + struct i2c_client *client = to_i2c_client(dev); | |
341 | + struct smm665_data *data = i2c_get_clientdata(client); | |
342 | + | |
343 | + return data->critical_min_limit[index]; | |
344 | +} | |
345 | + | |
346 | +static int smm665_get_crit(struct device *dev, int index) | |
347 | +{ | |
348 | + struct i2c_client *client = to_i2c_client(dev); | |
349 | + struct smm665_data *data = i2c_get_clientdata(client); | |
350 | + | |
351 | + return data->critical_max_limit[index]; | |
352 | +} | |
353 | + | |
354 | +static ssize_t smm665_show_crit_alarm(struct device *dev, | |
355 | + struct device_attribute *da, char *buf) | |
356 | +{ | |
357 | + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | |
358 | + struct smm665_data *data = smm665_update_device(dev); | |
359 | + int val = 0; | |
360 | + | |
361 | + if (IS_ERR(data)) | |
362 | + return PTR_ERR(data); | |
363 | + | |
364 | + if (data->faults & (1 << attr->index)) | |
365 | + val = 1; | |
366 | + | |
367 | + return snprintf(buf, PAGE_SIZE, "%d\n", val); | |
368 | +} | |
369 | + | |
370 | +static ssize_t smm665_show_input(struct device *dev, | |
371 | + struct device_attribute *da, char *buf) | |
372 | +{ | |
373 | + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | |
374 | + struct smm665_data *data = smm665_update_device(dev); | |
375 | + int adc = attr->index; | |
376 | + int val; | |
377 | + | |
378 | + if (IS_ERR(data)) | |
379 | + return PTR_ERR(data); | |
380 | + | |
381 | + val = smm665_convert(data->adc[adc], adc); | |
382 | + return snprintf(buf, PAGE_SIZE, "%d\n", val); | |
383 | +} | |
384 | + | |
385 | +#define SMM665_SHOW(what) \ | |
386 | + static ssize_t smm665_show_##what(struct device *dev, \ | |
387 | + struct device_attribute *da, char *buf) \ | |
388 | +{ \ | |
389 | + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); \ | |
390 | + const int val = smm665_get_##what(dev, attr->index); \ | |
391 | + return snprintf(buf, PAGE_SIZE, "%d\n", val); \ | |
392 | +} | |
393 | + | |
394 | +SMM665_SHOW(min); | |
395 | +SMM665_SHOW(max); | |
396 | +SMM665_SHOW(lcrit); | |
397 | +SMM665_SHOW(crit); | |
398 | + | |
399 | +/* These macros are used below in constructing device attribute objects | |
400 | + * for use with sysfs_create_group() to make a sysfs device file | |
401 | + * for each register. | |
402 | + */ | |
403 | + | |
404 | +#define SMM665_ATTR(name, type, cmd_idx) \ | |
405 | + static SENSOR_DEVICE_ATTR(name##_##type, S_IRUGO, \ | |
406 | + smm665_show_##type, NULL, cmd_idx) | |
407 | + | |
408 | +/* Construct a sensor_device_attribute structure for each register */ | |
409 | + | |
410 | +/* Input voltages */ | |
411 | +SMM665_ATTR(in1, input, SMM665_MISC16_ADC_DATA_12V); | |
412 | +SMM665_ATTR(in2, input, SMM665_MISC16_ADC_DATA_VDD); | |
413 | +SMM665_ATTR(in3, input, SMM665_MISC16_ADC_DATA_A); | |
414 | +SMM665_ATTR(in4, input, SMM665_MISC16_ADC_DATA_B); | |
415 | +SMM665_ATTR(in5, input, SMM665_MISC16_ADC_DATA_C); | |
416 | +SMM665_ATTR(in6, input, SMM665_MISC16_ADC_DATA_D); | |
417 | +SMM665_ATTR(in7, input, SMM665_MISC16_ADC_DATA_E); | |
418 | +SMM665_ATTR(in8, input, SMM665_MISC16_ADC_DATA_F); | |
419 | +SMM665_ATTR(in9, input, SMM665_MISC16_ADC_DATA_AIN1); | |
420 | +SMM665_ATTR(in10, input, SMM665_MISC16_ADC_DATA_AIN2); | |
421 | + | |
422 | +/* Input voltages min */ | |
423 | +SMM665_ATTR(in1, min, SMM665_MISC16_ADC_DATA_12V); | |
424 | +SMM665_ATTR(in2, min, SMM665_MISC16_ADC_DATA_VDD); | |
425 | +SMM665_ATTR(in3, min, SMM665_MISC16_ADC_DATA_A); | |
426 | +SMM665_ATTR(in4, min, SMM665_MISC16_ADC_DATA_B); | |
427 | +SMM665_ATTR(in5, min, SMM665_MISC16_ADC_DATA_C); | |
428 | +SMM665_ATTR(in6, min, SMM665_MISC16_ADC_DATA_D); | |
429 | +SMM665_ATTR(in7, min, SMM665_MISC16_ADC_DATA_E); | |
430 | +SMM665_ATTR(in8, min, SMM665_MISC16_ADC_DATA_F); | |
431 | +SMM665_ATTR(in9, min, SMM665_MISC16_ADC_DATA_AIN1); | |
432 | +SMM665_ATTR(in10, min, SMM665_MISC16_ADC_DATA_AIN2); | |
433 | + | |
434 | +/* Input voltages max */ | |
435 | +SMM665_ATTR(in1, max, SMM665_MISC16_ADC_DATA_12V); | |
436 | +SMM665_ATTR(in2, max, SMM665_MISC16_ADC_DATA_VDD); | |
437 | +SMM665_ATTR(in3, max, SMM665_MISC16_ADC_DATA_A); | |
438 | +SMM665_ATTR(in4, max, SMM665_MISC16_ADC_DATA_B); | |
439 | +SMM665_ATTR(in5, max, SMM665_MISC16_ADC_DATA_C); | |
440 | +SMM665_ATTR(in6, max, SMM665_MISC16_ADC_DATA_D); | |
441 | +SMM665_ATTR(in7, max, SMM665_MISC16_ADC_DATA_E); | |
442 | +SMM665_ATTR(in8, max, SMM665_MISC16_ADC_DATA_F); | |
443 | +SMM665_ATTR(in9, max, SMM665_MISC16_ADC_DATA_AIN1); | |
444 | +SMM665_ATTR(in10, max, SMM665_MISC16_ADC_DATA_AIN2); | |
445 | + | |
446 | +/* Input voltages lcrit */ | |
447 | +SMM665_ATTR(in1, lcrit, SMM665_MISC16_ADC_DATA_12V); | |
448 | +SMM665_ATTR(in2, lcrit, SMM665_MISC16_ADC_DATA_VDD); | |
449 | +SMM665_ATTR(in3, lcrit, SMM665_MISC16_ADC_DATA_A); | |
450 | +SMM665_ATTR(in4, lcrit, SMM665_MISC16_ADC_DATA_B); | |
451 | +SMM665_ATTR(in5, lcrit, SMM665_MISC16_ADC_DATA_C); | |
452 | +SMM665_ATTR(in6, lcrit, SMM665_MISC16_ADC_DATA_D); | |
453 | +SMM665_ATTR(in7, lcrit, SMM665_MISC16_ADC_DATA_E); | |
454 | +SMM665_ATTR(in8, lcrit, SMM665_MISC16_ADC_DATA_F); | |
455 | +SMM665_ATTR(in9, lcrit, SMM665_MISC16_ADC_DATA_AIN1); | |
456 | +SMM665_ATTR(in10, lcrit, SMM665_MISC16_ADC_DATA_AIN2); | |
457 | + | |
458 | +/* Input voltages crit */ | |
459 | +SMM665_ATTR(in1, crit, SMM665_MISC16_ADC_DATA_12V); | |
460 | +SMM665_ATTR(in2, crit, SMM665_MISC16_ADC_DATA_VDD); | |
461 | +SMM665_ATTR(in3, crit, SMM665_MISC16_ADC_DATA_A); | |
462 | +SMM665_ATTR(in4, crit, SMM665_MISC16_ADC_DATA_B); | |
463 | +SMM665_ATTR(in5, crit, SMM665_MISC16_ADC_DATA_C); | |
464 | +SMM665_ATTR(in6, crit, SMM665_MISC16_ADC_DATA_D); | |
465 | +SMM665_ATTR(in7, crit, SMM665_MISC16_ADC_DATA_E); | |
466 | +SMM665_ATTR(in8, crit, SMM665_MISC16_ADC_DATA_F); | |
467 | +SMM665_ATTR(in9, crit, SMM665_MISC16_ADC_DATA_AIN1); | |
468 | +SMM665_ATTR(in10, crit, SMM665_MISC16_ADC_DATA_AIN2); | |
469 | + | |
470 | +/* critical alarms */ | |
471 | +SMM665_ATTR(in1, crit_alarm, SMM665_FAULT_12V); | |
472 | +SMM665_ATTR(in2, crit_alarm, SMM665_FAULT_VDD); | |
473 | +SMM665_ATTR(in3, crit_alarm, SMM665_FAULT_A); | |
474 | +SMM665_ATTR(in4, crit_alarm, SMM665_FAULT_B); | |
475 | +SMM665_ATTR(in5, crit_alarm, SMM665_FAULT_C); | |
476 | +SMM665_ATTR(in6, crit_alarm, SMM665_FAULT_D); | |
477 | +SMM665_ATTR(in7, crit_alarm, SMM665_FAULT_E); | |
478 | +SMM665_ATTR(in8, crit_alarm, SMM665_FAULT_F); | |
479 | +SMM665_ATTR(in9, crit_alarm, SMM665_FAULT_AIN1); | |
480 | +SMM665_ATTR(in10, crit_alarm, SMM665_FAULT_AIN2); | |
481 | + | |
482 | +/* Temperature */ | |
483 | +SMM665_ATTR(temp1, input, SMM665_MISC16_ADC_DATA_INT_TEMP); | |
484 | +SMM665_ATTR(temp1, min, SMM665_MISC16_ADC_DATA_INT_TEMP); | |
485 | +SMM665_ATTR(temp1, max, SMM665_MISC16_ADC_DATA_INT_TEMP); | |
486 | +SMM665_ATTR(temp1, lcrit, SMM665_MISC16_ADC_DATA_INT_TEMP); | |
487 | +SMM665_ATTR(temp1, crit, SMM665_MISC16_ADC_DATA_INT_TEMP); | |
488 | +SMM665_ATTR(temp1, crit_alarm, SMM665_FAULT_TEMP); | |
489 | + | |
490 | +/* | |
491 | + * Finally, construct an array of pointers to members of the above objects, | |
492 | + * as required for sysfs_create_group() | |
493 | + */ | |
494 | +static struct attribute *smm665_attributes[] = { | |
495 | + &sensor_dev_attr_in1_input.dev_attr.attr, | |
496 | + &sensor_dev_attr_in1_min.dev_attr.attr, | |
497 | + &sensor_dev_attr_in1_max.dev_attr.attr, | |
498 | + &sensor_dev_attr_in1_lcrit.dev_attr.attr, | |
499 | + &sensor_dev_attr_in1_crit.dev_attr.attr, | |
500 | + &sensor_dev_attr_in1_crit_alarm.dev_attr.attr, | |
501 | + | |
502 | + &sensor_dev_attr_in2_input.dev_attr.attr, | |
503 | + &sensor_dev_attr_in2_min.dev_attr.attr, | |
504 | + &sensor_dev_attr_in2_max.dev_attr.attr, | |
505 | + &sensor_dev_attr_in2_lcrit.dev_attr.attr, | |
506 | + &sensor_dev_attr_in2_crit.dev_attr.attr, | |
507 | + &sensor_dev_attr_in2_crit_alarm.dev_attr.attr, | |
508 | + | |
509 | + &sensor_dev_attr_in3_input.dev_attr.attr, | |
510 | + &sensor_dev_attr_in3_min.dev_attr.attr, | |
511 | + &sensor_dev_attr_in3_max.dev_attr.attr, | |
512 | + &sensor_dev_attr_in3_lcrit.dev_attr.attr, | |
513 | + &sensor_dev_attr_in3_crit.dev_attr.attr, | |
514 | + &sensor_dev_attr_in3_crit_alarm.dev_attr.attr, | |
515 | + | |
516 | + &sensor_dev_attr_in4_input.dev_attr.attr, | |
517 | + &sensor_dev_attr_in4_min.dev_attr.attr, | |
518 | + &sensor_dev_attr_in4_max.dev_attr.attr, | |
519 | + &sensor_dev_attr_in4_lcrit.dev_attr.attr, | |
520 | + &sensor_dev_attr_in4_crit.dev_attr.attr, | |
521 | + &sensor_dev_attr_in4_crit_alarm.dev_attr.attr, | |
522 | + | |
523 | + &sensor_dev_attr_in5_input.dev_attr.attr, | |
524 | + &sensor_dev_attr_in5_min.dev_attr.attr, | |
525 | + &sensor_dev_attr_in5_max.dev_attr.attr, | |
526 | + &sensor_dev_attr_in5_lcrit.dev_attr.attr, | |
527 | + &sensor_dev_attr_in5_crit.dev_attr.attr, | |
528 | + &sensor_dev_attr_in5_crit_alarm.dev_attr.attr, | |
529 | + | |
530 | + &sensor_dev_attr_in6_input.dev_attr.attr, | |
531 | + &sensor_dev_attr_in6_min.dev_attr.attr, | |
532 | + &sensor_dev_attr_in6_max.dev_attr.attr, | |
533 | + &sensor_dev_attr_in6_lcrit.dev_attr.attr, | |
534 | + &sensor_dev_attr_in6_crit.dev_attr.attr, | |
535 | + &sensor_dev_attr_in6_crit_alarm.dev_attr.attr, | |
536 | + | |
537 | + &sensor_dev_attr_in7_input.dev_attr.attr, | |
538 | + &sensor_dev_attr_in7_min.dev_attr.attr, | |
539 | + &sensor_dev_attr_in7_max.dev_attr.attr, | |
540 | + &sensor_dev_attr_in7_lcrit.dev_attr.attr, | |
541 | + &sensor_dev_attr_in7_crit.dev_attr.attr, | |
542 | + &sensor_dev_attr_in7_crit_alarm.dev_attr.attr, | |
543 | + | |
544 | + &sensor_dev_attr_in8_input.dev_attr.attr, | |
545 | + &sensor_dev_attr_in8_min.dev_attr.attr, | |
546 | + &sensor_dev_attr_in8_max.dev_attr.attr, | |
547 | + &sensor_dev_attr_in8_lcrit.dev_attr.attr, | |
548 | + &sensor_dev_attr_in8_crit.dev_attr.attr, | |
549 | + &sensor_dev_attr_in8_crit_alarm.dev_attr.attr, | |
550 | + | |
551 | + &sensor_dev_attr_in9_input.dev_attr.attr, | |
552 | + &sensor_dev_attr_in9_min.dev_attr.attr, | |
553 | + &sensor_dev_attr_in9_max.dev_attr.attr, | |
554 | + &sensor_dev_attr_in9_lcrit.dev_attr.attr, | |
555 | + &sensor_dev_attr_in9_crit.dev_attr.attr, | |
556 | + &sensor_dev_attr_in9_crit_alarm.dev_attr.attr, | |
557 | + | |
558 | + &sensor_dev_attr_in10_input.dev_attr.attr, | |
559 | + &sensor_dev_attr_in10_min.dev_attr.attr, | |
560 | + &sensor_dev_attr_in10_max.dev_attr.attr, | |
561 | + &sensor_dev_attr_in10_lcrit.dev_attr.attr, | |
562 | + &sensor_dev_attr_in10_crit.dev_attr.attr, | |
563 | + &sensor_dev_attr_in10_crit_alarm.dev_attr.attr, | |
564 | + | |
565 | + &sensor_dev_attr_temp1_input.dev_attr.attr, | |
566 | + &sensor_dev_attr_temp1_min.dev_attr.attr, | |
567 | + &sensor_dev_attr_temp1_max.dev_attr.attr, | |
568 | + &sensor_dev_attr_temp1_lcrit.dev_attr.attr, | |
569 | + &sensor_dev_attr_temp1_crit.dev_attr.attr, | |
570 | + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, | |
571 | + | |
572 | + NULL, | |
573 | +}; | |
574 | + | |
575 | +static const struct attribute_group smm665_group = { | |
576 | + .attrs = smm665_attributes, | |
577 | +}; | |
578 | + | |
579 | +static int smm665_probe(struct i2c_client *client, | |
580 | + const struct i2c_device_id *id) | |
581 | +{ | |
582 | + struct i2c_adapter *adapter = client->adapter; | |
583 | + struct smm665_data *data; | |
584 | + int i, ret; | |
585 | + | |
586 | + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | |
587 | + | I2C_FUNC_SMBUS_WORD_DATA)) | |
588 | + return -ENODEV; | |
589 | + | |
590 | + if (i2c_smbus_read_byte_data(client, SMM665_ADOC_ENABLE) < 0) | |
591 | + return -ENODEV; | |
592 | + | |
593 | + ret = -ENOMEM; | |
594 | + data = kzalloc(sizeof(*data), GFP_KERNEL); | |
595 | + if (!data) | |
596 | + goto out_return; | |
597 | + | |
598 | + i2c_set_clientdata(client, data); | |
599 | + mutex_init(&data->update_lock); | |
600 | + | |
601 | + data->type = id->driver_data; | |
602 | + data->cmdreg = i2c_new_dummy(adapter, (client->addr & ~SMM665_REGMASK) | |
603 | + | SMM665_CMDREG_BASE); | |
604 | + if (!data->cmdreg) | |
605 | + goto out_kfree; | |
606 | + | |
607 | + switch (data->type) { | |
608 | + case smm465: | |
609 | + case smm665: | |
610 | + data->conversion_time = SMM665_ADC_WAIT_SMM665; | |
611 | + break; | |
612 | + case smm665c: | |
613 | + case smm764: | |
614 | + case smm766: | |
615 | + data->conversion_time = SMM665_ADC_WAIT_SMM766; | |
616 | + break; | |
617 | + } | |
618 | + | |
619 | + ret = -ENODEV; | |
620 | + if (i2c_smbus_read_byte_data(data->cmdreg, SMM665_MISC8_CMD_STS) < 0) | |
621 | + goto out_unregister; | |
622 | + | |
623 | + /* | |
624 | + * Read limits. | |
625 | + * | |
626 | + * Limit registers start with register SMM665_LIMIT_BASE. | |
627 | + * Each channel uses 8 registers, providing four limit values | |
628 | + * per channel. Each limit value requires two registers, with the | |
629 | + * high byte in the first register and the low byte in the second | |
630 | + * register. The first two limits are under limit values, followed | |
631 | + * by two over limit values. | |
632 | + * | |
633 | + * Limit register order matches the ADC register order, so we use | |
634 | + * ADC register defines throughout the code to index limit registers. | |
635 | + * | |
636 | + * We save the first retrieved value both as "critical" and "alarm" | |
637 | + * value. The second value overwrites either the critical or the | |
638 | + * alarm value, depending on its configuration. This ensures that both | |
639 | + * critical and alarm values are initialized, even if both registers are | |
640 | + * configured as critical or non-critical. | |
641 | + */ | |
642 | + for (i = 0; i < SMM665_NUM_ADC; i++) { | |
643 | + int val; | |
644 | + | |
645 | + val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8); | |
646 | + if (unlikely(val < 0)) | |
647 | + goto out_unregister; | |
648 | + data->critical_min_limit[i] = data->alarm_min_limit[i] | |
649 | + = smm665_convert(val, i); | |
650 | + val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8 + 2); | |
651 | + if (unlikely(val < 0)) | |
652 | + goto out_unregister; | |
653 | + if (smm665_is_critical(val)) | |
654 | + data->critical_min_limit[i] = smm665_convert(val, i); | |
655 | + else | |
656 | + data->alarm_min_limit[i] = smm665_convert(val, i); | |
657 | + val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8 + 4); | |
658 | + if (unlikely(val < 0)) | |
659 | + goto out_unregister; | |
660 | + data->critical_max_limit[i] = data->alarm_max_limit[i] | |
661 | + = smm665_convert(val, i); | |
662 | + val = smm665_read16(client, SMM665_LIMIT_BASE + i * 8 + 6); | |
663 | + if (unlikely(val < 0)) | |
664 | + goto out_unregister; | |
665 | + if (smm665_is_critical(val)) | |
666 | + data->critical_max_limit[i] = smm665_convert(val, i); | |
667 | + else | |
668 | + data->alarm_max_limit[i] = smm665_convert(val, i); | |
669 | + } | |
670 | + | |
671 | + /* Register sysfs hooks */ | |
672 | + ret = sysfs_create_group(&client->dev.kobj, &smm665_group); | |
673 | + if (ret) | |
674 | + goto out_unregister; | |
675 | + | |
676 | + data->hwmon_dev = hwmon_device_register(&client->dev); | |
677 | + if (IS_ERR(data->hwmon_dev)) { | |
678 | + ret = PTR_ERR(data->hwmon_dev); | |
679 | + goto out_remove_group; | |
680 | + } | |
681 | + | |
682 | + return 0; | |
683 | + | |
684 | +out_remove_group: | |
685 | + sysfs_remove_group(&client->dev.kobj, &smm665_group); | |
686 | +out_unregister: | |
687 | + i2c_unregister_device(data->cmdreg); | |
688 | +out_kfree: | |
689 | + kfree(data); | |
690 | +out_return: | |
691 | + return ret; | |
692 | +} | |
693 | + | |
694 | +static int smm665_remove(struct i2c_client *client) | |
695 | +{ | |
696 | + struct smm665_data *data = i2c_get_clientdata(client); | |
697 | + | |
698 | + i2c_unregister_device(data->cmdreg); | |
699 | + hwmon_device_unregister(data->hwmon_dev); | |
700 | + sysfs_remove_group(&client->dev.kobj, &smm665_group); | |
701 | + | |
702 | + kfree(data); | |
703 | + | |
704 | + return 0; | |
705 | +} | |
706 | + | |
707 | +static const struct i2c_device_id smm665_id[] = { | |
708 | + {"smm465", smm465}, | |
709 | + {"smm665", smm665}, | |
710 | + {"smm665c", smm665c}, | |
711 | + {"smm764", smm764}, | |
712 | + {"smm766", smm766}, | |
713 | + {} | |
714 | +}; | |
715 | + | |
716 | +MODULE_DEVICE_TABLE(i2c, smm665_id); | |
717 | + | |
718 | +/* This is the driver that will be inserted */ | |
719 | +static struct i2c_driver smm665_driver = { | |
720 | + .driver = { | |
721 | + .name = "smm665", | |
722 | + }, | |
723 | + .probe = smm665_probe, | |
724 | + .remove = smm665_remove, | |
725 | + .id_table = smm665_id, | |
726 | +}; | |
727 | + | |
728 | +static int __init smm665_init(void) | |
729 | +{ | |
730 | + return i2c_add_driver(&smm665_driver); | |
731 | +} | |
732 | + | |
733 | +static void __exit smm665_exit(void) | |
734 | +{ | |
735 | + i2c_del_driver(&smm665_driver); | |
736 | +} | |
737 | + | |
738 | +MODULE_AUTHOR("Guenter Roeck"); | |
739 | +MODULE_DESCRIPTION("SMM665 driver"); | |
740 | +MODULE_LICENSE("GPL"); | |
741 | + | |
742 | +module_init(smm665_init); | |
743 | +module_exit(smm665_exit); |