Skip to content
This repository
Browse code

Added hwmon/thermal driver for reporting core temperature. Thanks Dorian

  • Loading branch information...
commit e33263e7eab2d05349b438f6a4a8d1924686a824 1 parent c8edb23
popcornmix authored September 13, 2012
11  arch/arm/mach-bcm2708/bcm2708.c
@@ -592,6 +592,14 @@ static struct platform_device bcm2708_bsc1_device = {
592 592
 	.resource = bcm2708_bsc1_resources,
593 593
 };
594 594
 
  595
+static struct platform_device bcm2835_hwmon_device = {
  596
+	.name = "bcm2835_hwmon",
  597
+};
  598
+
  599
+static struct platform_device bcm2835_thermal_device = {
  600
+	.name = "bcm2835_thermal",
  601
+};
  602
+
595 603
 int __init bcm_register_device(struct platform_device *pdev)
596 604
 {
597 605
 	int ret;
@@ -656,6 +664,9 @@ void __init bcm2708_init(void)
656 664
 	bcm_register_device(&bcm2708_bsc0_device);
657 665
 	bcm_register_device(&bcm2708_bsc1_device);
658 666
 
  667
+	bcm_register_device(&bcm2835_hwmon_device);
  668
+	bcm_register_device(&bcm2835_thermal_device);
  669
+
659 670
 #ifdef CONFIG_BCM2708_VCMEM
660 671
 	{
661 672
 		extern void vc_mem_connected_init(void);
9  drivers/hwmon/Kconfig
@@ -1361,6 +1361,15 @@ config SENSORS_MC13783_ADC
1361 1361
         help
1362 1362
           Support for the A/D converter on MC13783 PMIC.
1363 1363
 
  1364
+config SENSORS_BCM2835
  1365
+	tristate "Broadcom BCM2835 HWMON Driver"
  1366
+	help
  1367
+	  If you say yes here you get support for the hardware
  1368
+	  monitoring features of the BCM2835 Chip
  1369
+
  1370
+	  This driver can also be built as a module.  If so, the module
  1371
+	  will be called bcm2835-hwmon.
  1372
+
1364 1373
 if ACPI
1365 1374
 
1366 1375
 comment "ACPI drivers"
1  drivers/hwmon/Makefile
@@ -125,6 +125,7 @@ obj-$(CONFIG_SENSORS_W83L785TS)	+= w83l785ts.o
125 125
 obj-$(CONFIG_SENSORS_W83L786NG)	+= w83l786ng.o
126 126
 obj-$(CONFIG_SENSORS_WM831X)	+= wm831x-hwmon.o
127 127
 obj-$(CONFIG_SENSORS_WM8350)	+= wm8350-hwmon.o
  128
+obj-$(CONFIG_SENSORS_BCM2835)	+= bcm2835-hwmon.o
128 129
 
129 130
 obj-$(CONFIG_PMBUS)		+= pmbus/
130 131
 
211  drivers/hwmon/bcm2835-hwmon.c
... ...
@@ -0,0 +1,211 @@
  1
+//bcm2835-hwmon.c
  2
+
  3
+#include <linux/kernel.h>
  4
+#include <linux/module.h>
  5
+#include <linux/init.h>
  6
+#include <linux/hwmon.h>
  7
+#include <linux/hwmon-sysfs.h>
  8
+#include <linux/platform_device.h>
  9
+#include <linux/sysfs.h>
  10
+#include <mach/vcio.h>
  11
+#include <linux/slab.h>
  12
+#include <linux/err.h>
  13
+
  14
+#define MODULE_NAME "bcm2835_hwmon"
  15
+
  16
+/*#define HWMON_DEBUG_ENABLE*/
  17
+
  18
+#ifdef HWMON_DEBUG_ENABLE
  19
+#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
  20
+#else
  21
+#define print_debug(fmt,...)
  22
+#endif
  23
+#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__)
  24
+#define print_info(fmt,...) printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ##__VA_ARGS__)
  25
+
  26
+#define VC_TAG_GET_TEMP 0x00030006
  27
+#define VC_TAG_GET_MAX_TEMP 0x0003000A
  28
+struct bcm2835_hwmon_data {
  29
+	struct device *hwmon_dev;
  30
+};
  31
+
  32
+/* --- STRUCTS --- */
  33
+
  34
+/* tag part of the message */
  35
+struct vc_msg_tag {
  36
+	uint32_t tag_id;		/* the tag ID for the temperature */
  37
+	uint32_t buffer_size;	/* size of the buffer (should be 8) */
  38
+	uint32_t request_code;	/* identifies message as a request (should be 0) */
  39
+	uint32_t id;			/* extra ID field (should be 0) */
  40
+	uint32_t val;			/* returned value of the temperature */
  41
+};
  42
+
  43
+/* message structure to be sent to videocore */
  44
+struct vc_msg {
  45
+	uint32_t msg_size;		/* simply, sizeof(struct vc_msg) */
  46
+	uint32_t request_code;		/* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */
  47
+	struct vc_msg_tag tag;		/* the tag structure above to make */
  48
+	uint32_t end_tag;		/* an end identifier, should be set to NULL */
  49
+};
  50
+
  51
+typedef enum {
  52
+	TEMP,
  53
+	MAX_TEMP,
  54
+} temp_type;
  55
+
  56
+/* --- PROTOTYPES --- */
  57
+static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf);
  58
+static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf);
  59
