/*
* Copyright (C) 2007-2009 Luca Tettamanti <kronos.it@gmail.com>
*
* This file is released under the GPLv2
* See COPYING in the top level directory of the kernel tree.
*/
#include <linux/kernel.h>
#include <linux/hwmon.h>
#include <linux/list.h>
#include <linux/module.h>
#include <acpi/acpi.h>
#include <acpi/acpixf.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#define ATK_HID "ATK0110"
/* Minimum time between readings, enforced in order to avoid
* hogging the CPU.
*/
#define CACHE_TIME HZ
#define BOARD_ID "MBIF"
#define METHOD_ENUMERATE "GGRP"
#define METHOD_READ "GITM"
#define METHOD_WRITE "SITM"
#define METHOD_OLD_READ_TMP "RTMP"
#define METHOD_OLD_READ_VLT "RVLT"
#define METHOD_OLD_READ_FAN "RFAN"
#define METHOD_OLD_ENUM_TMP "TSIF"
#define METHOD_OLD_ENUM_VLT "VSIF"
#define METHOD_OLD_ENUM_FAN "FSIF"
#define ATK_MUX_HWMON 0x00000006ULL
#define ATK_CLASS_MASK 0xff000000ULL
#define ATK_CLASS_FREQ_CTL 0x03000000ULL
#define ATK_CLASS_FAN_CTL 0x04000000ULL
#define ATK_CLASS_HWMON 0x06000000ULL
#define ATK_TYPE_MASK 0x00ff0000ULL
#define HWMON_TYPE_VOLT 0x00020000ULL
#define HWMON_TYPE_TEMP 0x00030000ULL
#define HWMON_TYPE_FAN 0x00040000ULL
#define HWMON_SENSOR_ID_MASK 0x0000ffffULL
enum atk_pack_member {
HWMON_PACK_FLAGS,
HWMON_PACK_NAME,
HWMON_PACK_LIMIT1,
HWMON_PACK_LIMIT2,
HWMON_PACK_ENABLE
};
/* New package format */
#define _HWMON_NEW_PACK_SIZE 7
#define _HWMON_NEW_PACK_FLAGS 0
#define _HWMON_NEW_PACK_NAME 1
#define _HWMON_NEW_PACK_UNK1 2
#define _HWMON_NEW_PACK_UNK2 3
#define _HWMON_NEW_PACK_LIMIT1 4
#define _HWMON_NEW_PACK_LIMIT2 5
#define _HWMON_NEW_PACK_ENABLE 6
/* Old package format */
#define _HWMON_OLD_PACK_SIZE 5
#define _HWMON_OLD_PACK_FLAGS 0
#define _HWMON_OLD_PACK_NAME 1
#define _HWMON_OLD_PACK_LIMIT1 2
#define _HWMON_OLD_PACK_LIMIT2 3
#define _HWMON_OLD_PACK_ENABLE 4
struct atk_data {
struct device *hwmon_dev;
acpi_handle atk_handle;
struct acpi_device *acpi_dev;
bool old_interface;
/* old interface */
acpi_handle rtmp_handle;
acpi_handle rvlt_handle;
acpi_handle rfan_handle;
/* new inteface */
acpi_handle enumerate_handle;
acpi_handle read_handle;
int voltage_count;
int temperature_count;
int fan_count;
struct list_head sensor_list;
};
typedef ssize_t (*sysfs_show_func)(struct device *dev,
struct device_attribute *attr, char *buf);
static const struct acpi_device_id atk_ids[] = {
{ATK_HID, 0},
{"", 0},
};
MODULE_DEVICE_TABLE(acpi, atk_ids);
#define ATTR_NAME_SIZE 16 /* Worst case is "tempN_input" */
struct atk_sensor_data {
struct list_head list;
struct atk_data *data;
struct device_attribute label_attr;
struct device_attribute input_attr;
struct device_attribute limit1_attr;
struct device_attribute limit2_attr;
char label_attr_name[ATTR_NAME_SIZE];
char input_attr_name[ATTR_NAME_SIZE];
char limit1_attr_name[ATTR_NAME_SIZE];
char limit2_attr_name[ATTR_NAME_SIZE];
u64 id;
u64 type;
u64 limit1;
u64 limit2;
u64 cached_value;
unsigned long last_updated; /* in jiffies */
bool is_valid;
char const *acpi_name;
};
struct atk_acpi_buffer_u64 {
union acpi_object buf;
u64 value;
};
static int atk_add(struct acpi_device *device);
static int atk_remove(struct acpi_device *device, int type);
static void atk_print_sensor(struct atk_data *data, union acpi_object *obj);
static int atk_read_value(struct atk_sensor_data *sensor, u64 *value);
static void atk_free_sensors(struct atk_data *data);
static struct acpi_driver atk_driver = {
.name = ATK_HID,
.class = "hwmon",
.ids = atk_ids,
.ops = {
.add = atk_add,
.remove = atk_remove,
},
};
#define input_to_atk_sensor(attr) \
container_of(attr, struct atk_sensor_data, input_attr)
#define label_to_atk_sensor(attr) \
container_of(attr, struct atk_sensor_data, label_attr)
#define limit1_to_atk_sensor(attr) \
container_of(attr, struct atk_sensor_data, limit1_attr)
#define limit2_to_atk_sensor(attr) \
container_of(attr, struct atk_sensor_data, limit2_attr)
static ssize_t atk_input_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct atk_sensor_data *s = input_to_atk_sensor(attr);
u64 value;
int err;
err = atk_read_value(s, &value);
if (err)
return err;
if (s->type == HWMON_TYPE_TEMP)
/* ACPI returns decidegree */
value *= 100;
return sprintf(buf, "%llu\n", value);
}
static ssize_t atk_label_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct atk_sensor_data *s = label_to_atk_sensor(attr);
return sprintf(buf, "%s\n", s->acpi_name);
}
static ssize_t atk_limit1_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct atk_sensor_data *s = limit1_to_atk_sensor(attr);
u64 value = s->limit1;
if (s->