aboutsummaryrefslogtreecommitdiffstats
path: root/include/linux/oprofile.h
blob: 5171639ecf0ff070e74d884deb8c969e3b88132d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/**
 * @file oprofile.h
 *
 * API for machine-specific interrupts to interface
 * to oprofile.
 *
 * @remark Copyright 2002 OProfile authors
 * @remark Read the file COPYING
 *
 * @author John Levon <levon@movementarian.org>
 */

#ifndef OPROFILE_H
#define OPROFILE_H

#include <linux/types.h>
#include <linux/spinlock.h>
#include <asm/atomic.h>
 
/* Each escaped entry is prefixed by ESCAPE_CODE
 * then one of the following codes, then the
 * relevant data.
 * These #defines live in this file so that arch-specific
 * buffer sync'ing code can access them.
 */
#define ESCAPE_CODE			~0UL
#define CTX_SWITCH_CODE			1
#define CPU_SWITCH_CODE			2
#define COOKIE_SWITCH_CODE		3
#define KERNEL_ENTER_SWITCH_CODE	4
#define KERNEL_EXIT_SWITCH_CODE		5
#define MODULE_LOADED_CODE		6
#define CTX_TGID_CODE			7
#define TRACE_BEGIN_CODE		8
#define TRACE_END_CODE			9
#define XEN_ENTER_SWITCH_CODE		10
#define SPU_PROFILING_CODE		11
#define SPU_CTX_SWITCH_CODE		12
#define IBS_FETCH_CODE			13
#define IBS_OP_CODE			14

struct super_block;
struct dentry;
struct file_operations;
struct pt_regs;
 
/* Operations structure to be filled in */
struct oprofile_operations {
	/* create any necessary configuration files in the oprofile fs.
	 * Optional. */
	int (*create_files)(struct super_block * sb, struct dentry * root);
	/* Do any necessary interrupt setup. Optional. */
	int (*setup)(void);
	/* Do any necessary interrupt shutdown. Optional. */
	void (*shutdown)(void);
	/* Start delivering interrupts. */
	int (*start)(void);
	/* Stop delivering interrupts. */
	void (*stop)(void);
	/* Arch-specific buffer sync functions.
	 * Return value = 0:  Success
	 * Return value = -1: Failure
	 * Return value = 1:  Run generic sync function
	 */
	int (*sync_start)(void);
	int (*sync_stop)(void);

	/* Initiate a stack backtrace. Optional. */
	void (*backtrace)(struct pt_regs * const regs, unsigned int depth);

	/* Multiplex between different events. Optional. */
	int (*switch_events)(void);
	/* CPU identification string. */
	char * cpu_type;
};

/**
 * One-time initialisation. *ops must be set to a filled-in
 * operations structure. This is called even in timer interrupt
 * mode so an arch can set a backtrace callback.
 *
 * If an error occurs, the fields should be left untouched.
 */
int oprofile_arch_init(struct oprofile_operations * ops);
 
/**
 * One-time exit/cleanup for the arch.
 */
void oprofile_arch_exit(void);

/**
 * Add a sample. This may be called from any context.
 */
void oprofile_add_sample(struct pt_regs * const regs, unsigned long event);

/**
 * Add an extended sample.  Use this when the PC is not from the regs, and
 * we cannot determine if we're in kernel mode from the regs.
 *
 * This function does perform a backtrace.
 *
 */
void oprofile_add_ext_sample(unsigned long pc, struct pt_regs * const regs,
				unsigned long event, int is_kernel);

/* Use this instead when the PC value is not from the regs. Doesn't
 * backtrace. */
void oprofile_add_pc(unsigned long pc, int is_kernel, unsigned long event);

/* add a backtrace entry, to be called from the ->backtrace callback */
void oprofile_add_trace(unsigned long eip);


/**
 * Create a file of the given name as a child of the given root, with
 * the specified file operations.
 */