+
  60
+/* --- GLOBALS --- */
  61
+
  62
+static struct bcm2835_hwmon_data *bcm2835_data;
  63
+static struct platform_driver bcm2835_hwmon_driver;
  64
+
  65
+static SENSOR_DEVICE_ATTR(name, S_IRUGO,bcm2835_get_name,NULL,0);
  66
+static SENSOR_DEVICE_ATTR(temp1_input,S_IRUGO,bcm2835_get_temp,NULL,TEMP);
  67
+static SENSOR_DEVICE_ATTR(temp,S_IRUGO,bcm2835_get_temp,NULL,TEMP);
  68
+static SENSOR_DEVICE_ATTR(temp1_max,S_IRUGO,bcm2835_get_temp,NULL,MAX_TEMP);
  69
+static SENSOR_DEVICE_ATTR(trip_point_0_temp,S_IRUGO,bcm2835_get_temp,NULL,MAX_TEMP);
  70
+
  71
+static struct attribute* bcm2835_attributes[] = {
  72
+	&sensor_dev_attr_name.dev_attr.attr,
  73
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
  74
+	&sensor_dev_attr_temp1_max.dev_attr.attr,
  75
+	&sensor_dev_attr_temp.dev_attr.attr,
  76
+	&sensor_dev_attr_trip_point_0_temp.dev_attr.attr,
  77
+	NULL,
  78
+};
  79
+
  80
+static struct attribute_group bcm2835_attr_group = {
  81
+	.attrs = bcm2835_attributes,
  82
+};
  83
+
  84
+/* --- FUNCTIONS --- */
  85
+
  86
+static ssize_t bcm2835_get_name(struct device *dev, struct device_attribute *attr, char *buf)
  87
+{
  88
+	return sprintf(buf,"bcm2835_hwmon\n");
  89
+}
  90
+
  91
+static ssize_t bcm2835_get_temp(struct device *dev, struct device_attribute *attr, char *buf)
  92
+{
  93
+	struct vc_msg msg;
  94
+	int result;
  95
+	uint temp = 0;
  96
+	int index = ((struct sensor_device_attribute*)to_sensor_dev_attr(attr))->index;
  97
+
  98
+	print_debug("IN");
  99
+
  100
+	/* wipe all previous message data */
  101
+	memset(&msg, 0, sizeof msg);
  102
+
  103
+	/* determine the message type */
  104
+	if(index == TEMP)
  105
+		msg.tag.tag_id = VC_TAG_GET_TEMP;
  106
+	else if (index == MAX_TEMP)
  107
+		msg.tag.tag_id = VC_TAG_GET_MAX_TEMP;
  108
+	else
  109
+	{
  110
+		print_debug("Unknown temperature message!");
  111
+		return -EINVAL;
  112
+	}
  113
+
  114
+	msg.msg_size = sizeof msg;
  115
+	msg.tag.buffer_size = 8;
  116
+
  117
+	/* send the message */
  118
+	result = bcm_mailbox_property(&msg, sizeof msg);
  119
+
  120
+	/* check if it was all ok and return the rate in milli degrees C */
  121
+	if (result == 0 && (msg.request_code & 0x80000000))
  122
+		temp = (uint)msg.tag.val;
  123
+	#ifdef HWMON_DEBUG_ENABLE
  124
+	else
  125
+		print_debug("Failed to get temperature!");
  126
+	#endif
  127
+	print_debug("Got temperature as %u",temp);
  128
+	print_debug("OUT");
  129
+	return sprintf(buf, "%u\n", temp);
  130
+}
  131
+
  132
+
  133
+static int bcm2835_hwmon_probe(struct platform_device *pdev)
  134
