leds-pca9633.c 4.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/*
 * Copyright 2011 bct electronic GmbH
 *
 * Author: Peter Meerwald <p.meerwald@bct-electronic.com>
 *
 * Based on leds-pca955x.c
 *
 * This file is subject to the terms and conditions of version 2 of
 * the GNU General Public License.  See the file COPYING in the main
 * directory of this archive for more details.
 *
 * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62)
 *
 */

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/leds.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/workqueue.h>
#include <linux/slab.h>
25
#include <linux/platform_data/leds-pca9633.h>
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

/* LED select registers determine the source that drives LED outputs */
#define PCA9633_LED_OFF		0x0	/* LED driver off */
#define PCA9633_LED_ON		0x1	/* LED driver on */
#define PCA9633_LED_PWM		0x2	/* Controlled through PWM */
#define PCA9633_LED_GRP_PWM	0x3	/* Controlled through PWM/GRPPWM */

#define PCA9633_MODE1		0x00
#define PCA9633_MODE2		0x01
#define PCA9633_PWM_BASE	0x02
#define PCA9633_LEDOUT		0x08

static const struct i2c_device_id pca9633_id[] = {
	{ "pca9633", 0 },
	{ }
};
MODULE_DEVICE_TABLE(i2c, pca9633_id);

struct pca9633_led {
	struct i2c_client *client;
	struct work_struct work;
	enum led_brightness brightness;
	struct led_classdev led_cdev;
	int led_num; /* 0 .. 3 potentially */
	char name[32];
};

static void pca9633_led_work(struct work_struct *work)
{
	struct pca9633_led *pca9633 = container_of(work,
		struct pca9633_led, work);
	u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT);
	int shift = 2 * pca9633->led_num;
	u8 mask = 0x3 << shift;

	switch (pca9633->brightness) {
	case LED_FULL:
		i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
			(ledout & ~mask) | (PCA9633_LED_ON << shift));
		break;
	case LED_OFF:
		i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
			ledout & ~mask);
		break;
	default:
		i2c_smbus_write_byte_data(pca9633->client,
			PCA9633_PWM_BASE + pca9633->led_num,
			pca9633->brightness);
		i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT,
			(ledout & ~mask) | (PCA9633_LED_PWM << shift));
		break;
	}
}

static void pca9633_led_set(struct led_classdev *led_cdev,
	enum led_brightness value)
{
	struct pca9633_led *pca9633;

	pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev);

	pca9633->brightness = value;

	/*
	 * Must use workqueue for the actual I/O since I2C operations
	 * can sleep.
	 */
	schedule_work(&pca9633->work);
}

static int __devinit pca9633_probe(struct i2c_client *client,
					const struct i2c_device_id *id)
{
	struct pca9633_led *pca9633;
100
	struct pca9633_platform_data *pdata;
101 102 103 104 105
	int i, err;

	pdata = client->dev.platform_data;

	if (pdata) {
106
		if (pdata->leds.num_leds <= 0 || pdata->leds.num_leds > 4) {
107 108 109 110 111
			dev_err(&client->dev, "board info must claim at most 4 LEDs");
			return -EINVAL;
		}
	}

112
	pca9633 = devm_kzalloc(&client->dev, 4 * sizeof(*pca9633), GFP_KERNEL);
113 114 115 116 117 118 119 120 121 122
	if (!pca9633)
		return -ENOMEM;

	i2c_set_clientdata(client, pca9633);

	for (i = 0; i < 4; i++) {
		pca9633[i].client = client;
		pca9633[i].led_num = i;

		/* Platform data can specify LED names and default triggers */
123 124
		if (pdata && i < pdata->leds.num_leds) {
			if (pdata->leds.leds[i].name)
125 126
				snprintf(pca9633[i].name,
					 sizeof(pca9633[i].name), "pca9633:%s",
127 128
					 pdata->leds.leds[i].name);
			if (pdata->leds.leds[i].default_trigger)
129
				pca9633[i].led_cdev.default_trigger =
130
					pdata->leds.leds[i].default_trigger;
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
		} else {
			snprintf(pca9633[i].name, sizeof(pca9633[i].name),
				 "pca9633:%d", i);
		}

		pca9633[i].led_cdev.name = pca9633[i].name;
		pca9633[i].led_cdev.brightness_set = pca9633_led_set;

		INIT_WORK(&pca9633[i].work, pca9633_led_work);

		err = led_classdev_register(&client->dev, &pca9633[i].led_cdev);
		if (err < 0)
			goto exit;
	}

	/* Disable LED all-call address and set normal mode */
	i2c_smbus_write_byte_data(client, PCA9633_MODE1, 0x00);

149 150 151 152
	/* Configure output: open-drain or totem pole (push-pull) */
	if (pdata && pdata->outdrv == PCA9633_OPEN_DRAIN)
		i2c_smbus_write_byte_data(client, PCA9633_MODE2, 0x01);

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
	/* Turn off LEDs */
	i2c_smbus_write_byte_data(client, PCA9633_LEDOUT, 0x00);

	return 0;

exit:
	while (i--) {
		led_classdev_unregister(&pca9633[i].led_cdev);
		cancel_work_sync(&pca9633[i].work);
	}

	return err;
}

static int __devexit pca9633_remove(struct i2c_client *client)
{
	struct pca9633_led *pca9633 = i2c_get_clientdata(client);
	int i;

	for (i = 0; i < 4; i++) {
		led_classdev_unregister(&pca9633[i].led_cdev);
		cancel_work_sync(&pca9633[i].work);
	}

	return 0;
}

static struct i2c_driver pca9633_driver = {
	.driver = {
		.name	= "leds-pca9633",
		.owner	= THIS_MODULE,
	},
	.probe	= pca9633_probe,
	.remove	= __devexit_p(pca9633_remove),
	.id_table = pca9633_id,
};

module_i2c_driver(pca9633_driver);

MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>");
MODULE_DESCRIPTION("PCA9633 LED driver");
MODULE_LICENSE("GPL v2");