int oprofilefs_create_file(struct super_block * sb, struct dentry * root,
	char const * name, const struct file_operations * fops);

int oprofilefs_create_file_perm(struct super_block * sb, struct dentry * root,
	char const * name, const struct file_operations * fops, int perm);
 
/** Create a file for read/write access to an unsigned long. */
int oprofilefs_create_ulong(struct super_block * sb, struct dentry * root,
	char const * name, ulong * val);
 
/** Create a file for read-only access to an unsigned long. */
int oprofilefs_create_ro_ulong(struct super_block * sb, struct dentry * root,
	char const * name, ulong * val);
 
/** Create a file for read-only access to an atomic_t. */
int oprofilefs_create_ro_atomic(struct super_block * sb, struct dentry * root,
	char const * name, atomic_t * val);
 
/** create a directory */
struct dentry * oprofilefs_mkdir(struct super_block * sb, struct dentry * root,
	char const * name);

/**
 * Write the given asciz string to the given user buffer @buf, updating *offset
 * appropriately. Returns bytes written or -EFAULT.
 */
ssize_t oprofilefs_str_to_user(char const * str, char __user * buf, size_t count, loff_t * offset);

/**
 * Convert an unsigned long value into ASCII and copy it to the user buffer @buf,
 * updating *offset appropriately. Returns bytes written or -EFAULT.
 */
ssize_t oprofilefs_ulong_to_user(unsigned long val, char __user * buf, size_t count, loff_t * offset);

/**
 * Read an ASCII string for a number from a userspace buffer and fill *val on success.
 * Returns 0 on success, < 0 on error.
 */
int oprofilefs_ulong_from_user(unsigned long * val, char const __user * buf, size_t count);

/** lock for read/write safety */
extern spinlock_t oprofilefs_lock;

/**
 * Add the contents of a circular buffer to the event buffer.
 */
void oprofile_put_buff(unsigned long *buf, unsigned int start,
			unsigned int stop, unsigned int max);

unsigned long oprofile_get_cpu_buffer_size(void);
void oprofile_cpu_buffer_inc_smpl_lost(void);
 
/* cpu buffer functions */

struct op_sample;

struct op_entry {
	struct ring_buffer_event *event;
	struct op_sample *sample;
	unsigned long size;
	unsigned long *data;
};

void oprofile_write_reserve(struct op_entry *entry,
			    struct pt_regs * const regs,
			    unsigned long pc, int code, int size);
int oprofile_add_data(struct op_entry *entry, unsigned long val);
int oprofile_add_data64(struct op_entry *entry, u64 val);
int oprofile_write_commit(struct op_entry *entry);