+{
  135
+	int err;
  136
+
  137
+	print_debug("IN");
  138
+	print_debug("HWMON Driver has been probed!");
  139
+
  140
+	/* check that the device isn't null!*/
  141
+	if(pdev == NULL)
  142
+	{
  143
+		print_debug("Platform device is empty!");
  144
+		return -ENODEV;
  145
+	}
  146
+
  147
+	/* allocate memory for neccessary data */
  148
+	bcm2835_data = kzalloc(sizeof(struct bcm2835_hwmon_data),GFP_KERNEL);
  149
+	if(!bcm2835_data)
  150
+	{
  151
+		print_debug("Unable to allocate memory for hwmon data!");
  152
+		err = -ENOMEM;
  153
+		goto kzalloc_error;
  154
+	}
  155
+
  156
+	/* create the sysfs files */
  157
+	if(sysfs_create_group(&pdev->dev.kobj, &bcm2835_attr_group))
  158
+	{
  159
+		print_debug("Unable to create sysfs files!");
  160
+		err = -EFAULT;
  161
+		goto sysfs_error;
  162
+	}
  163
+
  164
+	/* register the hwmon device */
  165
+	bcm2835_data->hwmon_dev = hwmon_device_register(&pdev->dev);
  166
+	if (IS_ERR(bcm2835_data->hwmon_dev))
  167
+	{
  168
+		err = PTR_ERR(bcm2835_data->hwmon_dev);
  169
+		goto hwmon_error;
  170
+	}
  171
+	print_debug("OUT");
  172
+	return 0;
  173
+
  174
+	/* error goto's */
  175
+	hwmon_error:
  176
+	sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group);
  177
+
  178
+	sysfs_error:
  179
+	kfree(bcm2835_data);
  180
+
  181
+	kzalloc_error:
  182
+
  183
+	return err;
  184
+
  185
+}
  186
+
  187
+static int bcm2835_hwmon_remove(struct platform_device *pdev)
  188
+{
  189
+	print_debug("IN");
  190
+	hwmon_device_unregister(bcm2835_data->hwmon_dev);
  191
+
  192
+	sysfs_remove_group(&pdev->dev.kobj, &bcm2835_attr_group);
  193
+	print_debug("OUT");
  194
+	return 0;
  195
+}
  196
+
  197
+/* Hwmon Driver */
  198
+static struct platform_driver bcm2835_hwmon_driver = {
  199
+	.probe = bcm2835_hwmon_probe,
  200
+	.remove = bcm2835_hwmon_remove,
  201
+	.driver = {
  202
+				.name = "bcm2835_hwmon",
  203
+				.owner = THIS_MODULE,
  204
+			},
  205
+};
  206
+
  207
+MODULE_LICENSE("GPL");
  208
+MODULE_AUTHOR("Dorian Peake");
  209
+MODULE_DESCRIPTION("HW Monitor driver for bcm2835 chip");
  210
+
  211
+module_platform_driver(bcm2835_hwmon_driver);
10  drivers/thermal/Kconfig
@@ -18,3 +18,13 @@ config THERMAL_HWMON
18 18
 	depends on THERMAL
19 19
 	depends on HWMON=y || HWMON=THERMAL
20 20
 	default y
  21
+
  22
+if THERMAL
  23
+
  24
+config THERMAL_BCM2835
  25
+	tristate "BCM2835 Thermal Driver"
  26
+	help
  27
+	  This will enable temperature monitoring for the Broadcom BCM2835
  28
+	  chip. If built as a module, it will be called 'bcm2835-thermal'.
  29
+
  30
+endif # THERMAL_BCM2835
1  drivers/thermal/Makefile
@@ -3,3 +3,4 @@
3 3
 #
4 4
 
5 5
 obj-$(CONFIG_THERMAL)		+= thermal_sys.o
  6
+obj-$(CONFIG_THERMAL_BCM2835)	+= bcm2835-thermal.o
195  drivers/thermal/bcm2835-thermal.c
... ...
@@ -0,0 +1,195 @@
  1
+//bcm2835-thermal.c
  2
+#include <linux/kernel.h>
  3
+#include <linux/module.h>
  4
+#include <linux/init.h>
  5
+#include <linux/platform_device.h>
  6
+#include <linux/slab.h>
  7
+#include <linux/sysfs.h>
  8
+#include <mach/vcio.h>
  9
+#include <linux/thermal.h>
  10
+
  11
+
  12
+/* --- DEFINITIONS --- */
  13
