--- /dev/null
+.. SPDX-License-Identifier: GPL-2.0
+
+===========================================================
+Kernel driver for Intel Cherry Trail Whiskey Cove PMIC LEDs
+===========================================================
+
+/sys/class/leds/<led>/hw_pattern
+--------------------------------
+
+Specify a hardware pattern for the Whiskey Cove PMIC LEDs.
+
+The only supported pattern is hardware breathing mode::
+
+    "0 2000 1 2000"
+
+       ^
+       |
+    Max-|     ---
+       |    /   \
+       |   /     \
+       |  /       \     /
+       | /         \   /
+    Min-|-           ---
+       |
+       0------2------4--> time (sec)
+
+The rise and fall times must be the same value.
+Supported values are 2000, 1000, 500 and 250 for
+breathing frequencies of 1/4, 1/2, 1 and 2 Hz.
+
+The set pattern only controls the timing. For max brightness the last
+set brightness is used and the max brightness can be changed
+while breathing by writing the brightness attribute.
+
+This is just like how blinking works in the LED subsystem,
+for both sw and hw blinking the brightness can also be changed
+while blinking. Breathing on this hw really is just a variant
+mode of blinking.
 
                return -1;
 }
 
-static int cht_wc_leds_blink_set(struct led_classdev *cdev,
-                                unsigned long *delay_on,
-                                unsigned long *delay_off)
+static int cht_wc_leds_set_effect(struct led_classdev *cdev,
+                                 unsigned long *delay_on,
+                                 unsigned long *delay_off,
+                                 u8 effect)
 {
        struct cht_wc_led *led = container_of(cdev, struct cht_wc_led, cdev);
        unsigned int ctrl;
        }
 
        ret = regmap_update_bits(led->regmap, led->regs->fsm,
-                                CHT_WC_LED_EFF_MASK, CHT_WC_LED_EFF_BLINKING);
+                                CHT_WC_LED_EFF_MASK, effect);
        if (ret < 0)
                dev_err(cdev->dev, "Failed to update LED FSM reg: %d\n", ret);
 
        return ret;
 }
 
+static int cht_wc_leds_blink_set(struct led_classdev *cdev,
+                                unsigned long *delay_on,
+                                unsigned long *delay_off)
+{
+       return cht_wc_leds_set_effect(cdev, delay_on, delay_off, CHT_WC_LED_EFF_BLINKING);
+}
+
+static int cht_wc_leds_pattern_set(struct led_classdev *cdev,
+                                  struct led_pattern *pattern,
+                                  u32 len, int repeat)
+{
+       unsigned long delay_off, delay_on;
+
+       if (repeat > 0 || len != 2 ||
+           pattern[0].brightness != 0 || pattern[1].brightness != 1 ||
+           pattern[0].delta_t != pattern[1].delta_t ||
+           (pattern[0].delta_t != 250 && pattern[0].delta_t != 500 &&
+            pattern[0].delta_t != 1000 && pattern[0].delta_t != 2000))
+               return -EINVAL;
+
+       delay_off = pattern[0].delta_t;
+       delay_on  = pattern[1].delta_t;
+
+       return cht_wc_leds_set_effect(cdev, &delay_on, &delay_off, CHT_WC_LED_EFF_BREATHING);
+}
+
+static int cht_wc_leds_pattern_clear(struct led_classdev *cdev)
+{
+       return cht_wc_leds_brightness_set(cdev, 0);
+}
+
 static int cht_wc_led_save_regs(struct cht_wc_led *led,
                                struct cht_wc_led_saved_regs *saved_regs)
 {
                led->cdev.brightness_set_blocking = cht_wc_leds_brightness_set;
                led->cdev.brightness_get = cht_wc_leds_brightness_get;
                led->cdev.blink_set = cht_wc_leds_blink_set;
+               led->cdev.pattern_set = cht_wc_leds_pattern_set;
+               led->cdev.pattern_clear = cht_wc_leds_pattern_clear;
                led->cdev.max_brightness = 255;
 
                ret = led_classdev_register(&pdev->dev, &led->cdev);