Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.9-rc2 194 lines 5.0 kB view raw
1/* 2 * Copyright 2011 bct electronic GmbH 3 * 4 * Author: Peter Meerwald <p.meerwald@bct-electronic.com> 5 * 6 * Based on leds-pca955x.c 7 * 8 * This file is subject to the terms and conditions of version 2 of 9 * the GNU General Public License. See the file COPYING in the main 10 * directory of this archive for more details. 11 * 12 * LED driver for the PCA9633 I2C LED driver (7-bit slave address 0x62) 13 * 14 */ 15 16#include <linux/module.h> 17#include <linux/delay.h> 18#include <linux/string.h> 19#include <linux/ctype.h> 20#include <linux/leds.h> 21#include <linux/err.h> 22#include <linux/i2c.h> 23#include <linux/workqueue.h> 24#include <linux/slab.h> 25#include <linux/platform_data/leds-pca9633.h> 26 27/* LED select registers determine the source that drives LED outputs */ 28#define PCA9633_LED_OFF 0x0 /* LED driver off */ 29#define PCA9633_LED_ON 0x1 /* LED driver on */ 30#define PCA9633_LED_PWM 0x2 /* Controlled through PWM */ 31#define PCA9633_LED_GRP_PWM 0x3 /* Controlled through PWM/GRPPWM */ 32 33#define PCA9633_MODE1 0x00 34#define PCA9633_MODE2 0x01 35#define PCA9633_PWM_BASE 0x02 36#define PCA9633_LEDOUT 0x08 37 38static const struct i2c_device_id pca9633_id[] = { 39 { "pca9633", 0 }, 40 { } 41}; 42MODULE_DEVICE_TABLE(i2c, pca9633_id); 43 44struct pca9633_led { 45 struct i2c_client *client; 46 struct work_struct work; 47 enum led_brightness brightness; 48 struct led_classdev led_cdev; 49 int led_num; /* 0 .. 3 potentially */ 50 char name[32]; 51}; 52 53static void pca9633_led_work(struct work_struct *work) 54{ 55 struct pca9633_led *pca9633 = container_of(work, 56 struct pca9633_led, work); 57 u8 ledout = i2c_smbus_read_byte_data(pca9633->client, PCA9633_LEDOUT); 58 int shift = 2 * pca9633->led_num; 59 u8 mask = 0x3 << shift; 60 61 switch (pca9633->brightness) { 62 case LED_FULL: 63 i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, 64 (ledout & ~mask) | (PCA9633_LED_ON << shift)); 65 break; 66 case LED_OFF: 67 i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, 68 ledout & ~mask); 69 break; 70 default: 71 i2c_smbus_write_byte_data(pca9633->client, 72 PCA9633_PWM_BASE + pca9633->led_num, 73 pca9633->brightness); 74 i2c_smbus_write_byte_data(pca9633->client, PCA9633_LEDOUT, 75 (ledout & ~mask) | (PCA9633_LED_PWM << shift)); 76 break; 77 } 78} 79 80static void pca9633_led_set(struct led_classdev *led_cdev, 81 enum led_brightness value) 82{ 83 struct pca9633_led *pca9633; 84 85 pca9633 = container_of(led_cdev, struct pca9633_led, led_cdev); 86 87 pca9633->brightness = value; 88 89 /* 90 * Must use workqueue for the actual I/O since I2C operations 91 * can sleep. 92 */ 93 schedule_work(&pca9633->work); 94} 95 96static int pca9633_probe(struct i2c_client *client, 97 const struct i2c_device_id *id) 98{ 99 struct pca9633_led *pca9633; 100 struct pca9633_platform_data *pdata; 101 int i, err; 102 103 pdata = client->dev.platform_data; 104 105 if (pdata) { 106 if (pdata->leds.num_leds <= 0 || pdata->leds.num_leds > 4) { 107 dev_err(&client->dev, "board info must claim at most 4 LEDs"); 108 return -EINVAL; 109 } 110 } 111 112 pca9633 = devm_kzalloc(&client->dev, 4 * sizeof(*pca9633), GFP_KERNEL); 113 if (!pca9633) 114 return -ENOMEM; 115 116 i2c_set_clientdata(client, pca9633); 117 118 for (i = 0; i < 4; i++) { 119 pca9633[i].client = client; 120 pca9633[i].led_num = i; 121 122 /* Platform data can specify LED names and default triggers */ 123 if (pdata && i < pdata->leds.num_leds) { 124 if (pdata->leds.leds[i].name) 125 snprintf(pca9633[i].name, 126 sizeof(pca9633[i].name), "pca9633:%s", 127 pdata->leds.leds[i].name); 128 if (pdata->leds.leds[i].default_trigger) 129 pca9633[i].led_cdev.default_trigger = 130 pdata->leds.leds[i].default_trigger; 131 } else { 132 snprintf(pca9633[i].name, sizeof(pca9633[i].name), 133 "pca9633:%d", i); 134 } 135 136 pca9633[i].led_cdev.name = pca9633[i].name; 137 pca9633[i].led_cdev.brightness_set = pca9633_led_set; 138 139 INIT_WORK(&pca9633[i].work, pca9633_led_work); 140 141 err = led_classdev_register(&client->dev, &pca9633[i].led_cdev); 142 if (err < 0) 143 goto exit; 144 } 145 146 /* Disable LED all-call address and set normal mode */ 147 i2c_smbus_write_byte_data(client, PCA9633_MODE1, 0x00); 148 149 /* Configure output: open-drain or totem pole (push-pull) */ 150 if (pdata && pdata->outdrv == PCA9633_OPEN_DRAIN) 151 i2c_smbus_write_byte_data(client, PCA9633_MODE2, 0x01); 152 153 /* Turn off LEDs */ 154 i2c_smbus_write_byte_data(client, PCA9633_LEDOUT, 0x00); 155 156 return 0; 157 158exit: 159 while (i--) { 160 led_classdev_unregister(&pca9633[i].led_cdev); 161 cancel_work_sync(&pca9633[i].work); 162 } 163 164 return err; 165} 166 167static int pca9633_remove(struct i2c_client *client) 168{ 169 struct pca9633_led *pca9633 = i2c_get_clientdata(client); 170 int i; 171 172 for (i = 0; i < 4; i++) { 173 led_classdev_unregister(&pca9633[i].led_cdev); 174 cancel_work_sync(&pca9633[i].work); 175 } 176 177 return 0; 178} 179 180static struct i2c_driver pca9633_driver = { 181 .driver = { 182 .name = "leds-pca9633", 183 .owner = THIS_MODULE, 184 }, 185 .probe = pca9633_probe, 186 .remove = pca9633_remove, 187 .id_table = pca9633_id, 188}; 189 190module_i2c_driver(pca9633_driver); 191 192MODULE_AUTHOR("Peter Meerwald <p.meerwald@bct-electronic.com>"); 193MODULE_DESCRIPTION("PCA9633 LED driver"); 194MODULE_LICENSE("GPL v2");