+#define MODULE_NAME "bcm2835_thermal"
  14
+
  15
+/*#define THERMAL_DEBUG_ENABLE*/
  16
+
  17
+#ifdef THERMAL_DEBUG_ENABLE
  18
+#define print_debug(fmt,...) printk(KERN_INFO "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__, __LINE__, ##__VA_ARGS__)
  19
+#else
  20
+#define print_debug(fmt,...)
  21
+#endif
  22
+#define print_err(fmt,...) printk(KERN_ERR "%s:%s:%d: "fmt"\n", MODULE_NAME, __func__,__LINE__, ##__VA_ARGS__)
  23
+#define print_info(fmt,...) printk(KERN_INFO "%s: "fmt"\n", MODULE_NAME, ##__VA_ARGS__)
  24
+
  25
+#define VC_TAG_GET_TEMP 0x00030006
  26
+#define VC_TAG_GET_MAX_TEMP 0x0003000A
  27
+
  28
+typedef enum {
  29
+	TEMP,
  30
+	MAX_TEMP,
  31
+} temp_type;
  32
+
  33
+/* --- STRUCTS --- */
  34
+/* tag part of the message */
  35
+struct vc_msg_tag {
  36
+	uint32_t tag_id;		/* the tag ID for the temperature */
  37
+	uint32_t buffer_size;	/* size of the buffer (should be 8) */
  38
+	uint32_t request_code;	/* identifies message as a request (should be 0) */
  39
+	uint32_t id;			/* extra ID field (should be 0) */
  40
+	uint32_t val;			/* returned value of the temperature */
  41
+};
  42
+
  43
+/* message structure to be sent to videocore */
  44
+struct vc_msg {
  45
+	uint32_t msg_size;		/* simply, sizeof(struct vc_msg) */
  46
+	uint32_t request_code;		/* holds various information like the success and number of bytes returned (refer to mailboxes wiki) */
  47
+	struct vc_msg_tag tag;		/* the tag structure above to make */
  48
+	uint32_t end_tag;		/* an end identifier, should be set to NULL */
  49
+};
  50
+
  51
+struct bcm2835_thermal_data {
  52
+	struct thermal_zone_device *thermal_dev;
  53
+	struct vc_msg msg;
  54
+};
  55
+
  56
+/* --- PROTOTYPES --- */
  57
+static int bcm2835_get_temp(struct thermal_zone_device *thermal_dev, unsigned long *);
  58
+static int bcm2835_get_max_temp(struct thermal_zone_device *thermal_dev, int, unsigned long *);
  59
+static int bcm2835_get_trip_type(struct thermal_zone_device *thermal_dev, int trip_num, enum thermal_trip_type *trip_type);
  60
+static int bcm2835_get_mode(struct thermal_zone_device *thermal_dev, enum thermal_device_mode *dev_mode);
  61
+
  62
+/* --- GLOBALS --- */
  63
+static struct bcm2835_thermal_data bcm2835_data;
  64
+
  65
+/* Thermal Device Operations */
  66
+static struct thermal_zone_device_ops ops;
  67
+
  68
+/* --- FUNCTIONS --- */
  69
+static int bcm2835_get_max_temp(struct thermal_zone_device *thermal_dev, int trip_num, unsigned long *temp)
  70
+{
  71
+	int result;
  72
+
  73
+	print_debug("IN");
  74
+
  75
+	/* wipe all previous message data */
  76
+	memset(&bcm2835_data.msg, 0, sizeof bcm2835_data.msg);
  77
+
  78
+	/* prepare message */
  79
+	bcm2835_data.msg.msg_size = sizeof bcm2835_data.msg;
  80
+	bcm2835_data.msg.tag.buffer_size = 8;
  81
+	bcm2835_data.msg.tag.tag_id = VC_TAG_GET_MAX_TEMP;
  82
+
  83
+	/* send the message */
  84
+	result = bcm_mailbox_property(&bcm2835_data.msg, sizeof bcm2835_data.msg);
  85
+
  86
+	/* check if it was all ok and return the rate in milli degrees C */
  87
+	if (result == 0 && (bcm2835_data.msg.request_code & 0x80000000))
  88
+		*temp = (uint)bcm2835_data.msg.tag.val;
  89
+	#ifdef THERMAL_DEBUG_ENABLE
  90
+	else
  91
+		print_debug("Failed to get temperature!");
  92
+	#endif
  93
+	print_debug("Got temperature as %u",(uint)*temp);
  94
+	print_debug("OUT");
  95
+	return 0;
  96
+}
  97
+
  98
