drivers/pwm/pwm-airoha.c

Source file repositories/reference/linux-study-clean/drivers/pwm/pwm-airoha.c

File Facts

System
Linux kernel
Corpus path
drivers/pwm/pwm-airoha.c
Extension
.c
Size
19014 bytes
Lines
623
Domain
Driver Families
Bucket
drivers/pwm
Inferred role
Driver Families: implementation source
Status
source 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 airoha_pwm_bucket {
	/* Concurrent access protected by PWM core */
	int used;
	u32 period_ticks;
	u32 duty_ticks;
};

struct airoha_pwm {
	struct regmap *regmap;

	DECLARE_BITMAP(initialized, AIROHA_PWM_MAX_CHANNELS);

	struct airoha_pwm_bucket buckets[AIROHA_PWM_NUM_BUCKETS];

	/* Cache bucket used by each pwm channel */
	u8 channel_bucket[AIROHA_PWM_MAX_CHANNELS];
};

/* The PWM hardware supports periods between 4 ms and 1 s */
#define AIROHA_PWM_PERIOD_TICK_NS	(4 * NSEC_PER_MSEC)
#define AIROHA_PWM_PERIOD_MAX_NS	(1 * NSEC_PER_SEC)
/* It is represented internally as 1/250 s between 1 and 250. Unit is ticks. */
#define AIROHA_PWM_PERIOD_MIN		1
#define AIROHA_PWM_PERIOD_MAX		250
/* Duty cycle is relative with 255 corresponding to 100% */
#define AIROHA_PWM_DUTY_FULL		255

static void airoha_pwm_get_flash_map_addr_and_shift(unsigned int hwpwm,
						    u32 *addr, u32 *shift)
{
	unsigned int offset, hwpwm_bit;

	if (hwpwm >= AIROHA_PWM_NUM_GPIO) {
		unsigned int sipohwpwm = hwpwm - AIROHA_PWM_NUM_GPIO;

		offset = sipohwpwm / AIROHA_PWM_PINS_PER_FLASH_MAP;
		hwpwm_bit = sipohwpwm % AIROHA_PWM_PINS_PER_FLASH_MAP;

		/* One FLASH_MAP register handles 8 pins */
		*shift = AIROHA_PWM_REG_GPIO_FLASH_MAP_SHIFT(hwpwm_bit);
		*addr = AIROHA_PWM_REG_SIPO_FLASH_MAP(offset);
	} else {
		offset = hwpwm / AIROHA_PWM_PINS_PER_FLASH_MAP;
		hwpwm_bit = hwpwm % AIROHA_PWM_PINS_PER_FLASH_MAP;

		/* One FLASH_MAP register handles 8 pins */
		*shift = AIROHA_PWM_REG_GPIO_FLASH_MAP_SHIFT(hwpwm_bit);
		*addr = AIROHA_PWM_REG_GPIO_FLASH_MAP(offset);
	}
}

static u32 airoha_pwm_get_period_ticks_from_ns(u32 period_ns)
{
	return period_ns / AIROHA_PWM_PERIOD_TICK_NS;
}

static u32 airoha_pwm_get_duty_ticks_from_ns(u32 period_ns, u32 duty_ns)
{
	return mul_u64_u32_div(duty_ns, AIROHA_PWM_DUTY_FULL, period_ns);
}

static u32 airoha_pwm_get_period_ns_from_ticks(u32 period_tick)
{
	return period_tick * AIROHA_PWM_PERIOD_TICK_NS;
}

static u32 airoha_pwm_get_duty_ns_from_ticks(u32 period_tick, u32 duty_tick)
{
	u32 period_ns = period_tick * AIROHA_PWM_PERIOD_TICK_NS;

	/*
	 * Overflow can't occur in multiplication as duty_tick is just 8 bit
	 * and period_ns is clamped to AIROHA_PWM_PERIOD_MAX_NS and fit in a
	 * u64.
	 */
	return DIV_U64_ROUND_UP(duty_tick * period_ns, AIROHA_PWM_DUTY_FULL);
}

static int airoha_pwm_get_bucket(struct airoha_pwm *pc, int bucket,
				 u64 *period_ns, u64 *duty_ns)
{
	struct regmap *map = pc->regmap;
	u32 period_tick, duty_tick;
	unsigned int offset;
	u32 shift, val;
	int ret;

	offset = bucket / AIROHA_PWM_BUCKET_PER_CYCLE_CFG;
	shift = bucket % AIROHA_PWM_BUCKET_PER_CYCLE_CFG;
	shift = AIROHA_PWM_REG_CYCLE_CFG_SHIFT(shift);

Annotation

Implementation Notes