drivers/counter/i8254.c

Source file repositories/reference/linux-study-clean/drivers/counter/i8254.c

File Facts

System
Linux kernel
Corpus path
drivers/counter/i8254.c
Extension
.c
Size
12626 bytes
Lines
448
Domain
Driver Families
Bucket
drivers/counter
Inferred role
Driver Families: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct i8254 {
	struct mutex lock;
	u16 preset[I8254_NUM_COUNTERS];
	u8 out_mode[I8254_NUM_COUNTERS];
	struct regmap *map;
};

static int i8254_count_read(struct counter_device *const counter, struct counter_count *const count,
			    u64 *const val)
{
	struct i8254 *const priv = counter_priv(counter);
	int ret;
	u8 value[2];

	mutex_lock(&priv->lock);

	ret = regmap_write(priv->map, I8254_CONTROL_REG, I8254_COUNTER_LATCH(count->id));
	if (ret) {
		mutex_unlock(&priv->lock);
		return ret;
	}
	ret = regmap_noinc_read(priv->map, I8254_COUNTER_REG(count->id), value, sizeof(value));
	if (ret) {
		mutex_unlock(&priv->lock);
		return ret;
	}

	mutex_unlock(&priv->lock);

	*val = get_unaligned_le16(value);

	return ret;
}

static int i8254_function_read(struct counter_device *const counter,
			       struct counter_count *const count,
			       enum counter_function *const function)
{
	*function = COUNTER_FUNCTION_DECREASE;
	return 0;
}

#define I8254_SYNAPSES_PER_COUNT 2
#define I8254_SIGNAL_ID_CLK 0
#define I8254_SIGNAL_ID_GATE 1

static int i8254_action_read(struct counter_device *const counter,
			     struct counter_count *const count,
			     struct counter_synapse *const synapse,
			     enum counter_synapse_action *const action)
{
	struct i8254 *const priv = counter_priv(counter);

	switch (synapse->signal->id % I8254_SYNAPSES_PER_COUNT) {
	case I8254_SIGNAL_ID_CLK:
		*action = COUNTER_SYNAPSE_ACTION_FALLING_EDGE;
		return 0;
	case I8254_SIGNAL_ID_GATE:
		switch (priv->out_mode[count->id]) {
		case I8254_MODE_HARDWARE_RETRIGGERABLE_ONESHOT:
		case I8254_MODE_RATE_GENERATOR:
		case I8254_MODE_SQUARE_WAVE_MODE:
		case I8254_MODE_HARDWARE_TRIGGERED_STROBE:
			*action = COUNTER_SYNAPSE_ACTION_RISING_EDGE;
			return 0;
		default:
			*action = COUNTER_SYNAPSE_ACTION_NONE;
			return 0;
		}
	default:
		/* should never reach this path */
		return -EINVAL;
	}
}

static int i8254_count_ceiling_read(struct counter_device *const counter,
				    struct counter_count *const count, u64 *const ceiling)
{
	struct i8254 *const priv = counter_priv(counter);

	mutex_lock(&priv->lock);

	switch (priv->out_mode[count->id]) {
	case I8254_MODE_RATE_GENERATOR:
		/* Rate Generator decrements 0 by one and the counter "wraps around" */
		*ceiling = (priv->preset[count->id] == 0) ? U16_MAX : priv->preset[count->id];
		break;
	case I8254_MODE_SQUARE_WAVE_MODE:
		if (priv->preset[count->id] % 2)
			*ceiling = priv->preset[count->id] - 1;

Annotation

Implementation Notes