cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

leds-bd2802.c (20891B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * leds-bd2802.c - RGB LED Driver
      4 *
      5 * Copyright (C) 2009 Samsung Electronics
      6 * Kim Kyuwon <q1.kim@samsung.com>
      7 *
      8 * Datasheet: http://www.rohm.com/products/databook/driver/pdf/bd2802gu-e.pdf
      9 */
     10
     11#include <linux/module.h>
     12#include <linux/i2c.h>
     13#include <linux/gpio/consumer.h>
     14#include <linux/delay.h>
     15#include <linux/leds.h>
     16#include <linux/leds-bd2802.h>
     17#include <linux/slab.h>
     18#include <linux/pm.h>
     19
     20#define LED_CTL(rgb2en, rgb1en) ((rgb2en) << 4 | ((rgb1en) << 0))
     21
     22#define BD2802_LED_OFFSET		0xa
     23#define BD2802_COLOR_OFFSET		0x3
     24
     25#define BD2802_REG_CLKSETUP		0x00
     26#define BD2802_REG_CONTROL		0x01
     27#define BD2802_REG_HOURSETUP		0x02
     28#define BD2802_REG_CURRENT1SETUP	0x03
     29#define BD2802_REG_CURRENT2SETUP	0x04
     30#define BD2802_REG_WAVEPATTERN		0x05
     31
     32#define BD2802_CURRENT_032		0x10 /* 3.2mA */
     33#define BD2802_CURRENT_000		0x00 /* 0.0mA */
     34
     35#define BD2802_PATTERN_FULL		0x07
     36#define BD2802_PATTERN_HALF		0x03
     37
     38enum led_ids {
     39	LED1,
     40	LED2,
     41	LED_NUM,
     42};
     43
     44enum led_colors {
     45	RED,
     46	GREEN,
     47	BLUE,
     48};
     49
     50enum led_bits {
     51	BD2802_OFF,
     52	BD2802_BLINK,
     53	BD2802_ON,
     54};
     55
     56/*
     57 * State '0' : 'off'
     58 * State '1' : 'blink'
     59 * State '2' : 'on'.
     60 */
     61struct led_state {
     62	unsigned r:2;
     63	unsigned g:2;
     64	unsigned b:2;
     65};
     66
     67struct bd2802_led {
     68	struct bd2802_led_platform_data	*pdata;
     69	struct i2c_client		*client;
     70	struct gpio_desc		*reset;
     71	struct rw_semaphore		rwsem;
     72
     73	struct led_state		led[2];
     74
     75	/*
     76	 * Making led_classdev as array is not recommended, because array
     77	 * members prevent using 'container_of' macro. So repetitive works
     78	 * are needed.
     79	 */
     80	struct led_classdev		cdev_led1r;
     81	struct led_classdev		cdev_led1g;
     82	struct led_classdev		cdev_led1b;
     83	struct led_classdev		cdev_led2r;
     84	struct led_classdev		cdev_led2g;
     85	struct led_classdev		cdev_led2b;
     86
     87	/*
     88	 * Advanced Configuration Function(ADF) mode:
     89	 * In ADF mode, user can set registers of BD2802GU directly,
     90	 * therefore BD2802GU doesn't enter reset state.
     91	 */
     92	int				adf_on;
     93
     94	enum led_ids			led_id;
     95	enum led_colors			color;
     96	enum led_bits			state;
     97
     98	/* General attributes of RGB LEDs */
     99	int				wave_pattern;
    100	int				rgb_current;
    101};
    102
    103
    104/*--------------------------------------------------------------*/
    105/*	BD2802GU helper functions					*/
    106/*--------------------------------------------------------------*/
    107
    108static inline int bd2802_is_rgb_off(struct bd2802_led *led, enum led_ids id,
    109							enum led_colors color)
    110{
    111	switch (color) {
    112	case RED:
    113		return !led->led[id].r;
    114	case GREEN:
    115		return !led->led[id].g;
    116	case BLUE:
    117		return !led->led[id].b;
    118	default:
    119		dev_err(&led->client->dev, "%s: Invalid color\n", __func__);
    120		return -EINVAL;
    121	}
    122}
    123
    124static inline int bd2802_is_led_off(struct bd2802_led *led, enum led_ids id)
    125{
    126	if (led->led[id].r || led->led[id].g || led->led[id].b)
    127		return 0;
    128
    129	return 1;
    130}
    131
    132static inline int bd2802_is_all_off(struct bd2802_led *led)
    133{
    134	int i;
    135
    136	for (i = 0; i < LED_NUM; i++)
    137		if (!bd2802_is_led_off(led, i))
    138			return 0;
    139
    140	return 1;
    141}
    142
    143static inline u8 bd2802_get_base_offset(enum led_ids id, enum led_colors color)
    144{
    145	return id * BD2802_LED_OFFSET + color * BD2802_COLOR_OFFSET;
    146}
    147
    148static inline u8 bd2802_get_reg_addr(enum led_ids id, enum led_colors color,
    149								u8 reg_offset)
    150{
    151	return reg_offset + bd2802_get_base_offset(id, color);
    152}
    153
    154
    155/*--------------------------------------------------------------*/
    156/*	BD2802GU core functions					*/
    157/*--------------------------------------------------------------*/
    158
    159static int bd2802_write_byte(struct i2c_client *client, u8 reg, u8 val)
    160{
    161	int ret = i2c_smbus_write_byte_data(client, reg, val);
    162	if (ret >= 0)
    163		return 0;
    164
    165	dev_err(&client->dev, "%s: reg 0x%x, val 0x%x, err %d\n",
    166						__func__, reg, val, ret);
    167
    168	return ret;
    169}
    170
    171static void bd2802_update_state(struct bd2802_led *led, enum led_ids id,
    172				enum led_colors color, enum led_bits led_bit)
    173{
    174	int i;
    175	u8 value;
    176
    177	for (i = 0; i < LED_NUM; i++) {
    178		if (i == id) {
    179			switch (color) {
    180			case RED:
    181				led->led[i].r = led_bit;
    182				break;
    183			case GREEN:
    184				led->led[i].g = led_bit;
    185				break;
    186			case BLUE:
    187				led->led[i].b = led_bit;
    188				break;
    189			default:
    190				dev_err(&led->client->dev,
    191					"%s: Invalid color\n", __func__);
    192				return;
    193			}
    194		}
    195	}
    196
    197	if (led_bit == BD2802_BLINK || led_bit == BD2802_ON)
    198		return;
    199
    200	if (!bd2802_is_led_off(led, id))
    201		return;
    202
    203	if (bd2802_is_all_off(led) && !led->adf_on) {
    204		gpiod_set_value(led->reset, 1);
    205		return;
    206	}
    207
    208	/*
    209	 * In this case, other led is turned on, and current led is turned
    210	 * off. So set RGB LED Control register to stop the current RGB LED
    211	 */
    212	value = (id == LED1) ? LED_CTL(1, 0) : LED_CTL(0, 1);
    213	bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
    214}
    215
    216static void bd2802_configure(struct bd2802_led *led)
    217{
    218	struct bd2802_led_platform_data *pdata = led->pdata;
    219	u8 reg;
    220
    221	reg = bd2802_get_reg_addr(LED1, RED, BD2802_REG_HOURSETUP);
    222	bd2802_write_byte(led->client, reg, pdata->rgb_time);
    223
    224	reg = bd2802_get_reg_addr(LED2, RED, BD2802_REG_HOURSETUP);
    225	bd2802_write_byte(led->client, reg, pdata->rgb_time);
    226}
    227
    228static void bd2802_reset_cancel(struct bd2802_led *led)
    229{
    230	gpiod_set_value(led->reset, 0);
    231	udelay(100);
    232	bd2802_configure(led);
    233}
    234
    235static void bd2802_enable(struct bd2802_led *led, enum led_ids id)
    236{
    237	enum led_ids other_led = (id == LED1) ? LED2 : LED1;
    238	u8 value, other_led_on;
    239
    240	other_led_on = !bd2802_is_led_off(led, other_led);
    241	if (id == LED1)
    242		value = LED_CTL(other_led_on, 1);
    243	else
    244		value = LED_CTL(1 , other_led_on);
    245
    246	bd2802_write_byte(led->client, BD2802_REG_CONTROL, value);
    247}
    248
    249static void bd2802_set_on(struct bd2802_led *led, enum led_ids id,
    250							enum led_colors color)
    251{
    252	u8 reg;
    253
    254	if (bd2802_is_all_off(led) && !led->adf_on)
    255		bd2802_reset_cancel(led);
    256
    257	reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
    258	bd2802_write_byte(led->client, reg, led->rgb_current);
    259	reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
    260	bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
    261	reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
    262	bd2802_write_byte(led->client, reg, BD2802_PATTERN_FULL);
    263
    264	bd2802_enable(led, id);
    265	bd2802_update_state(led, id, color, BD2802_ON);
    266}
    267
    268static void bd2802_set_blink(struct bd2802_led *led, enum led_ids id,
    269							enum led_colors color)
    270{
    271	u8 reg;
    272
    273	if (bd2802_is_all_off(led) && !led->adf_on)
    274		bd2802_reset_cancel(led);
    275
    276	reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
    277	bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
    278	reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
    279	bd2802_write_byte(led->client, reg, led->rgb_current);
    280	reg = bd2802_get_reg_addr(id, color, BD2802_REG_WAVEPATTERN);
    281	bd2802_write_byte(led->client, reg, led->wave_pattern);
    282
    283	bd2802_enable(led, id);
    284	bd2802_update_state(led, id, color, BD2802_BLINK);
    285}
    286
    287static void bd2802_turn_on(struct bd2802_led *led, enum led_ids id,
    288				enum led_colors color, enum led_bits led_bit)
    289{
    290	if (led_bit == BD2802_OFF) {
    291		dev_err(&led->client->dev,
    292					"Only 'blink' and 'on' are allowed\n");
    293		return;
    294	}
    295
    296	if (led_bit == BD2802_BLINK)
    297		bd2802_set_blink(led, id, color);
    298	else
    299		bd2802_set_on(led, id, color);
    300}
    301
    302static void bd2802_turn_off(struct bd2802_led *led, enum led_ids id,
    303							enum led_colors color)
    304{
    305	u8 reg;
    306
    307	if (bd2802_is_rgb_off(led, id, color))
    308		return;
    309
    310	reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT1SETUP);
    311	bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
    312	reg = bd2802_get_reg_addr(id, color, BD2802_REG_CURRENT2SETUP);
    313	bd2802_write_byte(led->client, reg, BD2802_CURRENT_000);
    314
    315	bd2802_update_state(led, id, color, BD2802_OFF);
    316}
    317
    318#define BD2802_SET_REGISTER(reg_addr, reg_name)				\
    319static ssize_t bd2802_store_reg##reg_addr(struct device *dev,		\
    320	struct device_attribute *attr, const char *buf, size_t count)	\
    321{									\
    322	struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
    323	unsigned long val;						\
    324	int ret;							\
    325	if (!count)							\
    326		return -EINVAL;						\
    327	ret = kstrtoul(buf, 16, &val);					\
    328	if (ret)							\
    329		return ret;						\
    330	down_write(&led->rwsem);					\
    331	bd2802_write_byte(led->client, reg_addr, (u8) val);		\
    332	up_write(&led->rwsem);						\
    333	return count;							\
    334}									\
    335static struct device_attribute bd2802_reg##reg_addr##_attr = {		\
    336	.attr = {.name = reg_name, .mode = 0644},			\
    337	.store = bd2802_store_reg##reg_addr,				\
    338};
    339
    340BD2802_SET_REGISTER(0x00, "0x00");
    341BD2802_SET_REGISTER(0x01, "0x01");
    342BD2802_SET_REGISTER(0x02, "0x02");
    343BD2802_SET_REGISTER(0x03, "0x03");
    344BD2802_SET_REGISTER(0x04, "0x04");
    345BD2802_SET_REGISTER(0x05, "0x05");
    346BD2802_SET_REGISTER(0x06, "0x06");
    347BD2802_SET_REGISTER(0x07, "0x07");
    348BD2802_SET_REGISTER(0x08, "0x08");
    349BD2802_SET_REGISTER(0x09, "0x09");
    350BD2802_SET_REGISTER(0x0a, "0x0a");
    351BD2802_SET_REGISTER(0x0b, "0x0b");
    352BD2802_SET_REGISTER(0x0c, "0x0c");
    353BD2802_SET_REGISTER(0x0d, "0x0d");
    354BD2802_SET_REGISTER(0x0e, "0x0e");
    355BD2802_SET_REGISTER(0x0f, "0x0f");
    356BD2802_SET_REGISTER(0x10, "0x10");
    357BD2802_SET_REGISTER(0x11, "0x11");
    358BD2802_SET_REGISTER(0x12, "0x12");
    359BD2802_SET_REGISTER(0x13, "0x13");
    360BD2802_SET_REGISTER(0x14, "0x14");
    361BD2802_SET_REGISTER(0x15, "0x15");
    362
    363static struct device_attribute *bd2802_addr_attributes[] = {
    364	&bd2802_reg0x00_attr,
    365	&bd2802_reg0x01_attr,
    366	&bd2802_reg0x02_attr,
    367	&bd2802_reg0x03_attr,
    368	&bd2802_reg0x04_attr,
    369	&bd2802_reg0x05_attr,
    370	&bd2802_reg0x06_attr,
    371	&bd2802_reg0x07_attr,
    372	&bd2802_reg0x08_attr,
    373	&bd2802_reg0x09_attr,
    374	&bd2802_reg0x0a_attr,
    375	&bd2802_reg0x0b_attr,
    376	&bd2802_reg0x0c_attr,
    377	&bd2802_reg0x0d_attr,
    378	&bd2802_reg0x0e_attr,
    379	&bd2802_reg0x0f_attr,
    380	&bd2802_reg0x10_attr,
    381	&bd2802_reg0x11_attr,
    382	&bd2802_reg0x12_attr,
    383	&bd2802_reg0x13_attr,
    384	&bd2802_reg0x14_attr,
    385	&bd2802_reg0x15_attr,
    386};
    387
    388static void bd2802_enable_adv_conf(struct bd2802_led *led)
    389{
    390	int i, ret;
    391
    392	for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++) {
    393		ret = device_create_file(&led->client->dev,
    394						bd2802_addr_attributes[i]);
    395		if (ret) {
    396			dev_err(&led->client->dev, "failed: sysfs file %s\n",
    397					bd2802_addr_attributes[i]->attr.name);
    398			goto failed_remove_files;
    399		}
    400	}
    401
    402	if (bd2802_is_all_off(led))
    403		bd2802_reset_cancel(led);
    404
    405	led->adf_on = 1;
    406
    407	return;
    408
    409failed_remove_files:
    410	for (i--; i >= 0; i--)
    411		device_remove_file(&led->client->dev,
    412						bd2802_addr_attributes[i]);
    413}
    414
    415static void bd2802_disable_adv_conf(struct bd2802_led *led)
    416{
    417	int i;
    418
    419	for (i = 0; i < ARRAY_SIZE(bd2802_addr_attributes); i++)
    420		device_remove_file(&led->client->dev,
    421						bd2802_addr_attributes[i]);
    422
    423	if (bd2802_is_all_off(led))
    424		gpiod_set_value(led->reset, 1);
    425
    426	led->adf_on = 0;
    427}
    428
    429static ssize_t bd2802_show_adv_conf(struct device *dev,
    430	struct device_attribute *attr, char *buf)
    431{
    432	struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
    433	ssize_t ret;
    434
    435	down_read(&led->rwsem);
    436	if (led->adf_on)
    437		ret = sprintf(buf, "on\n");
    438	else
    439		ret = sprintf(buf, "off\n");
    440	up_read(&led->rwsem);
    441
    442	return ret;
    443}
    444
    445static ssize_t bd2802_store_adv_conf(struct device *dev,
    446	struct device_attribute *attr, const char *buf, size_t count)
    447{
    448	struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));
    449
    450	if (!count)
    451		return -EINVAL;
    452
    453	down_write(&led->rwsem);
    454	if (!led->adf_on && !strncmp(buf, "on", 2))
    455		bd2802_enable_adv_conf(led);
    456	else if (led->adf_on && !strncmp(buf, "off", 3))
    457		bd2802_disable_adv_conf(led);
    458	up_write(&led->rwsem);
    459
    460	return count;
    461}
    462
    463static struct device_attribute bd2802_adv_conf_attr = {
    464	.attr = {
    465		.name = "advanced_configuration",
    466		.mode = 0644,
    467	},
    468	.show = bd2802_show_adv_conf,
    469	.store = bd2802_store_adv_conf,
    470};
    471
    472#define BD2802_CONTROL_ATTR(attr_name, name_str)			\
    473static ssize_t bd2802_show_##attr_name(struct device *dev,		\
    474	struct device_attribute *attr, char *buf)			\
    475{									\
    476	struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
    477	ssize_t ret;							\
    478	down_read(&led->rwsem);						\
    479	ret = sprintf(buf, "0x%02x\n", led->attr_name);			\
    480	up_read(&led->rwsem);						\
    481	return ret;							\
    482}									\
    483static ssize_t bd2802_store_##attr_name(struct device *dev,		\
    484	struct device_attribute *attr, const char *buf, size_t count)	\
    485{									\
    486	struct bd2802_led *led = i2c_get_clientdata(to_i2c_client(dev));\
    487	unsigned long val;						\
    488	int ret;							\
    489	if (!count)							\
    490		return -EINVAL;						\
    491	ret = kstrtoul(buf, 16, &val);					\
    492	if (ret)							\
    493		return ret;						\
    494	down_write(&led->rwsem);					\
    495	led->attr_name = val;						\
    496	up_write(&led->rwsem);						\
    497	return count;							\
    498}									\
    499static struct device_attribute bd2802_##attr_name##_attr = {		\
    500	.attr = {							\
    501		.name = name_str,					\
    502		.mode = 0644,						\
    503	},								\
    504	.show = bd2802_show_##attr_name,				\
    505	.store = bd2802_store_##attr_name,				\
    506};
    507
    508BD2802_CONTROL_ATTR(wave_pattern, "wave_pattern");
    509BD2802_CONTROL_ATTR(rgb_current, "rgb_current");
    510
    511static struct device_attribute *bd2802_attributes[] = {
    512	&bd2802_adv_conf_attr,
    513	&bd2802_wave_pattern_attr,
    514	&bd2802_rgb_current_attr,
    515};
    516
    517#define BD2802_CONTROL_RGBS(name, id, clr)				\
    518static int bd2802_set_##name##_brightness(struct led_classdev *led_cdev,\
    519					enum led_brightness value)	\
    520{									\
    521	struct bd2802_led *led =					\
    522		container_of(led_cdev, struct bd2802_led, cdev_##name);	\
    523	led->led_id = id;						\
    524	led->color = clr;						\
    525	if (value == LED_OFF) {						\
    526		led->state = BD2802_OFF;				\
    527		bd2802_turn_off(led, led->led_id, led->color);		\
    528	} else {							\
    529		led->state = BD2802_ON;					\
    530		bd2802_turn_on(led, led->led_id, led->color, BD2802_ON);\
    531	}								\
    532	return 0;							\
    533}									\
    534static int bd2802_set_##name##_blink(struct led_classdev *led_cdev,	\
    535		unsigned long *delay_on, unsigned long *delay_off)	\
    536{									\
    537	struct bd2802_led *led =					\
    538		container_of(led_cdev, struct bd2802_led, cdev_##name);	\
    539	if (*delay_on == 0 || *delay_off == 0)				\
    540		return -EINVAL;						\
    541	led->led_id = id;						\
    542	led->color = clr;						\
    543	led->state = BD2802_BLINK;					\
    544	bd2802_turn_on(led, led->led_id, led->color, BD2802_BLINK);	\
    545	return 0;							\
    546}
    547
    548BD2802_CONTROL_RGBS(led1r, LED1, RED);
    549BD2802_CONTROL_RGBS(led1g, LED1, GREEN);
    550BD2802_CONTROL_RGBS(led1b, LED1, BLUE);
    551BD2802_CONTROL_RGBS(led2r, LED2, RED);
    552BD2802_CONTROL_RGBS(led2g, LED2, GREEN);
    553BD2802_CONTROL_RGBS(led2b, LED2, BLUE);
    554
    555static int bd2802_register_led_classdev(struct bd2802_led *led)
    556{
    557	int ret;
    558
    559	led->cdev_led1r.name = "led1_R";
    560	led->cdev_led1r.brightness = LED_OFF;
    561	led->cdev_led1r.brightness_set_blocking = bd2802_set_led1r_brightness;
    562	led->cdev_led1r.blink_set = bd2802_set_led1r_blink;
    563
    564	ret = led_classdev_register(&led->client->dev, &led->cdev_led1r);
    565	if (ret < 0) {
    566		dev_err(&led->client->dev, "couldn't register LED %s\n",
    567							led->cdev_led1r.name);
    568		goto failed_unregister_led1_R;
    569	}
    570
    571	led->cdev_led1g.name = "led1_G";
    572	led->cdev_led1g.brightness = LED_OFF;
    573	led->cdev_led1g.brightness_set_blocking = bd2802_set_led1g_brightness;
    574	led->cdev_led1g.blink_set = bd2802_set_led1g_blink;
    575
    576	ret = led_classdev_register(&led->client->dev, &led->cdev_led1g);
    577	if (ret < 0) {
    578		dev_err(&led->client->dev, "couldn't register LED %s\n",
    579							led->cdev_led1g.name);
    580		goto failed_unregister_led1_G;
    581	}
    582
    583	led->cdev_led1b.name = "led1_B";
    584	led->cdev_led1b.brightness = LED_OFF;
    585	led->cdev_led1b.brightness_set_blocking = bd2802_set_led1b_brightness;
    586	led->cdev_led1b.blink_set = bd2802_set_led1b_blink;
    587
    588	ret = led_classdev_register(&led->client->dev, &led->cdev_led1b);
    589	if (ret < 0) {
    590		dev_err(&led->client->dev, "couldn't register LED %s\n",
    591							led->cdev_led1b.name);
    592		goto failed_unregister_led1_B;
    593	}
    594
    595	led->cdev_led2r.name = "led2_R";
    596	led->cdev_led2r.brightness = LED_OFF;
    597	led->cdev_led2r.brightness_set_blocking = bd2802_set_led2r_brightness;
    598	led->cdev_led2r.blink_set = bd2802_set_led2r_blink;
    599
    600	ret = led_classdev_register(&led->client->dev, &led->cdev_led2r);
    601	if (ret < 0) {
    602		dev_err(&led->client->dev, "couldn't register LED %s\n",
    603							led->cdev_led2r.name);
    604		goto failed_unregister_led2_R;
    605	}
    606
    607	led->cdev_led2g.name = "led2_G";
    608	led->cdev_led2g.brightness = LED_OFF;
    609	led->cdev_led2g.brightness_set_blocking = bd2802_set_led2g_brightness;
    610	led->cdev_led2g.blink_set = bd2802_set_led2g_blink;
    611
    612	ret = led_classdev_register(&led->client->dev, &led->cdev_led2g);
    613	if (ret < 0) {
    614		dev_err(&led->client->dev, "couldn't register LED %s\n",
    615							led->cdev_led2g.name);
    616		goto failed_unregister_led2_G;
    617	}
    618
    619	led->cdev_led2b.name = "led2_B";
    620	led->cdev_led2b.brightness = LED_OFF;
    621	led->cdev_led2b.brightness_set_blocking = bd2802_set_led2b_brightness;
    622	led->cdev_led2b.blink_set = bd2802_set_led2b_blink;
    623	led->cdev_led2b.flags |= LED_CORE_SUSPENDRESUME;
    624
    625	ret = led_classdev_register(&led->client->dev, &led->cdev_led2b);
    626	if (ret < 0) {
    627		dev_err(&led->client->dev, "couldn't register LED %s\n",
    628							led->cdev_led2b.name);
    629		goto failed_unregister_led2_B;
    630	}
    631
    632	return 0;
    633
    634failed_unregister_led2_B:
    635	led_classdev_unregister(&led->cdev_led2g);
    636failed_unregister_led2_G:
    637	led_classdev_unregister(&led->cdev_led2r);
    638failed_unregister_led2_R:
    639	led_classdev_unregister(&led->cdev_led1b);
    640failed_unregister_led1_B:
    641	led_classdev_unregister(&led->cdev_led1g);
    642failed_unregister_led1_G:
    643	led_classdev_unregister(&led->cdev_led1r);
    644failed_unregister_led1_R:
    645
    646	return ret;
    647}
    648
    649static void bd2802_unregister_led_classdev(struct bd2802_led *led)
    650{
    651	led_classdev_unregister(&led->cdev_led2b);
    652	led_classdev_unregister(&led->cdev_led2g);
    653	led_classdev_unregister(&led->cdev_led2r);
    654	led_classdev_unregister(&led->cdev_led1b);
    655	led_classdev_unregister(&led->cdev_led1g);
    656	led_classdev_unregister(&led->cdev_led1r);
    657}
    658
    659static int bd2802_probe(struct i2c_client *client,
    660			const struct i2c_device_id *id)
    661{
    662	struct bd2802_led *led;
    663	int ret, i;
    664
    665	led = devm_kzalloc(&client->dev, sizeof(struct bd2802_led), GFP_KERNEL);
    666	if (!led)
    667		return -ENOMEM;
    668
    669	led->client = client;
    670	i2c_set_clientdata(client, led);
    671
    672	/*
    673	 * Configure RESET GPIO (L: RESET, H: RESET cancel)
    674	 *
    675	 * We request the reset GPIO as OUT_LOW which means de-asserted,
    676	 * board files specifying this GPIO line in a machine descriptor
    677	 * table should take care to specify GPIO_ACTIVE_LOW for this line.
    678	 */
    679	led->reset = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
    680	if (IS_ERR(led->reset))
    681		return PTR_ERR(led->reset);
    682
    683	/* Tacss = min 0.1ms */
    684	udelay(100);
    685
    686	/* Detect BD2802GU */
    687	ret = bd2802_write_byte(client, BD2802_REG_CLKSETUP, 0x00);
    688	if (ret < 0) {
    689		dev_err(&client->dev, "failed to detect device\n");
    690		return ret;
    691	} else
    692		dev_info(&client->dev, "return 0x%02x\n", ret);
    693
    694	/* To save the power, reset BD2802 after detecting */
    695	gpiod_set_value(led->reset, 1);
    696
    697	/* Default attributes */
    698	led->wave_pattern = BD2802_PATTERN_HALF;
    699	led->rgb_current = BD2802_CURRENT_032;
    700
    701	init_rwsem(&led->rwsem);
    702
    703	for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++) {
    704		ret = device_create_file(&led->client->dev,
    705						bd2802_attributes[i]);
    706		if (ret) {
    707			dev_err(&led->client->dev, "failed: sysfs file %s\n",
    708					bd2802_attributes[i]->attr.name);
    709			goto failed_unregister_dev_file;
    710		}
    711	}
    712
    713	ret = bd2802_register_led_classdev(led);
    714	if (ret < 0)
    715		goto failed_unregister_dev_file;
    716
    717	return 0;
    718
    719failed_unregister_dev_file:
    720	for (i--; i >= 0; i--)
    721		device_remove_file(&led->client->dev, bd2802_attributes[i]);
    722	return ret;
    723}
    724
    725static int bd2802_remove(struct i2c_client *client)
    726{
    727	struct bd2802_led *led = i2c_get_clientdata(client);
    728	int i;
    729
    730	gpiod_set_value(led->reset, 1);
    731	bd2802_unregister_led_classdev(led);
    732	if (led->adf_on)
    733		bd2802_disable_adv_conf(led);
    734	for (i = 0; i < ARRAY_SIZE(bd2802_attributes); i++)
    735		device_remove_file(&led->client->dev, bd2802_attributes[i]);
    736
    737	return 0;
    738}
    739
    740#ifdef CONFIG_PM_SLEEP
    741static void bd2802_restore_state(struct bd2802_led *led)
    742{
    743	int i;
    744
    745	for (i = 0; i < LED_NUM; i++) {
    746		if (led->led[i].r)
    747			bd2802_turn_on(led, i, RED, led->led[i].r);
    748		if (led->led[i].g)
    749			bd2802_turn_on(led, i, GREEN, led->led[i].g);
    750		if (led->led[i].b)
    751			bd2802_turn_on(led, i, BLUE, led->led[i].b);
    752	}
    753}
    754
    755static int bd2802_suspend(struct device *dev)
    756{
    757	struct i2c_client *client = to_i2c_client(dev);
    758	struct bd2802_led *led = i2c_get_clientdata(client);
    759
    760	gpiod_set_value(led->reset, 1);
    761
    762	return 0;
    763}
    764
    765static int bd2802_resume(struct device *dev)
    766{
    767	struct i2c_client *client = to_i2c_client(dev);
    768	struct bd2802_led *led = i2c_get_clientdata(client);
    769
    770	if (!bd2802_is_all_off(led) || led->adf_on) {
    771		bd2802_reset_cancel(led);
    772		bd2802_restore_state(led);
    773	}
    774
    775	return 0;
    776}
    777#endif
    778
    779static SIMPLE_DEV_PM_OPS(bd2802_pm, bd2802_suspend, bd2802_resume);
    780
    781static const struct i2c_device_id bd2802_id[] = {
    782	{ "BD2802", 0 },
    783	{ }
    784};
    785MODULE_DEVICE_TABLE(i2c, bd2802_id);
    786
    787static struct i2c_driver bd2802_i2c_driver = {
    788	.driver	= {
    789		.name	= "BD2802",
    790		.pm	= &bd2802_pm,
    791	},
    792	.probe		= bd2802_probe,
    793	.remove		= bd2802_remove,
    794	.id_table	= bd2802_id,
    795};
    796
    797module_i2c_driver(bd2802_i2c_driver);
    798
    799MODULE_AUTHOR("Kim Kyuwon <q1.kim@samsung.com>");
    800MODULE_DESCRIPTION("BD2802 LED driver");
    801MODULE_LICENSE("GPL v2");