static int bcm6328_blink_set()

in leds-bcm6328.c [162:253]


static int bcm6328_blink_set(struct led_classdev *led_cdev,
			     unsigned long *delay_on, unsigned long *delay_off)
{
	struct bcm6328_led *led =
		container_of(led_cdev, struct bcm6328_led, cdev);
	unsigned long delay, flags;
	int rc;

	if (!*delay_on)
		*delay_on = BCM6328_LED_DEF_DELAY;
	if (!*delay_off)
		*delay_off = BCM6328_LED_DEF_DELAY;

	delay = bcm6328_blink_delay(*delay_on);
	if (delay != bcm6328_blink_delay(*delay_off)) {
		dev_dbg(led_cdev->dev,
			"fallback to soft blinking (delay_on != delay_off)\n");
		return -EINVAL;
	}

	if (delay > BCM6328_LED_BLINK_MASK) {
		dev_dbg(led_cdev->dev,
			"fallback to soft blinking (delay > %ums)\n",
			BCM6328_LED_BLINK_MASK * BCM6328_LED_BLINK_MS);
		return -EINVAL;
	}

	spin_lock_irqsave(led->lock, flags);
	/*
	 * Check if any of the two configurable HW blinking intervals is
	 * available:
	 *   1. No LEDs assigned to the HW blinking interval.
	 *   2. Only this LED is assigned to the HW blinking interval.
	 *   3. LEDs with the same delay assigned.
	 */
	if (led->blink_leds[0] == 0 ||
	    led->blink_leds[0] == BIT(led->pin) ||
	    led->blink_delay[0] == delay) {
		unsigned long val;

		/* Add LED to the first HW blinking interval cache */
		led->blink_leds[0] |= BIT(led->pin);

		/* Remove LED from the second HW blinking interval cache */
		led->blink_leds[1] &= ~BIT(led->pin);

		/* Cache first HW blinking interval delay */
		led->blink_delay[0] = delay;

		/* Update the delay for the first HW blinking interval */
		val = bcm6328_led_read(led->mem + BCM6328_REG_INIT);
		val &= ~BCM6328_LED_BLINK1_MASK;
		val |= (delay << BCM6328_LED_BLINK1_SHIFT);
		bcm6328_led_write(led->mem + BCM6328_REG_INIT, val);

		/* Set the LED to first HW blinking interval */
		bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK1);

		rc = 0;
	} else if (led->blink_leds[1] == 0 ||
		   led->blink_leds[1] == BIT(led->pin) ||
		   led->blink_delay[1] == delay) {
		unsigned long val;

		/* Remove LED from the first HW blinking interval */
		led->blink_leds[0] &= ~BIT(led->pin);

		/* Add LED to the second HW blinking interval */
		led->blink_leds[1] |= BIT(led->pin);

		/* Cache second HW blinking interval delay */
		led->blink_delay[1] = delay;

		/* Update the delay for the second HW blinking interval */
		val = bcm6328_led_read(led->mem + BCM6328_REG_INIT);
		val &= ~BCM6328_LED_BLINK2_MASK;
		val |= (delay << BCM6328_LED_BLINK2_SHIFT);
		bcm6328_led_write(led->mem + BCM6328_REG_INIT, val);

		/* Set the LED to second HW blinking interval */
		bcm6328_led_mode(led, BCM6328_LED_MODE_BLINK2);

		rc = 0;
	} else {
		dev_dbg(led_cdev->dev,
			"fallback to soft blinking (delay already set)\n");
		rc = -EINVAL;
	}
	spin_unlock_irqrestore(led->lock, flags);

	return rc;
}