#endif /* OPROFILE_H */
rn err; *temp = tmp108_temp_reg_to_mC(regval); break; case hwmon_temp_min_alarm: case hwmon_temp_max_alarm: err = regmap_read(tmp108->regmap, TMP108_REG_CONF, &regval); if (err < 0) return err; *temp = !!(regval & (attr == hwmon_temp_min_alarm ? TMP108_CONF_FL : TMP108_CONF_FH)); break; case hwmon_temp_min_hyst: case hwmon_temp_max_hyst: err = regmap_read(tmp108->regmap, TMP108_REG_CONF, &regval); if (err < 0) return err; switch (regval & TMP108_CONF_HYSTERESIS_MASK) { case TMP108_HYSTERESIS_0C: default: hyst = 0; break; case TMP108_HYSTERESIS_1C: hyst = 1000; break; case TMP108_HYSTERESIS_2C: hyst = 2000; break; case TMP108_HYSTERESIS_4C: hyst = 4000; break; } err = regmap_read(tmp108->regmap, attr == hwmon_temp_min_hyst ? TMP108_REG_TLOW : TMP108_REG_THIGH, &regval); if (err < 0) return err; *temp = tmp108_temp_reg_to_mC(regval); if (attr == hwmon_temp_min_hyst) *temp += hyst; else *temp -= hyst; break; default: return -EOPNOTSUPP; } return 0; } static int tmp108_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long temp) { struct tmp108 *tmp108 = dev_get_drvdata(dev); u32 regval, mask; int err; if (type == hwmon_chip) { if (attr == hwmon_chip_update_interval) { if (temp < 156) mask = TMP108_CONVRATE_16HZ; else if (temp < 625) mask = TMP108_CONVRATE_4HZ; else if (temp < 2500) mask = TMP108_CONVRATE_1HZ; else mask = TMP108_CONVRATE_0P25HZ; return regmap_update_bits(tmp108->regmap, TMP108_REG_CONF, TMP108_CONF_CONVRATE_MASK, mask); } return -EOPNOTSUPP; } switch (attr) { case hwmon_temp_min: case hwmon_temp_max: temp = clamp_val(temp, TMP108_TEMP_MIN_MC, TMP108_TEMP_MAX_MC); return regmap_write(tmp108->regmap, attr == hwmon_temp_min ? TMP108_REG_TLOW : TMP108_REG_THIGH, tmp108_mC_to_temp_reg(temp)); case hwmon_temp_min_hyst: case hwmon_temp_max_hyst: temp = clamp_val(temp, TMP108_TEMP_MIN_MC, TMP108_TEMP_MAX_MC); err = regmap_read(tmp108->regmap, attr == hwmon_temp_min_hyst ? TMP108_REG_TLOW : TMP108_REG_THIGH, &regval); if (err < 0) return err; if (attr == hwmon_temp_min_hyst) temp -= tmp108_temp_reg_to_mC(regval); else temp = tmp108_temp_reg_to_mC(regval) - temp; if (temp < 500) mask = TMP108_HYSTERESIS_0C; else if (temp < 1500) mask = TMP108_HYSTERESIS_1C; else if (temp < 3000) mask = TMP108_HYSTERESIS_2C; else mask = TMP108_HYSTERESIS_4C; return regmap_update_bits(tmp108->regmap, TMP108_REG_CONF, TMP108_CONF_HYSTERESIS_MASK, mask); default: return -EOPNOTSUPP; } } static umode_t tmp108_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) { if (type == hwmon_chip && attr == hwmon_chip_update_interval) return 0644; if (type != hwmon_temp) return 0; switch (attr) { case hwmon_temp_input: case hwmon_temp_min_alarm: case hwmon_temp_max_alarm: return 0444; case hwmon_temp_min: case hwmon_temp_max: case hwmon_temp_min_hyst: case hwmon_temp_max_hyst: return 0644; default: return 0; } } static u32 tmp108_chip_config[] = { HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL, 0 }; static const struct hwmon_channel_info tmp108_chip = { .type = hwmon_chip, .config = tmp108_chip_config, }; static u32 tmp108_temp_config[] = { HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | HWMON_T_MIN_HYST | HWMON_T_MAX_HYST | HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM, 0 }; static const struct hwmon_channel_info tmp108_temp = { .type = hwmon_temp, .config = tmp108_temp_config, }; static const struct hwmon_channel_info *tmp108_info[] = { &tmp108_chip, &tmp108_temp, NULL }; static const struct hwmon_ops tmp108_hwmon_ops = { .is_visible = tmp108_is_visible, .read = tmp108_read, .write = tmp108_write, }; static const struct hwmon_chip_info tmp108_chip_info = { .ops = &tmp108_hwmon_ops, .info = tmp108_info, }; static void tmp108_restore_config(void *data) { struct tmp108 *tmp108 = data; regmap_write(tmp108->regmap, TMP108_REG_CONF, tmp108->orig_config); } static bool tmp108_is_writeable_reg(struct device *dev, unsigned int reg) { return reg != TMP108_REG_TEMP; } static bool tmp108_is_volatile_reg(struct device *dev, unsigned int reg) { /* Configuration register must be volatile to enable FL and FH. */ return reg == TMP108_REG_TEMP || reg == TMP108_REG_CONF; } static const struct regmap_config tmp108_regmap_config = { .reg_bits = 8, .val_bits = 16, .max_register = TMP108_REG_THIGH, .writeable_reg = tmp108_is_writeable_reg, .volatile_reg = tmp108_is_volatile_reg, .val_format_endian = REGMAP_ENDIAN_BIG, .cache_type = REGCACHE_RBTREE, .use_single_read = true, .use_single_write = true, }; static int tmp108_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct device *hwmon_dev; struct tmp108 *tmp108; int err; u32 config; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) { dev_err(dev, "adapter doesn't support SMBus word transactions\n"); return -ENODEV; } tmp108 = devm_kzalloc(dev, sizeof(*tmp108), GFP_KERNEL); if (!tmp108) return -ENOMEM; dev_set_drvdata(dev, tmp108); tmp108->regmap = devm_regmap_init_i2c(client, &tmp108_regmap_config); if (IS_ERR(tmp108->regmap)) { err = PTR_ERR(tmp108->regmap); dev_err(dev, "regmap init failed: %d", err); return err; } err = regmap_read(tmp108->regmap, TMP108_REG_CONF, &config); if (err < 0) { dev_err(dev, "error reading config register: %d", err); return err; } tmp108->orig_config = config; /* Only continuous mode is supported. */ config &= ~TMP108_CONF_MODE_MASK; config |= TMP108_MODE_CONTINUOUS; /* Only comparator mode is supported. */ config &= ~TMP108_CONF_TM; err = regmap_write(tmp108->regmap, TMP108_REG_CONF, config); if (err < 0) { dev_err(dev, "error writing config register: %d", err); return err; } tmp108->ready_time = jiffies; if ((tmp108->orig_config & TMP108_CONF_MODE_MASK) == TMP108_MODE_SHUTDOWN) tmp108->ready_time += msecs_to_jiffies(TMP108_CONVERSION_TIME_MS); err = devm_add_action_or_reset(dev, tmp108_restore_config, tmp108); if (err) { dev_err(dev, "add action or reset failed: %d", err); return err; } hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, tmp108, &tmp108_chip_info, NULL); return PTR_ERR_OR_ZERO(hwmon_dev); } static int __maybe_unused tmp108_suspend(struct device *dev) { struct tmp108 *tmp108 = dev_get_drvdata(dev); return regmap_update_bits(tmp108->regmap, TMP108_REG_CONF, TMP108_CONF_MODE_MASK, TMP108_MODE_SHUTDOWN); } static int __maybe_unused tmp108_resume(struct device *dev) { struct tmp108 *tmp108 = dev_get_drvdata(dev); int err; err = regmap_update_bits(tmp108->regmap, TMP108_REG_CONF, TMP108_CONF_MODE_MASK, TMP108_MODE_CONTINUOUS); tmp108->ready_time = jiffies + msecs_to_jiffies(TMP108_CONVERSION_TIME_MS); return err; } static SIMPLE_DEV_PM_OPS(tmp108_dev_pm_ops, tmp108_suspend, tmp108_resume); static const struct i2c_device_id tmp108_i2c_ids[] = { { "tmp108", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp108_i2c_ids); #ifdef CONFIG_OF static const struct of_device_id tmp108_of_ids[] = { { .compatible = "ti,tmp108", }, {} }; MODULE_DEVICE_TABLE(of, tmp108_of_ids); #endif static struct i2c_driver tmp108_driver = { .driver = { .name = DRIVER_NAME, .pm = &tmp108_dev_pm_ops, .of_match_table = of_match_ptr(tmp108_of_ids), }, .probe = tmp108_probe, .id_table = tmp108_i2c_ids, }; module_i2c_driver(tmp108_driver); MODULE_AUTHOR("John Muir <john@jmuir.com>"); MODULE_DESCRIPTION("Texas Instruments TMP108 temperature sensor driver"); MODULE_LICENSE("GPL");