^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * LEDs driver for Analog Devices ADP5520/ADP5501 MFD PMICs
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright 2009 Analog Devices Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Loosely derived from leds-da903x:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Copyright (C) 2008 Compulab, Ltd.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) * Mike Rapoport <mike@compulab.co.il>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) * Copyright (C) 2006-2008 Marvell International Ltd.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) * Eric Miao <eric.miao@marvell.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/leds.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/mfd/adp5520.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) struct adp5520_led {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) struct led_classdev cdev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) struct device *master;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) int id;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) int flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) static int adp5520_led_set(struct led_classdev *led_cdev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) enum led_brightness value)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) struct adp5520_led *led;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) led = container_of(led_cdev, struct adp5520_led, cdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) return adp5520_write(led->master, ADP5520_LED1_CURRENT + led->id - 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) value >> 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) static int adp5520_led_setup(struct adp5520_led *led)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) struct device *dev = led->master;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) int flags = led->flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) switch (led->id) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) case FLAG_ID_ADP5520_LED1_ADP5501_LED0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) ret |= adp5520_set_bits(dev, ADP5520_LED_TIME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) (flags >> ADP5520_FLAG_OFFT_SHIFT) &
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) ADP5520_FLAG_OFFT_MASK);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) ADP5520_LED1_EN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) case FLAG_ID_ADP5520_LED2_ADP5501_LED1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) ret |= adp5520_set_bits(dev, ADP5520_LED_TIME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) ((flags >> ADP5520_FLAG_OFFT_SHIFT) &
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) ADP5520_FLAG_OFFT_MASK) << 2);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) ADP5520_R3_MODE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) ADP5520_LED2_EN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) case FLAG_ID_ADP5520_LED3_ADP5501_LED2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) ret |= adp5520_set_bits(dev, ADP5520_LED_TIME,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) ((flags >> ADP5520_FLAG_OFFT_SHIFT) &
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) ADP5520_FLAG_OFFT_MASK) << 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) ret |= adp5520_clr_bits(dev, ADP5520_LED_CONTROL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) ADP5520_C3_MODE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) ret |= adp5520_set_bits(dev, ADP5520_LED_CONTROL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) ADP5520_LED3_EN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) static int adp5520_led_prepare(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) struct device *dev = pdev->dev.parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) int ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) ret |= adp5520_write(dev, ADP5520_LED1_CURRENT, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) ret |= adp5520_write(dev, ADP5520_LED2_CURRENT, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) ret |= adp5520_write(dev, ADP5520_LED3_CURRENT, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) ret |= adp5520_write(dev, ADP5520_LED_TIME, pdata->led_on_time << 6);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) ret |= adp5520_write(dev, ADP5520_LED_FADE, FADE_VAL(pdata->fade_in,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) pdata->fade_out));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) static int adp5520_led_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) struct adp5520_led *led, *led_dat;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) struct led_info *cur_led;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) int ret, i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) if (pdata == NULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) dev_err(&pdev->dev, "missing platform data\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) if (pdata->num_leds > ADP5520_01_MAXLEDS) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) dev_err(&pdev->dev, "can't handle more than %d LEDS\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) ADP5520_01_MAXLEDS);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) return -EFAULT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) led = devm_kcalloc(&pdev->dev, pdata->num_leds, sizeof(*led),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) if (!led)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) ret = adp5520_led_prepare(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) dev_err(&pdev->dev, "failed to write\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) for (i = 0; i < pdata->num_leds; ++i) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) cur_led = &pdata->leds[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) led_dat = &led[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) led_dat->cdev.name = cur_led->name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) led_dat->cdev.default_trigger = cur_led->default_trigger;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) led_dat->cdev.brightness_set_blocking = adp5520_led_set;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) led_dat->cdev.brightness = LED_OFF;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) if (cur_led->flags & ADP5520_FLAG_LED_MASK)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) led_dat->flags = cur_led->flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) led_dat->flags = i + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) led_dat->id = led_dat->flags & ADP5520_FLAG_LED_MASK;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) led_dat->master = pdev->dev.parent;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) ret = led_classdev_register(led_dat->master, &led_dat->cdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) dev_err(&pdev->dev, "failed to register LED %d\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) led_dat->id);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) goto err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) ret = adp5520_led_setup(led_dat);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) if (ret) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) dev_err(&pdev->dev, "failed to write\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) i++;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) goto err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) platform_set_drvdata(pdev, led);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) err:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) if (i > 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) for (i = i - 1; i >= 0; i--)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) led_classdev_unregister(&led[i].cdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) static int adp5520_led_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) struct adp5520_leds_platform_data *pdata = dev_get_platdata(&pdev->dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) struct adp5520_led *led;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) led = platform_get_drvdata(pdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) adp5520_clr_bits(led->master, ADP5520_LED_CONTROL,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) ADP5520_LED1_EN | ADP5520_LED2_EN | ADP5520_LED3_EN);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) for (i = 0; i < pdata->num_leds; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) led_classdev_unregister(&led[i].cdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) static struct platform_driver adp5520_led_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) .name = "adp5520-led",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) .probe = adp5520_led_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) .remove = adp5520_led_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) module_platform_driver(adp5520_led_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) MODULE_DESCRIPTION("LEDS ADP5520(01) Driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) MODULE_LICENSE("GPL");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) MODULE_ALIAS("platform:adp5520-led");