+static int bcm2835_get_temp(struct thermal_zone_device *thermal_dev, unsigned long *temp)
  99
+{
  100
+	int result;
  101
+
  102
+	print_debug("IN");
  103
+
  104
+	/* wipe all previous message data */
  105
+	memset(&bcm2835_data.msg, 0, sizeof bcm2835_data.msg);
  106
+
  107
+	/* prepare message */
  108
+	bcm2835_data.msg.msg_size = sizeof bcm2835_data.msg;
  109
+	bcm2835_data.msg.tag.buffer_size = 8;
  110
+	bcm2835_data.msg.tag.tag_id = VC_TAG_GET_TEMP;
  111
+
  112
+	/* send the message */
  113
+	result = bcm_mailbox_property(&bcm2835_data.msg, sizeof bcm2835_data.msg);
  114
+
  115
+	/* check if it was all ok and return the rate in milli degrees C */
  116
+	if (result == 0 && (bcm2835_data.msg.request_code & 0x80000000))
  117
+		*temp = (uint)bcm2835_data.msg.tag.val;
  118
+	#ifdef THERMAL_DEBUG_ENABLE
  119
+	else
  120
+		print_debug("Failed to get temperature!");
  121
+	#endif
  122
+	print_debug("Got temperature as %u",(uint)*temp);
  123
+	print_debug("OUT");
  124
+	return 0;
  125
+}
  126
+
  127
+
  128
+static int bcm2835_get_trip_type(struct thermal_zone_device * thermal_dev, int trip_num, enum thermal_trip_type *trip_type)
  129
+{
  130
+	*trip_type = THERMAL_TRIP_HOT;
  131
+	return 0;
  132
+}
  133
+
  134
+
  135
+static int bcm2835_get_mode(struct thermal_zone_device *thermal_dev, enum thermal_device_mode *dev_mode)
  136
+{
  137
+	*dev_mode = THERMAL_DEVICE_ENABLED;
  138
+	return 0;
  139
+}
  140
+
  141
+
  142
+static int bcm2835_thermal_probe(struct platform_device *pdev)
  143
+{
  144
+	print_debug("IN");
  145
+	print_debug("THERMAL Driver has been probed!");
  146
+
  147
+	/* check that the device isn't null!*/
  148
+	if(pdev == NULL)
  149
+	{
  150
+		print_debug("Platform device is empty!");
  151
+		return -ENODEV;
  152
+	}
  153
+
  154
+	if(!(bcm2835_data.thermal_dev = thermal_zone_device_register("bcm2835_thermal",	1, NULL, &ops,1,1,1000,1000)))
  155
+	{
  156
+		print_debug("Unable to register the thermal device!");
  157
+		return -EFAULT;
  158
+	}
  159
+	return 0;
  160
+}
  161
+
  162
+
  163
+static int bcm2835_thermal_remove(struct platform_device *pdev)
  164
+{
  165
+	print_debug("IN");
  166
+
  167
+	thermal_zone_device_unregister(bcm2835_data.thermal_dev);
  168
+
  169
+	print_debug("OUT");
  170
+
  171
+	return 0;
  172
+}
  173
+
  174
+static struct thermal_zone_device_ops ops  = {
  175
+	.get_temp = bcm2835_get_temp,
  176
+	.get_trip_temp = bcm2835_get_max_temp,
  177
+	.get_trip_type = bcm2835_get_trip_type,
  178
+	.get_mode = bcm2835_get_mode,
  179
+};
  180
+
  181
+/* Thermal Driver */
  182
+static struct platform_driver bcm2835_thermal_driver = {
  183
+	.probe = bcm2835_thermal_probe,
  184
+	.remove = bcm2835_thermal_remove,
  185
+	.driver = {
  186
+				.name = "bcm2835_thermal",
  187
+				.owner = THIS_MODULE,
  188
+			},
  189
+};
  190
+
  191
+MODULE_LICENSE("GPL");
  192
+MODULE_AUTHOR("Dorian Peake");
  193
+MODULE_DESCRIPTION("Thermal driver for bcm2835 chip");
  194
+
  195
+module_platform_driver(bcm2835_thermal_driver);

1 note on commit e33263e

Christopher Perrin

I had been wondering if someone writes a kernel driver for the termal sensor and TADAAA!! here it is. Good work!

Riemer van der Zee

If the tempeture read fails, maybe we should return an error code instead of returning 0 and keep *temp unchanged? The driver is a useful addition though, something I just needed :)

Please sign in to comment.
Something went wrong with that request. Please try again.