brintos

brintos / linux-shallow public Read only

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