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-blinkm.c (18490B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 *  leds-blinkm.c
      4 *  (c) Jan-Simon Möller (dl9pf@gmx.de)
      5 */
      6
      7#include <linux/module.h>
      8#include <linux/slab.h>
      9#include <linux/jiffies.h>
     10#include <linux/i2c.h>
     11#include <linux/err.h>
     12#include <linux/mutex.h>
     13#include <linux/sysfs.h>
     14#include <linux/printk.h>
     15#include <linux/pm_runtime.h>
     16#include <linux/leds.h>
     17#include <linux/delay.h>
     18
     19/* Addresses to scan - BlinkM is on 0x09 by default*/
     20static const unsigned short normal_i2c[] = { 0x09, I2C_CLIENT_END };
     21
     22static int blinkm_transfer_hw(struct i2c_client *client, int cmd);
     23static int blinkm_test_run(struct i2c_client *client);
     24
     25struct blinkm_led {
     26	struct i2c_client *i2c_client;
     27	struct led_classdev led_cdev;
     28	int id;
     29};
     30
     31#define cdev_to_blmled(c)          container_of(c, struct blinkm_led, led_cdev)
     32
     33struct blinkm_data {
     34	struct i2c_client *i2c_client;
     35	struct mutex update_lock;
     36	/* used for led class interface */
     37	struct blinkm_led blinkm_leds[3];
     38	/* used for "blinkm" sysfs interface */
     39	u8 red;			/* color red */
     40	u8 green;		/* color green */
     41	u8 blue;		/* color blue */
     42	/* next values to use for transfer */
     43	u8 next_red;			/* color red */
     44	u8 next_green;		/* color green */
     45	u8 next_blue;		/* color blue */
     46	/* internal use */
     47	u8 args[7];		/* set of args for transmission */
     48	u8 i2c_addr;		/* i2c addr */
     49	u8 fw_ver;		/* firmware version */
     50	/* used, but not from userspace */
     51	u8 hue;			/* HSB  hue */
     52	u8 saturation;		/* HSB  saturation */
     53	u8 brightness;		/* HSB  brightness */
     54	u8 next_hue;			/* HSB  hue */
     55	u8 next_saturation;		/* HSB  saturation */
     56	u8 next_brightness;		/* HSB  brightness */
     57	/* currently unused / todo */
     58	u8 fade_speed;		/* fade speed     1 - 255 */
     59	s8 time_adjust;		/* time adjust -128 - 127 */
     60	u8 fade:1;		/* fade on = 1, off = 0 */
     61	u8 rand:1;		/* rand fade mode on = 1 */
     62	u8 script_id;		/* script ID */
     63	u8 script_repeats;	/* repeats of script */
     64	u8 script_startline;	/* line to start */
     65};
     66
     67/* Colors */
     68#define RED   0
     69#define GREEN 1
     70#define BLUE  2
     71
     72/* mapping command names to cmd chars - see datasheet */
     73#define BLM_GO_RGB            0
     74#define BLM_FADE_RGB          1
     75#define BLM_FADE_HSB          2
     76#define BLM_FADE_RAND_RGB     3
     77#define BLM_FADE_RAND_HSB     4
     78#define BLM_PLAY_SCRIPT       5
     79#define BLM_STOP_SCRIPT       6
     80#define BLM_SET_FADE_SPEED    7
     81#define BLM_SET_TIME_ADJ      8
     82#define BLM_GET_CUR_RGB       9
     83#define BLM_WRITE_SCRIPT_LINE 10
     84#define BLM_READ_SCRIPT_LINE  11
     85#define BLM_SET_SCRIPT_LR     12	/* Length & Repeats */
     86#define BLM_SET_ADDR          13
     87#define BLM_GET_ADDR          14
     88#define BLM_GET_FW_VER        15
     89#define BLM_SET_STARTUP_PARAM 16
     90
     91/* BlinkM Commands
     92 *  as extracted out of the datasheet:
     93 *
     94 *  cmdchar = command (ascii)
     95 *  cmdbyte = command in hex
     96 *  nr_args = number of arguments (to send)
     97 *  nr_ret  = number of return values (to read)
     98 *  dir = direction (0 = read, 1 = write, 2 = both)
     99 *
    100 */
    101static const struct {
    102	char cmdchar;
    103	u8 cmdbyte;
    104	u8 nr_args;
    105	u8 nr_ret;
    106	u8 dir:2;
    107} blinkm_cmds[17] = {
    108  /* cmdchar, cmdbyte, nr_args, nr_ret,  dir */
    109	{ 'n', 0x6e, 3, 0, 1},
    110	{ 'c', 0x63, 3, 0, 1},
    111	{ 'h', 0x68, 3, 0, 1},
    112	{ 'C', 0x43, 3, 0, 1},
    113	{ 'H', 0x48, 3, 0, 1},
    114	{ 'p', 0x70, 3, 0, 1},
    115	{ 'o', 0x6f, 0, 0, 1},
    116	{ 'f', 0x66, 1, 0, 1},
    117	{ 't', 0x74, 1, 0, 1},
    118	{ 'g', 0x67, 0, 3, 0},
    119	{ 'W', 0x57, 7, 0, 1},
    120	{ 'R', 0x52, 2, 5, 2},
    121	{ 'L', 0x4c, 3, 0, 1},
    122	{ 'A', 0x41, 4, 0, 1},
    123	{ 'a', 0x61, 0, 1, 0},
    124	{ 'Z', 0x5a, 0, 1, 0},
    125	{ 'B', 0x42, 5, 0, 1},
    126};
    127
    128static ssize_t show_color_common(struct device *dev, char *buf, int color)
    129{
    130	struct i2c_client *client;
    131	struct blinkm_data *data;
    132	int ret;
    133
    134	client = to_i2c_client(dev);
    135	data = i2c_get_clientdata(client);
    136
    137	ret = blinkm_transfer_hw(client, BLM_GET_CUR_RGB);
    138	if (ret < 0)
    139		return ret;
    140	switch (color) {
    141	case RED:
    142		return scnprintf(buf, PAGE_SIZE, "%02X\n", data->red);
    143	case GREEN:
    144		return scnprintf(buf, PAGE_SIZE, "%02X\n", data->green);
    145	case BLUE:
    146		return scnprintf(buf, PAGE_SIZE, "%02X\n", data->blue);
    147	default:
    148		return -EINVAL;
    149	}
    150	return -EINVAL;
    151}
    152
    153static int store_color_common(struct device *dev, const char *buf, int color)
    154{
    155	struct i2c_client *client;
    156	struct blinkm_data *data;
    157	int ret;
    158	u8 value;
    159
    160	client = to_i2c_client(dev);
    161	data = i2c_get_clientdata(client);
    162
    163	ret = kstrtou8(buf, 10, &value);
    164	if (ret < 0) {
    165		dev_err(dev, "BlinkM: value too large!\n");
    166		return ret;
    167	}
    168
    169	switch (color) {
    170	case RED:
    171		data->next_red = value;
    172		break;
    173	case GREEN:
    174		data->next_green = value;
    175		break;
    176	case BLUE:
    177		data->next_blue = value;
    178		break;
    179	default:
    180		return -EINVAL;
    181	}
    182
    183	dev_dbg(dev, "next_red = %d, next_green = %d, next_blue = %d\n",
    184			data->next_red, data->next_green, data->next_blue);
    185
    186	/* if mode ... */
    187	ret = blinkm_transfer_hw(client, BLM_GO_RGB);
    188	if (ret < 0) {
    189		dev_err(dev, "BlinkM: can't set RGB\n");
    190		return ret;
    191	}
    192	return 0;
    193}
    194
    195static ssize_t red_show(struct device *dev, struct device_attribute *attr,
    196			char *buf)
    197{
    198	return show_color_common(dev, buf, RED);
    199}
    200
    201static ssize_t red_store(struct device *dev, struct device_attribute *attr,
    202			 const char *buf, size_t count)
    203{
    204	int ret;
    205
    206	ret = store_color_common(dev, buf, RED);
    207	if (ret < 0)
    208		return ret;
    209	return count;
    210}
    211
    212static DEVICE_ATTR_RW(red);
    213
    214static ssize_t green_show(struct device *dev, struct device_attribute *attr,
    215			  char *buf)
    216{
    217	return show_color_common(dev, buf, GREEN);
    218}
    219
    220static ssize_t green_store(struct device *dev, struct device_attribute *attr,
    221			   const char *buf, size_t count)
    222{
    223
    224	int ret;
    225
    226	ret = store_color_common(dev, buf, GREEN);
    227	if (ret < 0)
    228		return ret;
    229	return count;
    230}
    231
    232static DEVICE_ATTR_RW(green);
    233
    234static ssize_t blue_show(struct device *dev, struct device_attribute *attr,
    235			 char *buf)
    236{
    237	return show_color_common(dev, buf, BLUE);
    238}
    239
    240static ssize_t blue_store(struct device *dev, struct device_attribute *attr,
    241			  const char *buf, size_t count)
    242{
    243	int ret;
    244
    245	ret = store_color_common(dev, buf, BLUE);
    246	if (ret < 0)
    247		return ret;
    248	return count;
    249}
    250
    251static DEVICE_ATTR_RW(blue);
    252
    253static ssize_t test_show(struct device *dev, struct device_attribute *attr,
    254			 char *buf)
    255{
    256	return scnprintf(buf, PAGE_SIZE,
    257			 "#Write into test to start test sequence!#\n");
    258}
    259
    260static ssize_t test_store(struct device *dev, struct device_attribute *attr,
    261			  const char *buf, size_t count)
    262{
    263
    264	struct i2c_client *client;
    265	int ret;
    266	client = to_i2c_client(dev);
    267
    268	/*test */
    269	ret = blinkm_test_run(client);
    270	if (ret < 0)
    271		return ret;
    272
    273	return count;
    274}
    275
    276static DEVICE_ATTR_RW(test);
    277
    278/* TODO: HSB, fade, timeadj, script ... */
    279
    280static struct attribute *blinkm_attrs[] = {
    281	&dev_attr_red.attr,
    282	&dev_attr_green.attr,
    283	&dev_attr_blue.attr,
    284	&dev_attr_test.attr,
    285	NULL,
    286};
    287
    288static const struct attribute_group blinkm_group = {
    289	.name = "blinkm",
    290	.attrs = blinkm_attrs,
    291};
    292
    293static int blinkm_write(struct i2c_client *client, int cmd, u8 *arg)
    294{
    295	int result;
    296	int i;
    297	int arglen = blinkm_cmds[cmd].nr_args;
    298	/* write out cmd to blinkm - always / default step */
    299	result = i2c_smbus_write_byte(client, blinkm_cmds[cmd].cmdbyte);
    300	if (result < 0)
    301		return result;
    302	/* no args to write out */
    303	if (arglen == 0)
    304		return 0;
    305
    306	for (i = 0; i < arglen; i++) {
    307		/* repeat for arglen */
    308		result = i2c_smbus_write_byte(client, arg[i]);
    309		if (result < 0)
    310			return result;
    311	}
    312	return 0;
    313}
    314
    315static int blinkm_read(struct i2c_client *client, int cmd, u8 *arg)
    316{
    317	int result;
    318	int i;
    319	int retlen = blinkm_cmds[cmd].nr_ret;
    320	for (i = 0; i < retlen; i++) {
    321		/* repeat for retlen */
    322		result = i2c_smbus_read_byte(client);
    323		if (result < 0)
    324			return result;
    325		arg[i] = result;
    326	}
    327
    328	return 0;
    329}
    330
    331static int blinkm_transfer_hw(struct i2c_client *client, int cmd)
    332{
    333	/* the protocol is simple but non-standard:
    334	 * e.g.  cmd 'g' (= 0x67) for "get device address"
    335	 * - which defaults to 0x09 - would be the sequence:
    336	 *   a) write 0x67 to the device (byte write)
    337	 *   b) read the value (0x09) back right after (byte read)
    338	 *
    339	 * Watch out for "unfinished" sequences (i.e. not enough reads
    340	 * or writes after a command. It will make the blinkM misbehave.
    341	 * Sequence is key here.
    342	 */
    343
    344	/* args / return are in private data struct */
    345	struct blinkm_data *data = i2c_get_clientdata(client);
    346
    347	/* We start hardware transfers which are not to be
    348	 * mixed with other commands. Aquire a lock now. */
    349	if (mutex_lock_interruptible(&data->update_lock) < 0)
    350		return -EAGAIN;
    351
    352	/* switch cmd - usually write before reads */
    353	switch (cmd) {
    354	case BLM_FADE_RAND_RGB:
    355	case BLM_GO_RGB:
    356	case BLM_FADE_RGB:
    357		data->args[0] = data->next_red;
    358		data->args[1] = data->next_green;
    359		data->args[2] = data->next_blue;
    360		blinkm_write(client, cmd, data->args);
    361		data->red = data->args[0];
    362		data->green = data->args[1];
    363		data->blue = data->args[2];
    364		break;
    365	case BLM_FADE_HSB:
    366	case BLM_FADE_RAND_HSB:
    367		data->args[0] = data->next_hue;
    368		data->args[1] = data->next_saturation;
    369		data->args[2] = data->next_brightness;
    370		blinkm_write(client, cmd, data->args);
    371		data->hue = data->next_hue;
    372		data->saturation = data->next_saturation;
    373		data->brightness = data->next_brightness;
    374		break;
    375	case BLM_PLAY_SCRIPT:
    376		data->args[0] = data->script_id;
    377		data->args[1] = data->script_repeats;
    378		data->args[2] = data->script_startline;
    379		blinkm_write(client, cmd, data->args);
    380		break;
    381	case BLM_STOP_SCRIPT:
    382		blinkm_write(client, cmd, NULL);
    383		break;
    384	case BLM_GET_CUR_RGB:
    385		data->args[0] = data->red;
    386		data->args[1] = data->green;
    387		data->args[2] = data->blue;
    388		blinkm_write(client, cmd, NULL);
    389		blinkm_read(client, cmd, data->args);
    390		data->red = data->args[0];
    391		data->green = data->args[1];
    392		data->blue = data->args[2];
    393		break;
    394	case BLM_GET_ADDR:
    395		data->args[0] = data->i2c_addr;
    396		blinkm_write(client, cmd, NULL);
    397		blinkm_read(client, cmd, data->args);
    398		data->i2c_addr = data->args[0];
    399		break;
    400	case BLM_SET_TIME_ADJ:
    401	case BLM_SET_FADE_SPEED:
    402	case BLM_READ_SCRIPT_LINE:
    403	case BLM_WRITE_SCRIPT_LINE:
    404	case BLM_SET_SCRIPT_LR:
    405	case BLM_SET_ADDR:
    406	case BLM_GET_FW_VER:
    407	case BLM_SET_STARTUP_PARAM:
    408		dev_err(&client->dev,
    409				"BlinkM: cmd %d not implemented yet.\n", cmd);
    410		break;
    411	default:
    412		dev_err(&client->dev, "BlinkM: unknown command %d\n", cmd);
    413		mutex_unlock(&data->update_lock);
    414		return -EINVAL;
    415	}			/* end switch(cmd) */
    416
    417	/* transfers done, unlock */
    418	mutex_unlock(&data->update_lock);
    419	return 0;
    420}
    421
    422static int blinkm_led_common_set(struct led_classdev *led_cdev,
    423				 enum led_brightness value, int color)
    424{
    425	/* led_brightness is 0, 127 or 255 - we just use it here as-is */
    426	struct blinkm_led *led = cdev_to_blmled(led_cdev);
    427	struct blinkm_data *data = i2c_get_clientdata(led->i2c_client);
    428
    429	switch (color) {
    430	case RED:
    431		/* bail out if there's no change */
    432		if (data->next_red == (u8) value)
    433			return 0;
    434		data->next_red = (u8) value;
    435		break;
    436	case GREEN:
    437		/* bail out if there's no change */
    438		if (data->next_green == (u8) value)
    439			return 0;
    440		data->next_green = (u8) value;
    441		break;
    442	case BLUE:
    443		/* bail out if there's no change */
    444		if (data->next_blue == (u8) value)
    445			return 0;
    446		data->next_blue = (u8) value;
    447		break;
    448
    449	default:
    450		dev_err(&led->i2c_client->dev, "BlinkM: unknown color.\n");
    451		return -EINVAL;
    452	}
    453
    454	blinkm_transfer_hw(led->i2c_client, BLM_GO_RGB);
    455	dev_dbg(&led->i2c_client->dev,
    456			"# DONE # next_red = %d, next_green = %d,"
    457			" next_blue = %d\n",
    458			data->next_red, data->next_green,
    459			data->next_blue);
    460	return 0;
    461}
    462
    463static int blinkm_led_red_set(struct led_classdev *led_cdev,
    464			       enum led_brightness value)
    465{
    466	return blinkm_led_common_set(led_cdev, value, RED);
    467}
    468
    469static int blinkm_led_green_set(struct led_classdev *led_cdev,
    470				 enum led_brightness value)
    471{
    472	return blinkm_led_common_set(led_cdev, value, GREEN);
    473}
    474
    475static int blinkm_led_blue_set(struct led_classdev *led_cdev,
    476				enum led_brightness value)
    477{
    478	return blinkm_led_common_set(led_cdev, value, BLUE);
    479}
    480
    481static void blinkm_init_hw(struct i2c_client *client)
    482{
    483	blinkm_transfer_hw(client, BLM_STOP_SCRIPT);
    484	blinkm_transfer_hw(client, BLM_GO_RGB);
    485}
    486
    487static int blinkm_test_run(struct i2c_client *client)
    488{
    489	int ret;
    490	struct blinkm_data *data = i2c_get_clientdata(client);
    491
    492	data->next_red = 0x01;
    493	data->next_green = 0x05;
    494	data->next_blue = 0x10;
    495	ret = blinkm_transfer_hw(client, BLM_GO_RGB);
    496	if (ret < 0)
    497		return ret;
    498	msleep(2000);
    499
    500	data->next_red = 0x25;
    501	data->next_green = 0x10;
    502	data->next_blue = 0x31;
    503	ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
    504	if (ret < 0)
    505		return ret;
    506	msleep(2000);
    507
    508	data->next_hue = 0x50;
    509	data->next_saturation = 0x10;
    510	data->next_brightness = 0x20;
    511	ret = blinkm_transfer_hw(client, BLM_FADE_HSB);
    512	if (ret < 0)
    513		return ret;
    514	msleep(2000);
    515
    516	return 0;
    517}
    518
    519/* Return 0 if detection is successful, -ENODEV otherwise */
    520static int blinkm_detect(struct i2c_client *client, struct i2c_board_info *info)
    521{
    522	struct i2c_adapter *adapter = client->adapter;
    523	int ret;
    524	int count = 99;
    525	u8 tmpargs[7];
    526
    527	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA
    528				     | I2C_FUNC_SMBUS_WORD_DATA
    529				     | I2C_FUNC_SMBUS_WRITE_BYTE))
    530		return -ENODEV;
    531
    532	/* Now, we do the remaining detection. Simple for now. */
    533	/* We might need more guards to protect other i2c slaves */
    534
    535	/* make sure the blinkM is balanced (read/writes) */
    536	while (count > 0) {
    537		ret = blinkm_write(client, BLM_GET_ADDR, NULL);
    538		if (ret)
    539			return ret;
    540		usleep_range(5000, 10000);
    541		ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);
    542		if (ret)
    543			return ret;
    544		usleep_range(5000, 10000);
    545		if (tmpargs[0] == 0x09)
    546			count = 0;
    547		count--;
    548	}
    549
    550	/* Step 1: Read BlinkM address back  -  cmd_char 'a' */
    551	ret = blinkm_write(client, BLM_GET_ADDR, NULL);
    552	if (ret < 0)
    553		return ret;
    554	usleep_range(20000, 30000);	/* allow a small delay */
    555	ret = blinkm_read(client, BLM_GET_ADDR, tmpargs);
    556	if (ret < 0)
    557		return ret;
    558
    559	if (tmpargs[0] != 0x09) {
    560		dev_err(&client->dev, "enodev DEV ADDR = 0x%02X\n", tmpargs[0]);
    561		return -ENODEV;
    562	}
    563
    564	strlcpy(info->type, "blinkm", I2C_NAME_SIZE);
    565	return 0;
    566}
    567
    568static int blinkm_probe(struct i2c_client *client,
    569			const struct i2c_device_id *id)
    570{
    571	struct blinkm_data *data;
    572	struct blinkm_led *led[3];
    573	int err, i;
    574	char blinkm_led_name[28];
    575
    576	data = devm_kzalloc(&client->dev,
    577			sizeof(struct blinkm_data), GFP_KERNEL);
    578	if (!data) {
    579		err = -ENOMEM;
    580		goto exit;
    581	}
    582
    583	data->i2c_addr = 0x08;
    584	/* i2c addr  - use fake addr of 0x08 initially (real is 0x09) */
    585	data->fw_ver = 0xfe;
    586	/* firmware version - use fake until we read real value
    587	 * (currently broken - BlinkM confused!) */
    588	data->script_id = 0x01;
    589	data->i2c_client = client;
    590
    591	i2c_set_clientdata(client, data);
    592	mutex_init(&data->update_lock);
    593
    594	/* Register sysfs hooks */
    595	err = sysfs_create_group(&client->dev.kobj, &blinkm_group);
    596	if (err < 0) {
    597		dev_err(&client->dev, "couldn't register sysfs group\n");
    598		goto exit;
    599	}
    600
    601	for (i = 0; i < 3; i++) {
    602		/* RED = 0, GREEN = 1, BLUE = 2 */
    603		led[i] = &data->blinkm_leds[i];
    604		led[i]->i2c_client = client;
    605		led[i]->id = i;
    606		led[i]->led_cdev.max_brightness = 255;
    607		led[i]->led_cdev.flags = LED_CORE_SUSPENDRESUME;
    608		switch (i) {
    609		case RED:
    610			snprintf(blinkm_led_name, sizeof(blinkm_led_name),
    611					 "blinkm-%d-%d-red",
    612					 client->adapter->nr,
    613					 client->addr);
    614			led[i]->led_cdev.name = blinkm_led_name;
    615			led[i]->led_cdev.brightness_set_blocking =
    616							blinkm_led_red_set;
    617			err = led_classdev_register(&client->dev,
    618						    &led[i]->led_cdev);
    619			if (err < 0) {
    620				dev_err(&client->dev,
    621					"couldn't register LED %s\n",
    622					led[i]->led_cdev.name);
    623				goto failred;
    624			}
    625			break;
    626		case GREEN:
    627			snprintf(blinkm_led_name, sizeof(blinkm_led_name),
    628					 "blinkm-%d-%d-green",
    629					 client->adapter->nr,
    630					 client->addr);
    631			led[i]->led_cdev.name = blinkm_led_name;
    632			led[i]->led_cdev.brightness_set_blocking =
    633							blinkm_led_green_set;
    634			err = led_classdev_register(&client->dev,
    635						    &led[i]->led_cdev);
    636			if (err < 0) {
    637				dev_err(&client->dev,
    638					"couldn't register LED %s\n",
    639					led[i]->led_cdev.name);
    640				goto failgreen;
    641			}
    642			break;
    643		case BLUE:
    644			snprintf(blinkm_led_name, sizeof(blinkm_led_name),
    645					 "blinkm-%d-%d-blue",
    646					 client->adapter->nr,
    647					 client->addr);
    648			led[i]->led_cdev.name = blinkm_led_name;
    649			led[i]->led_cdev.brightness_set_blocking =
    650							blinkm_led_blue_set;
    651			err = led_classdev_register(&client->dev,
    652						    &led[i]->led_cdev);
    653			if (err < 0) {
    654				dev_err(&client->dev,
    655					"couldn't register LED %s\n",
    656					led[i]->led_cdev.name);
    657				goto failblue;
    658			}
    659			break;
    660		}		/* end switch */
    661	}			/* end for */
    662
    663	/* Initialize the blinkm */
    664	blinkm_init_hw(client);
    665
    666	return 0;
    667
    668failblue:
    669	led_classdev_unregister(&led[GREEN]->led_cdev);
    670
    671failgreen:
    672	led_classdev_unregister(&led[RED]->led_cdev);
    673
    674failred:
    675	sysfs_remove_group(&client->dev.kobj, &blinkm_group);
    676exit:
    677	return err;
    678}
    679
    680static int blinkm_remove(struct i2c_client *client)
    681{
    682	struct blinkm_data *data = i2c_get_clientdata(client);
    683	int ret = 0;
    684	int i;
    685
    686	/* make sure no workqueue entries are pending */
    687	for (i = 0; i < 3; i++)
    688		led_classdev_unregister(&data->blinkm_leds[i].led_cdev);
    689
    690	/* reset rgb */
    691	data->next_red = 0x00;
    692	data->next_green = 0x00;
    693	data->next_blue = 0x00;
    694	ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
    695	if (ret < 0)
    696		dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
    697
    698	/* reset hsb */
    699	data->next_hue = 0x00;
    700	data->next_saturation = 0x00;
    701	data->next_brightness = 0x00;
    702	ret = blinkm_transfer_hw(client, BLM_FADE_HSB);
    703	if (ret < 0)
    704		dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
    705
    706	/* red fade to off */
    707	data->next_red = 0xff;
    708	ret = blinkm_transfer_hw(client, BLM_GO_RGB);
    709	if (ret < 0)
    710		dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
    711
    712	/* off */
    713	data->next_red = 0x00;
    714	ret = blinkm_transfer_hw(client, BLM_FADE_RGB);
    715	if (ret < 0)
    716		dev_err(&client->dev, "Failure in blinkm_remove ignored. Continuing.\n");
    717
    718	sysfs_remove_group(&client->dev.kobj, &blinkm_group);
    719	return 0;
    720}
    721
    722static const struct i2c_device_id blinkm_id[] = {
    723	{"blinkm", 0},
    724	{}
    725};
    726
    727MODULE_DEVICE_TABLE(i2c, blinkm_id);
    728
    729  /* This is the driver that will be inserted */
    730static struct i2c_driver blinkm_driver = {
    731	.class = I2C_CLASS_HWMON,
    732	.driver = {
    733		   .name = "blinkm",
    734		   },
    735	.probe = blinkm_probe,
    736	.remove = blinkm_remove,
    737	.id_table = blinkm_id,
    738	.detect = blinkm_detect,
    739	.address_list = normal_i2c,
    740};
    741
    742module_i2c_driver(blinkm_driver);
    743
    744MODULE_AUTHOR("Jan-Simon Moeller <dl9pf@gmx.de>");
    745MODULE_DESCRIPTION("BlinkM RGB LED driver");
    746MODULE_LICENSE("GPL");
    747