xref: /openbmc/linux/drivers/hwmon/i5k_amb.c (revision 2207515d)
11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2298c7524SDarrick J. Wong /*
3298c7524SDarrick J. Wong  * A hwmon driver for the Intel 5000 series chipset FB-DIMM AMB
4298c7524SDarrick J. Wong  * temperature sensors
5298c7524SDarrick J. Wong  * Copyright (C) 2007 IBM
6298c7524SDarrick J. Wong  *
75407e051SDarrick J. Wong  * Author: Darrick J. Wong <darrick.wong@oracle.com>
8298c7524SDarrick J. Wong  */
9298c7524SDarrick J. Wong 
10298c7524SDarrick J. Wong #include <linux/module.h>
11298c7524SDarrick J. Wong #include <linux/hwmon.h>
12298c7524SDarrick J. Wong #include <linux/hwmon-sysfs.h>
13298c7524SDarrick J. Wong #include <linux/err.h>
14298c7524SDarrick J. Wong #include <linux/mutex.h>
15298c7524SDarrick J. Wong #include <linux/log2.h>
16298c7524SDarrick J. Wong #include <linux/pci.h>
17298c7524SDarrick J. Wong #include <linux/platform_device.h>
185a0e3ad6STejun Heo #include <linux/slab.h>
19298c7524SDarrick J. Wong 
20298c7524SDarrick J. Wong #define DRVNAME "i5k_amb"
21298c7524SDarrick J. Wong 
22298c7524SDarrick J. Wong #define I5K_REG_AMB_BASE_ADDR		0x48
23298c7524SDarrick J. Wong #define I5K_REG_AMB_LEN_ADDR		0x50
24298c7524SDarrick J. Wong #define I5K_REG_CHAN0_PRESENCE_ADDR	0x64
25298c7524SDarrick J. Wong #define I5K_REG_CHAN1_PRESENCE_ADDR	0x66
26298c7524SDarrick J. Wong 
27298c7524SDarrick J. Wong #define AMB_REG_TEMP_MIN_ADDR		0x80
28298c7524SDarrick J. Wong #define AMB_REG_TEMP_MID_ADDR		0x81
29298c7524SDarrick J. Wong #define AMB_REG_TEMP_MAX_ADDR		0x82
30298c7524SDarrick J. Wong #define AMB_REG_TEMP_STATUS_ADDR	0x84
31298c7524SDarrick J. Wong #define AMB_REG_TEMP_ADDR		0x85
32298c7524SDarrick J. Wong 
33298c7524SDarrick J. Wong #define AMB_CONFIG_SIZE			2048
34298c7524SDarrick J. Wong #define AMB_FUNC_3_OFFSET		768
35298c7524SDarrick J. Wong 
amb_reg_temp_status(unsigned int amb)3659a030a9SDarrick J. Wong static unsigned long amb_reg_temp_status(unsigned int amb)
3759a030a9SDarrick J. Wong {
3859a030a9SDarrick J. Wong 	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_STATUS_ADDR +
3959a030a9SDarrick J. Wong 	       AMB_CONFIG_SIZE * amb;
4059a030a9SDarrick J. Wong }
4159a030a9SDarrick J. Wong 
amb_reg_temp_min(unsigned int amb)4259a030a9SDarrick J. Wong static unsigned long amb_reg_temp_min(unsigned int amb)
4359a030a9SDarrick J. Wong {
4459a030a9SDarrick J. Wong 	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MIN_ADDR +
4559a030a9SDarrick J. Wong 	       AMB_CONFIG_SIZE * amb;
4659a030a9SDarrick J. Wong }
4759a030a9SDarrick J. Wong 
amb_reg_temp_mid(unsigned int amb)4859a030a9SDarrick J. Wong static unsigned long amb_reg_temp_mid(unsigned int amb)
4959a030a9SDarrick J. Wong {
5059a030a9SDarrick J. Wong 	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MID_ADDR +
5159a030a9SDarrick J. Wong 	       AMB_CONFIG_SIZE * amb;
5259a030a9SDarrick J. Wong }
5359a030a9SDarrick J. Wong 
amb_reg_temp_max(unsigned int amb)5459a030a9SDarrick J. Wong static unsigned long amb_reg_temp_max(unsigned int amb)
5559a030a9SDarrick J. Wong {
5659a030a9SDarrick J. Wong 	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_MAX_ADDR +
5759a030a9SDarrick J. Wong 	       AMB_CONFIG_SIZE * amb;
5859a030a9SDarrick J. Wong }
5959a030a9SDarrick J. Wong 
amb_reg_temp(unsigned int amb)6059a030a9SDarrick J. Wong static unsigned long amb_reg_temp(unsigned int amb)
6159a030a9SDarrick J. Wong {
6259a030a9SDarrick J. Wong 	return AMB_FUNC_3_OFFSET + AMB_REG_TEMP_ADDR +
6359a030a9SDarrick J. Wong 	       AMB_CONFIG_SIZE * amb;
6459a030a9SDarrick J. Wong }
65298c7524SDarrick J. Wong 
66298c7524SDarrick J. Wong #define MAX_MEM_CHANNELS		4
67298c7524SDarrick J. Wong #define MAX_AMBS_PER_CHANNEL		16
68298c7524SDarrick J. Wong #define MAX_AMBS			(MAX_MEM_CHANNELS * \
69298c7524SDarrick J. Wong 					 MAX_AMBS_PER_CHANNEL)
70963d96b5SDarrick J. Wong #define CHANNEL_SHIFT			4
71963d96b5SDarrick J. Wong #define DIMM_MASK			0xF
72298c7524SDarrick J. Wong /*
73298c7524SDarrick J. Wong  * Ugly hack: For some reason the highest bit is set if there
74298c7524SDarrick J. Wong  * are _any_ DIMMs in the channel.  Attempting to read from
75298c7524SDarrick J. Wong  * this "high-order" AMB results in a memory bus error, so
76298c7524SDarrick J. Wong  * for now we'll just ignore that top bit, even though that
77298c7524SDarrick J. Wong  * might prevent us from seeing the 16th DIMM in the channel.
78298c7524SDarrick J. Wong  */
79298c7524SDarrick J. Wong #define REAL_MAX_AMBS_PER_CHANNEL	15
80963d96b5SDarrick J. Wong #define KNOBS_PER_AMB			6
81298c7524SDarrick J. Wong 
amb_num_from_reg(unsigned int byte_num,unsigned int bit)8259a030a9SDarrick J. Wong static unsigned long amb_num_from_reg(unsigned int byte_num, unsigned int bit)
8359a030a9SDarrick J. Wong {
8459a030a9SDarrick J. Wong 	return byte_num * MAX_AMBS_PER_CHANNEL + bit;
8559a030a9SDarrick J. Wong }
86298c7524SDarrick J. Wong 
87298c7524SDarrick J. Wong #define AMB_SYSFS_NAME_LEN		16
88298c7524SDarrick J. Wong struct i5k_device_attribute {
89298c7524SDarrick J. Wong 	struct sensor_device_attribute s_attr;
90298c7524SDarrick J. Wong 	char name[AMB_SYSFS_NAME_LEN];
91298c7524SDarrick J. Wong };
92298c7524SDarrick J. Wong 
93298c7524SDarrick J. Wong struct i5k_amb_data {
94298c7524SDarrick J. Wong 	struct device *hwmon_dev;
95298c7524SDarrick J. Wong 
96298c7524SDarrick J. Wong 	unsigned long amb_base;
97298c7524SDarrick J. Wong 	unsigned long amb_len;
98298c7524SDarrick J. Wong 	u16 amb_present[MAX_MEM_CHANNELS];
99298c7524SDarrick J. Wong 	void __iomem *amb_mmio;
100298c7524SDarrick J. Wong 	struct i5k_device_attribute *attrs;
101298c7524SDarrick J. Wong 	unsigned int num_attrs;
102298c7524SDarrick J. Wong };
103298c7524SDarrick J. Wong 
name_show(struct device * dev,struct device_attribute * devattr,char * buf)1043edf03b3SJulia Lawall static ssize_t name_show(struct device *dev, struct device_attribute *devattr,
105298c7524SDarrick J. Wong 			 char *buf)
106298c7524SDarrick J. Wong {
107298c7524SDarrick J. Wong 	return sprintf(buf, "%s\n", DRVNAME);
108298c7524SDarrick J. Wong }
109298c7524SDarrick J. Wong 
110298c7524SDarrick J. Wong 
1113edf03b3SJulia Lawall static DEVICE_ATTR_RO(name);
112298c7524SDarrick J. Wong 
113298c7524SDarrick J. Wong static struct platform_device *amb_pdev;
114298c7524SDarrick J. Wong 
amb_read_byte(struct i5k_amb_data * data,unsigned long offset)115298c7524SDarrick J. Wong static u8 amb_read_byte(struct i5k_amb_data *data, unsigned long offset)
116298c7524SDarrick J. Wong {
117298c7524SDarrick J. Wong 	return ioread8(data->amb_mmio + offset);
118298c7524SDarrick J. Wong }
119298c7524SDarrick J. Wong 
amb_write_byte(struct i5k_amb_data * data,unsigned long offset,u8 val)120298c7524SDarrick J. Wong static void amb_write_byte(struct i5k_amb_data *data, unsigned long offset,
121298c7524SDarrick J. Wong 			   u8 val)
122298c7524SDarrick J. Wong {
123298c7524SDarrick J. Wong 	iowrite8(val, data->amb_mmio + offset);
124298c7524SDarrick J. Wong }
125298c7524SDarrick J. Wong 
show_amb_alarm(struct device * dev,struct device_attribute * devattr,char * buf)126298c7524SDarrick J. Wong static ssize_t show_amb_alarm(struct device *dev,
127298c7524SDarrick J. Wong 			     struct device_attribute *devattr,
128298c7524SDarrick J. Wong 			     char *buf)
129298c7524SDarrick J. Wong {
130298c7524SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
131298c7524SDarrick J. Wong 	struct i5k_amb_data *data = dev_get_drvdata(dev);
132298c7524SDarrick J. Wong 
13359a030a9SDarrick J. Wong 	if (!(amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x20) &&
13459a030a9SDarrick J. Wong 	     (amb_read_byte(data, amb_reg_temp_status(attr->index)) & 0x8))
135298c7524SDarrick J. Wong 		return sprintf(buf, "1\n");
136298c7524SDarrick J. Wong 	else
137298c7524SDarrick J. Wong 		return sprintf(buf, "0\n");
138298c7524SDarrick J. Wong }
139298c7524SDarrick J. Wong 
store_amb_min(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)140298c7524SDarrick J. Wong static ssize_t store_amb_min(struct device *dev,
141298c7524SDarrick J. Wong 			     struct device_attribute *devattr,
142298c7524SDarrick J. Wong 			     const char *buf,
143298c7524SDarrick J. Wong 			     size_t count)
144298c7524SDarrick J. Wong {
145298c7524SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
146298c7524SDarrick J. Wong 	struct i5k_amb_data *data = dev_get_drvdata(dev);
1477b102ed6SFrans Meulenbroeks 	unsigned long temp;
1487b102ed6SFrans Meulenbroeks 	int ret = kstrtoul(buf, 10, &temp);
1497b102ed6SFrans Meulenbroeks 	if (ret < 0)
1507b102ed6SFrans Meulenbroeks 		return ret;
151298c7524SDarrick J. Wong 
1527b102ed6SFrans Meulenbroeks 	temp = temp / 500;
153298c7524SDarrick J. Wong 	if (temp > 255)
154298c7524SDarrick J. Wong 		temp = 255;
155298c7524SDarrick J. Wong 
15659a030a9SDarrick J. Wong 	amb_write_byte(data, amb_reg_temp_min(attr->index), temp);
157298c7524SDarrick J. Wong 	return count;
158298c7524SDarrick J. Wong }
159298c7524SDarrick J. Wong 
store_amb_mid(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)160298c7524SDarrick J. Wong static ssize_t store_amb_mid(struct device *dev,
161298c7524SDarrick J. Wong 			     struct device_attribute *devattr,
162298c7524SDarrick J. Wong 			     const char *buf,
163298c7524SDarrick J. Wong 			     size_t count)
164298c7524SDarrick J. Wong {
165298c7524SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
166298c7524SDarrick J. Wong 	struct i5k_amb_data *data = dev_get_drvdata(dev);
1677b102ed6SFrans Meulenbroeks 	unsigned long temp;
1687b102ed6SFrans Meulenbroeks 	int ret = kstrtoul(buf, 10, &temp);
1697b102ed6SFrans Meulenbroeks 	if (ret < 0)
1707b102ed6SFrans Meulenbroeks 		return ret;
171298c7524SDarrick J. Wong 
1727b102ed6SFrans Meulenbroeks 	temp = temp / 500;
173298c7524SDarrick J. Wong 	if (temp > 255)
174298c7524SDarrick J. Wong 		temp = 255;
175298c7524SDarrick J. Wong 
17659a030a9SDarrick J. Wong 	amb_write_byte(data, amb_reg_temp_mid(attr->index), temp);
177298c7524SDarrick J. Wong 	return count;
178298c7524SDarrick J. Wong }
179298c7524SDarrick J. Wong 
store_amb_max(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)180298c7524SDarrick J. Wong static ssize_t store_amb_max(struct device *dev,
181298c7524SDarrick J. Wong 			     struct device_attribute *devattr,
182298c7524SDarrick J. Wong 			     const char *buf,
183298c7524SDarrick J. Wong 			     size_t count)
184298c7524SDarrick J. Wong {
185298c7524SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
186298c7524SDarrick J. Wong 	struct i5k_amb_data *data = dev_get_drvdata(dev);
1877b102ed6SFrans Meulenbroeks 	unsigned long temp;
1887b102ed6SFrans Meulenbroeks 	int ret = kstrtoul(buf, 10, &temp);
1897b102ed6SFrans Meulenbroeks 	if (ret < 0)
1907b102ed6SFrans Meulenbroeks 		return ret;
191298c7524SDarrick J. Wong 
1927b102ed6SFrans Meulenbroeks 	temp = temp / 500;
193298c7524SDarrick J. Wong 	if (temp > 255)
194298c7524SDarrick J. Wong 		temp = 255;
195298c7524SDarrick J. Wong 
19659a030a9SDarrick J. Wong 	amb_write_byte(data, amb_reg_temp_max(attr->index), temp);
197298c7524SDarrick J. Wong 	return count;
198298c7524SDarrick J. Wong }
199298c7524SDarrick J. Wong 
show_amb_min(struct device * dev,struct device_attribute * devattr,char * buf)200298c7524SDarrick J. Wong static ssize_t show_amb_min(struct device *dev,
201298c7524SDarrick J. Wong 			     struct device_attribute *devattr,
202298c7524SDarrick J. Wong 			     char *buf)
203298c7524SDarrick J. Wong {
204298c7524SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
205298c7524SDarrick J. Wong 	struct i5k_amb_data *data = dev_get_drvdata(dev);
206298c7524SDarrick J. Wong 	return sprintf(buf, "%d\n",
20759a030a9SDarrick J. Wong 		500 * amb_read_byte(data, amb_reg_temp_min(attr->index)));
208298c7524SDarrick J. Wong }
209298c7524SDarrick J. Wong 
show_amb_mid(struct device * dev,struct device_attribute * devattr,char * buf)210298c7524SDarrick J. Wong static ssize_t show_amb_mid(struct device *dev,
211298c7524SDarrick J. Wong 			     struct device_attribute *devattr,
212298c7524SDarrick J. Wong 			     char *buf)
213298c7524SDarrick J. Wong {
214298c7524SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
215298c7524SDarrick J. Wong 	struct i5k_amb_data *data = dev_get_drvdata(dev);
216298c7524SDarrick J. Wong 	return sprintf(buf, "%d\n",
21759a030a9SDarrick J. Wong 		500 * amb_read_byte(data, amb_reg_temp_mid(attr->index)));
218298c7524SDarrick J. Wong }
219298c7524SDarrick J. Wong 
show_amb_max(struct device * dev,struct device_attribute * devattr,char * buf)220298c7524SDarrick J. Wong static ssize_t show_amb_max(struct device *dev,
221298c7524SDarrick J. Wong 			     struct device_attribute *devattr,
222298c7524SDarrick J. Wong 			     char *buf)
223298c7524SDarrick J. Wong {
224298c7524SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
225298c7524SDarrick J. Wong 	struct i5k_amb_data *data = dev_get_drvdata(dev);
226298c7524SDarrick J. Wong 	return sprintf(buf, "%d\n",
22759a030a9SDarrick J. Wong 		500 * amb_read_byte(data, amb_reg_temp_max(attr->index)));
228298c7524SDarrick J. Wong }
229298c7524SDarrick J. Wong 
show_amb_temp(struct device * dev,struct device_attribute * devattr,char * buf)230298c7524SDarrick J. Wong static ssize_t show_amb_temp(struct device *dev,
231298c7524SDarrick J. Wong 			     struct device_attribute *devattr,
232298c7524SDarrick J. Wong 			     char *buf)
233298c7524SDarrick J. Wong {
234298c7524SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
235298c7524SDarrick J. Wong 	struct i5k_amb_data *data = dev_get_drvdata(dev);
236298c7524SDarrick J. Wong 	return sprintf(buf, "%d\n",
23759a030a9SDarrick J. Wong 		500 * amb_read_byte(data, amb_reg_temp(attr->index)));
238298c7524SDarrick J. Wong }
239298c7524SDarrick J. Wong 
show_label(struct device * dev,struct device_attribute * devattr,char * buf)240963d96b5SDarrick J. Wong static ssize_t show_label(struct device *dev,
241963d96b5SDarrick J. Wong 			  struct device_attribute *devattr,
242963d96b5SDarrick J. Wong 			  char *buf)
243963d96b5SDarrick J. Wong {
244963d96b5SDarrick J. Wong 	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
245963d96b5SDarrick J. Wong 
246963d96b5SDarrick J. Wong 	return sprintf(buf, "Ch. %d DIMM %d\n", attr->index >> CHANNEL_SHIFT,
247963d96b5SDarrick J. Wong 		       attr->index & DIMM_MASK);
248963d96b5SDarrick J. Wong }
249963d96b5SDarrick J. Wong 
i5k_amb_hwmon_init(struct platform_device * pdev)2506c931ae1SBill Pemberton static int i5k_amb_hwmon_init(struct platform_device *pdev)
251298c7524SDarrick J. Wong {
252298c7524SDarrick J. Wong 	int i, j, k, d = 0;
253298c7524SDarrick J. Wong 	u16 c;
254298c7524SDarrick J. Wong 	int res = 0;
255298c7524SDarrick J. Wong 	int num_ambs = 0;
256298c7524SDarrick J. Wong 	struct i5k_amb_data *data = platform_get_drvdata(pdev);
257298c7524SDarrick J. Wong 
258298c7524SDarrick J. Wong 	/* Count the number of AMBs found */
259298c7524SDarrick J. Wong 	/* ignore the high-order bit, see "Ugly hack" comment above */
260298c7524SDarrick J. Wong 	for (i = 0; i < MAX_MEM_CHANNELS; i++)
261298c7524SDarrick J. Wong 		num_ambs += hweight16(data->amb_present[i] & 0x7fff);
262298c7524SDarrick J. Wong 
263298c7524SDarrick J. Wong 	/* Set up sysfs stuff */
2646396bb22SKees Cook 	data->attrs = kzalloc(array3_size(num_ambs, KNOBS_PER_AMB,
2656396bb22SKees Cook 					  sizeof(*data->attrs)),
266298c7524SDarrick J. Wong 			      GFP_KERNEL);
267298c7524SDarrick J. Wong 	if (!data->attrs)
268298c7524SDarrick J. Wong 		return -ENOMEM;
269298c7524SDarrick J. Wong 	data->num_attrs = 0;
270298c7524SDarrick J. Wong 
271298c7524SDarrick J. Wong 	for (i = 0; i < MAX_MEM_CHANNELS; i++) {
272298c7524SDarrick J. Wong 		c = data->amb_present[i];
273298c7524SDarrick J. Wong 		for (j = 0; j < REAL_MAX_AMBS_PER_CHANNEL; j++, c >>= 1) {
274298c7524SDarrick J. Wong 			struct i5k_device_attribute *iattr;
275298c7524SDarrick J. Wong 
27659a030a9SDarrick J. Wong 			k = amb_num_from_reg(i, j);
277298c7524SDarrick J. Wong 			if (!(c & 0x1))
278298c7524SDarrick J. Wong 				continue;
279298c7524SDarrick J. Wong 			d++;
280298c7524SDarrick J. Wong 
281963d96b5SDarrick J. Wong 			/* sysfs label */
282963d96b5SDarrick J. Wong 			iattr = data->attrs + data->num_attrs;
283963d96b5SDarrick J. Wong 			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
284963d96b5SDarrick J. Wong 				 "temp%d_label", d);
285963d96b5SDarrick J. Wong 			iattr->s_attr.dev_attr.attr.name = iattr->name;
2862f8f7102SGuenter Roeck 			iattr->s_attr.dev_attr.attr.mode = 0444;
287963d96b5SDarrick J. Wong 			iattr->s_attr.dev_attr.show = show_label;
288963d96b5SDarrick J. Wong 			iattr->s_attr.index = k;
2890e6c7870SKAMEZAWA Hiroyuki 			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
290963d96b5SDarrick J. Wong 			res = device_create_file(&pdev->dev,
291963d96b5SDarrick J. Wong 						 &iattr->s_attr.dev_attr);
292963d96b5SDarrick J. Wong 			if (res)
293963d96b5SDarrick J. Wong 				goto exit_remove;
294963d96b5SDarrick J. Wong 			data->num_attrs++;
295963d96b5SDarrick J. Wong 
296298c7524SDarrick J. Wong 			/* Temperature sysfs knob */
297298c7524SDarrick J. Wong 			iattr = data->attrs + data->num_attrs;
298298c7524SDarrick J. Wong 			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
299298c7524SDarrick J. Wong 				 "temp%d_input", d);
300298c7524SDarrick J. Wong 			iattr->s_attr.dev_attr.attr.name = iattr->name;
3012f8f7102SGuenter Roeck 			iattr->s_attr.dev_attr.attr.mode = 0444;
302298c7524SDarrick J. Wong 			iattr->s_attr.dev_attr.show = show_amb_temp;
303298c7524SDarrick J. Wong 			iattr->s_attr.index = k;
3040e6c7870SKAMEZAWA Hiroyuki 			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
305298c7524SDarrick J. Wong 			res = device_create_file(&pdev->dev,
306298c7524SDarrick J. Wong 						 &iattr->s_attr.dev_attr);
307298c7524SDarrick J. Wong 			if (res)
308298c7524SDarrick J. Wong 				goto exit_remove;
309298c7524SDarrick J. Wong 			data->num_attrs++;
310298c7524SDarrick J. Wong 
311298c7524SDarrick J. Wong 			/* Temperature min sysfs knob */
312298c7524SDarrick J. Wong 			iattr = data->attrs + data->num_attrs;
313298c7524SDarrick J. Wong 			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
314298c7524SDarrick J. Wong 				 "temp%d_min", d);
315298c7524SDarrick J. Wong 			iattr->s_attr.dev_attr.attr.name = iattr->name;
3162f8f7102SGuenter Roeck 			iattr->s_attr.dev_attr.attr.mode = 0644;
317298c7524SDarrick J. Wong 			iattr->s_attr.dev_attr.show = show_amb_min;
318298c7524SDarrick J. Wong 			iattr->s_attr.dev_attr.store = store_amb_min;
319298c7524SDarrick J. Wong 			iattr->s_attr.index = k;
3200e6c7870SKAMEZAWA Hiroyuki 			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
321298c7524SDarrick J. Wong 			res = device_create_file(&pdev->dev,
322298c7524SDarrick J. Wong 						 &iattr->s_attr.dev_attr);
323298c7524SDarrick J. Wong 			if (res)
324298c7524SDarrick J. Wong 				goto exit_remove;
325298c7524SDarrick J. Wong 			data->num_attrs++;
326298c7524SDarrick J. Wong 
327298c7524SDarrick J. Wong 			/* Temperature mid sysfs knob */
328298c7524SDarrick J. Wong 			iattr = data->attrs + data->num_attrs;
329298c7524SDarrick J. Wong 			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
330298c7524SDarrick J. Wong 				 "temp%d_mid", d);
331298c7524SDarrick J. Wong 			iattr->s_attr.dev_attr.attr.name = iattr->name;
3322f8f7102SGuenter Roeck 			iattr->s_attr.dev_attr.attr.mode = 0644;
333298c7524SDarrick J. Wong 			iattr->s_attr.dev_attr.show = show_amb_mid;
334298c7524SDarrick J. Wong 			iattr->s_attr.dev_attr.store = store_amb_mid;
335298c7524SDarrick J. Wong 			iattr->s_attr.index = k;
3360e6c7870SKAMEZAWA Hiroyuki 			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
337298c7524SDarrick J. Wong 			res = device_create_file(&pdev->dev,
338298c7524SDarrick J. Wong 						 &iattr->s_attr.dev_attr);
339298c7524SDarrick J. Wong 			if (res)
340298c7524SDarrick J. Wong 				goto exit_remove;
341298c7524SDarrick J. Wong 			data->num_attrs++;
342298c7524SDarrick J. Wong 
343298c7524SDarrick J. Wong 			/* Temperature max sysfs knob */
344298c7524SDarrick J. Wong 			iattr = data->attrs + data->num_attrs;
345298c7524SDarrick J. Wong 			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
346298c7524SDarrick J. Wong 				 "temp%d_max", d);
347298c7524SDarrick J. Wong 			iattr->s_attr.dev_attr.attr.name = iattr->name;
3482f8f7102SGuenter Roeck 			iattr->s_attr.dev_attr.attr.mode = 0644;
349298c7524SDarrick J. Wong 			iattr->s_attr.dev_attr.show = show_amb_max;
350298c7524SDarrick J. Wong 			iattr->s_attr.dev_attr.store = store_amb_max;
351298c7524SDarrick J. Wong 			iattr->s_attr.index = k;
3520e6c7870SKAMEZAWA Hiroyuki 			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
353298c7524SDarrick J. Wong 			res = device_create_file(&pdev->dev,
354298c7524SDarrick J. Wong 						 &iattr->s_attr.dev_attr);
355298c7524SDarrick J. Wong 			if (res)
356298c7524SDarrick J. Wong 				goto exit_remove;
357298c7524SDarrick J. Wong 			data->num_attrs++;
358298c7524SDarrick J. Wong 
359298c7524SDarrick J. Wong 			/* Temperature alarm sysfs knob */
360298c7524SDarrick J. Wong 			iattr = data->attrs + data->num_attrs;
361298c7524SDarrick J. Wong 			snprintf(iattr->name, AMB_SYSFS_NAME_LEN,
362298c7524SDarrick J. Wong 				 "temp%d_alarm", d);
363298c7524SDarrick J. Wong 			iattr->s_attr.dev_attr.attr.name = iattr->name;
3642f8f7102SGuenter Roeck 			iattr->s_attr.dev_attr.attr.mode = 0444;
365298c7524SDarrick J. Wong 			iattr->s_attr.dev_attr.show = show_amb_alarm;
366298c7524SDarrick J. Wong 			iattr->s_attr.index = k;
3670e6c7870SKAMEZAWA Hiroyuki 			sysfs_attr_init(&iattr->s_attr.dev_attr.attr);
368298c7524SDarrick J. Wong 			res = device_create_file(&pdev->dev,
369298c7524SDarrick J. Wong 						 &iattr->s_attr.dev_attr);
370298c7524SDarrick J. Wong 			if (res)
371298c7524SDarrick J. Wong 				goto exit_remove;
372298c7524SDarrick J. Wong 			data->num_attrs++;
373298c7524SDarrick J. Wong 		}
374298c7524SDarrick J. Wong 	}
375298c7524SDarrick J. Wong 
376298c7524SDarrick J. Wong 	res = device_create_file(&pdev->dev, &dev_attr_name);
377298c7524SDarrick J. Wong 	if (res)
378298c7524SDarrick J. Wong 		goto exit_remove;
379298c7524SDarrick J. Wong 
380298c7524SDarrick J. Wong 	data->hwmon_dev = hwmon_device_register(&pdev->dev);
381298c7524SDarrick J. Wong 	if (IS_ERR(data->hwmon_dev)) {
382298c7524SDarrick J. Wong 		res = PTR_ERR(data->hwmon_dev);
383298c7524SDarrick J. Wong 		goto exit_remove;
384298c7524SDarrick J. Wong 	}
385298c7524SDarrick J. Wong 
386298c7524SDarrick J. Wong 	return res;
387298c7524SDarrick J. Wong 
388298c7524SDarrick J. Wong exit_remove:
389298c7524SDarrick J. Wong 	device_remove_file(&pdev->dev, &dev_attr_name);
390298c7524SDarrick J. Wong 	for (i = 0; i < data->num_attrs; i++)
391298c7524SDarrick J. Wong 		device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr);
392298c7524SDarrick J. Wong 	kfree(data->attrs);
393298c7524SDarrick J. Wong 
394298c7524SDarrick J. Wong 	return res;
395298c7524SDarrick J. Wong }
396298c7524SDarrick J. Wong 
i5k_amb_add(void)3976c931ae1SBill Pemberton static int i5k_amb_add(void)
398298c7524SDarrick J. Wong {
3999e444234SColin Ian King 	int res;
400298c7524SDarrick J. Wong 
401298c7524SDarrick J. Wong 	/* only ever going to be one of these */
402298c7524SDarrick J. Wong 	amb_pdev = platform_device_alloc(DRVNAME, 0);
403298c7524SDarrick J. Wong 	if (!amb_pdev)
404298c7524SDarrick J. Wong 		return -ENOMEM;
405298c7524SDarrick J. Wong 
406298c7524SDarrick J. Wong 	res = platform_device_add(amb_pdev);
407298c7524SDarrick J. Wong 	if (res)
408298c7524SDarrick J. Wong 		goto err;
409298c7524SDarrick J. Wong 	return 0;
410298c7524SDarrick J. Wong 
411298c7524SDarrick J. Wong err:
412298c7524SDarrick J. Wong 	platform_device_put(amb_pdev);
413298c7524SDarrick J. Wong 	return res;
414298c7524SDarrick J. Wong }
415298c7524SDarrick J. Wong 
i5k_find_amb_registers(struct i5k_amb_data * data,unsigned long devid)4166c931ae1SBill Pemberton static int i5k_find_amb_registers(struct i5k_amb_data *data,
417b8fdaf5aSDarrick J. Wong 					    unsigned long devid)
418298c7524SDarrick J. Wong {
419298c7524SDarrick J. Wong 	struct pci_dev *pcidev;
420298c7524SDarrick J. Wong 	u32 val32;
421298c7524SDarrick J. Wong 	int res = -ENODEV;
422298c7524SDarrick J. Wong 
423298c7524SDarrick J. Wong 	/* Find AMB register memory space */
424298c7524SDarrick J. Wong 	pcidev = pci_get_device(PCI_VENDOR_ID_INTEL,
425b8fdaf5aSDarrick J. Wong 				devid,
426298c7524SDarrick J. Wong 				NULL);
427298c7524SDarrick J. Wong 	if (!pcidev)
428298c7524SDarrick J. Wong 		return -ENODEV;
429298c7524SDarrick J. Wong 
4302207515dSSaheed O. Bolarinwa 	pci_read_config_dword(pcidev, I5K_REG_AMB_BASE_ADDR, &val32);
4312207515dSSaheed O. Bolarinwa 	if (val32 == (u32)~0)
432298c7524SDarrick J. Wong 		goto out;
433298c7524SDarrick J. Wong 	data->amb_base = val32;
434298c7524SDarrick J. Wong 
4352207515dSSaheed O. Bolarinwa 	pci_read_config_dword(pcidev, I5K_REG_AMB_LEN_ADDR, &val32);
4362207515dSSaheed O. Bolarinwa 	if (val32 == (u32)~0)
437298c7524SDarrick J. Wong 		goto out;
438298c7524SDarrick J. Wong 	data->amb_len = val32;
439298c7524SDarrick J. Wong 
440298c7524SDarrick J. Wong 	/* Is it big enough? */
441298c7524SDarrick J. Wong 	if (data->amb_len < AMB_CONFIG_SIZE * MAX_AMBS) {
442298c7524SDarrick J. Wong 		dev_err(&pcidev->dev, "AMB region too small!\n");
443298c7524SDarrick J. Wong 		goto out;
444298c7524SDarrick J. Wong 	}
445298c7524SDarrick J. Wong 
446298c7524SDarrick J. Wong 	res = 0;
447298c7524SDarrick J. Wong out:
448298c7524SDarrick J. Wong 	pci_dev_put(pcidev);
449298c7524SDarrick J. Wong 	return res;
450298c7524SDarrick J. Wong }
451298c7524SDarrick J. Wong 
i5k_channel_probe(u16 * amb_present,unsigned long dev_id)4526c931ae1SBill Pemberton static int i5k_channel_probe(u16 *amb_present, unsigned long dev_id)
453298c7524SDarrick J. Wong {
454298c7524SDarrick J. Wong 	struct pci_dev *pcidev;
455298c7524SDarrick J. Wong 	u16 val16;
456298c7524SDarrick J. Wong 	int res = -ENODEV;
457298c7524SDarrick J. Wong 
458298c7524SDarrick J. Wong 	/* Copy the DIMM presence map for these two channels */
459298c7524SDarrick J. Wong 	pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, dev_id, NULL);
460298c7524SDarrick J. Wong 	if (!pcidev)
461298c7524SDarrick J. Wong 		return -ENODEV;
462298c7524SDarrick J. Wong 
4632207515dSSaheed O. Bolarinwa 	pci_read_config_word(pcidev, I5K_REG_CHAN0_PRESENCE_ADDR, &val16);
4642207515dSSaheed O. Bolarinwa 	if (val16 == (u16)~0)
465298c7524SDarrick J. Wong 		goto out;
466298c7524SDarrick J. Wong 	amb_present[0] = val16;
467298c7524SDarrick J. Wong 
4682207515dSSaheed O. Bolarinwa 	pci_read_config_word(pcidev, I5K_REG_CHAN1_PRESENCE_ADDR, &val16);
4692207515dSSaheed O. Bolarinwa 	if (val16 == (u16)~0)
470298c7524SDarrick J. Wong 		goto out;
471298c7524SDarrick J. Wong 	amb_present[1] = val16;
472298c7524SDarrick J. Wong 
473298c7524SDarrick J. Wong 	res = 0;
474298c7524SDarrick J. Wong 
475298c7524SDarrick J. Wong out:
476298c7524SDarrick J. Wong 	pci_dev_put(pcidev);
477298c7524SDarrick J. Wong 	return res;
478298c7524SDarrick J. Wong }
479298c7524SDarrick J. Wong 
480b4cb0d4dSJean Delvare static struct {
481b4cb0d4dSJean Delvare 	unsigned long err;
482b4cb0d4dSJean Delvare 	unsigned long fbd0;
483a5977246SBill Pemberton } chipset_ids[]  = {
484b4cb0d4dSJean Delvare 	{ PCI_DEVICE_ID_INTEL_5000_ERR, PCI_DEVICE_ID_INTEL_5000_FBD0 },
485b4cb0d4dSJean Delvare 	{ PCI_DEVICE_ID_INTEL_5400_ERR, PCI_DEVICE_ID_INTEL_5400_FBD0 },
486b4cb0d4dSJean Delvare 	{ 0, 0 }
487b8fdaf5aSDarrick J. Wong };
488b8fdaf5aSDarrick J. Wong 
489c48a2916SGuenter Roeck #ifdef MODULE
4904d3f2414SArvind Yadav static const struct pci_device_id i5k_amb_ids[] = {
4916e31eb2bSDarrick J. Wong 	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5000_ERR) },
4926e31eb2bSDarrick J. Wong 	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_5400_ERR) },
4936e31eb2bSDarrick J. Wong 	{ 0, }
4946e31eb2bSDarrick J. Wong };
4956e31eb2bSDarrick J. Wong MODULE_DEVICE_TABLE(pci, i5k_amb_ids);
496c48a2916SGuenter Roeck #endif
4976e31eb2bSDarrick J. Wong 
i5k_amb_probe(struct platform_device * pdev)4986c931ae1SBill Pemberton static int i5k_amb_probe(struct platform_device *pdev)
499298c7524SDarrick J. Wong {
500298c7524SDarrick J. Wong 	struct i5k_amb_data *data;
501298c7524SDarrick J. Wong 	struct resource *reso;
502b4cb0d4dSJean Delvare 	int i, res;
503298c7524SDarrick J. Wong 
504298c7524SDarrick J. Wong 	data = kzalloc(sizeof(*data), GFP_KERNEL);
505298c7524SDarrick J. Wong 	if (!data)
506298c7524SDarrick J. Wong 		return -ENOMEM;
507298c7524SDarrick J. Wong 
508298c7524SDarrick J. Wong 	/* Figure out where the AMB registers live */
509b8fdaf5aSDarrick J. Wong 	i = 0;
510b8fdaf5aSDarrick J. Wong 	do {
511b4cb0d4dSJean Delvare 		res = i5k_find_amb_registers(data, chipset_ids[i].err);
512b4cb0d4dSJean Delvare 		if (res == 0)
513b4cb0d4dSJean Delvare 			break;
514b8fdaf5aSDarrick J. Wong 		i++;
515b4cb0d4dSJean Delvare 	} while (chipset_ids[i].err);
516b8fdaf5aSDarrick J. Wong 
517298c7524SDarrick J. Wong 	if (res)
518298c7524SDarrick J. Wong 		goto err;
519298c7524SDarrick J. Wong 
520298c7524SDarrick J. Wong 	/* Copy the DIMM presence map for the first two channels */
521b4cb0d4dSJean Delvare 	res = i5k_channel_probe(&data->amb_present[0], chipset_ids[i].fbd0);
522298c7524SDarrick J. Wong 	if (res)
523298c7524SDarrick J. Wong 		goto err;
524298c7524SDarrick J. Wong 
525298c7524SDarrick J. Wong 	/* Copy the DIMM presence map for the optional second two channels */
526b4cb0d4dSJean Delvare 	i5k_channel_probe(&data->amb_present[2], chipset_ids[i].fbd0 + 1);
527298c7524SDarrick J. Wong 
528298c7524SDarrick J. Wong 	/* Set up resource regions */
529298c7524SDarrick J. Wong 	reso = request_mem_region(data->amb_base, data->amb_len, DRVNAME);
530298c7524SDarrick J. Wong 	if (!reso) {
531298c7524SDarrick J. Wong 		res = -EBUSY;
532298c7524SDarrick J. Wong 		goto err;
533298c7524SDarrick J. Wong 	}
534298c7524SDarrick J. Wong 
5354bdc0d67SChristoph Hellwig 	data->amb_mmio = ioremap(data->amb_base, data->amb_len);
536298c7524SDarrick J. Wong 	if (!data->amb_mmio) {
537298c7524SDarrick J. Wong 		res = -EBUSY;
538298c7524SDarrick J. Wong 		goto err_map_failed;
539298c7524SDarrick J. Wong 	}
540298c7524SDarrick J. Wong 
541298c7524SDarrick J. Wong 	platform_set_drvdata(pdev, data);
542298c7524SDarrick J. Wong 
543298c7524SDarrick J. Wong 	res = i5k_amb_hwmon_init(pdev);
544298c7524SDarrick J. Wong 	if (res)
545298c7524SDarrick J. Wong 		goto err_init_failed;
546298c7524SDarrick J. Wong 
547298c7524SDarrick J. Wong 	return res;
548298c7524SDarrick J. Wong 
549298c7524SDarrick J. Wong err_init_failed:
550298c7524SDarrick J. Wong 	iounmap(data->amb_mmio);
551298c7524SDarrick J. Wong err_map_failed:
552298c7524SDarrick J. Wong 	release_mem_region(data->amb_base, data->amb_len);
553298c7524SDarrick J. Wong err:
554298c7524SDarrick J. Wong 	kfree(data);
555298c7524SDarrick J. Wong 	return res;
556298c7524SDarrick J. Wong }
557298c7524SDarrick J. Wong 
i5k_amb_remove(struct platform_device * pdev)558281dfd0bSBill Pemberton static int i5k_amb_remove(struct platform_device *pdev)
559298c7524SDarrick J. Wong {
560298c7524SDarrick J. Wong 	int i;
561298c7524SDarrick J. Wong 	struct i5k_amb_data *data = platform_get_drvdata(pdev);
562298c7524SDarrick J. Wong 
563298c7524SDarrick J. Wong 	hwmon_device_unregister(data->hwmon_dev);
564298c7524SDarrick J. Wong 	device_remove_file(&pdev->dev, &dev_attr_name);
565298c7524SDarrick J. Wong 	for (i = 0; i < data->num_attrs; i++)
566298c7524SDarrick J. Wong 		device_remove_file(&pdev->dev, &data->attrs[i].s_attr.dev_attr);
567298c7524SDarrick J. Wong 	kfree(data->attrs);
568298c7524SDarrick J. Wong 	iounmap(data->amb_mmio);
569298c7524SDarrick J. Wong 	release_mem_region(data->amb_base, data->amb_len);
570298c7524SDarrick J. Wong 	kfree(data);
571298c7524SDarrick J. Wong 	return 0;
572298c7524SDarrick J. Wong }
573298c7524SDarrick J. Wong 
574298c7524SDarrick J. Wong static struct platform_driver i5k_amb_driver = {
575298c7524SDarrick J. Wong 	.driver = {
576298c7524SDarrick J. Wong 		.name = DRVNAME,
577298c7524SDarrick J. Wong 	},
578298c7524SDarrick J. Wong 	.probe = i5k_amb_probe,
5799e5e9b7aSBill Pemberton 	.remove = i5k_amb_remove,
580298c7524SDarrick J. Wong };
581298c7524SDarrick J. Wong 
i5k_amb_init(void)582298c7524SDarrick J. Wong static int __init i5k_amb_init(void)
583298c7524SDarrick J. Wong {
584298c7524SDarrick J. Wong 	int res;
585298c7524SDarrick J. Wong 
586298c7524SDarrick J. Wong 	res = platform_driver_register(&i5k_amb_driver);
587298c7524SDarrick J. Wong 	if (res)
588298c7524SDarrick J. Wong 		return res;
589298c7524SDarrick J. Wong 
590298c7524SDarrick J. Wong 	res = i5k_amb_add();
591298c7524SDarrick J. Wong 	if (res)
592298c7524SDarrick J. Wong 		platform_driver_unregister(&i5k_amb_driver);
593298c7524SDarrick J. Wong 
594298c7524SDarrick J. Wong 	return res;
595298c7524SDarrick J. Wong }
596298c7524SDarrick J. Wong 
i5k_amb_exit(void)597298c7524SDarrick J. Wong static void __exit i5k_amb_exit(void)
598298c7524SDarrick J. Wong {
599298c7524SDarrick J. Wong 	platform_device_unregister(amb_pdev);
600298c7524SDarrick J. Wong 	platform_driver_unregister(&i5k_amb_driver);
601298c7524SDarrick J. Wong }
602298c7524SDarrick J. Wong 
6035407e051SDarrick J. Wong MODULE_AUTHOR("Darrick J. Wong <darrick.wong@oracle.com>");
604298c7524SDarrick J. Wong MODULE_DESCRIPTION("Intel 5000 chipset FB-DIMM AMB temperature sensor");
605298c7524SDarrick J. Wong MODULE_LICENSE("GPL");
606298c7524SDarrick J. Wong 
607298c7524SDarrick J. Wong module_init(i5k_amb_init);
608298c7524SDarrick J. Wong module_exit(i5k_amb_exit);
609