0001
0002
0003
0004
0005
0006 #include <linux/leds.h>
0007 #include <linux/mfd/motorola-cpcap.h>
0008 #include <linux/module.h>
0009 #include <linux/mutex.h>
0010 #include <linux/of_device.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/regmap.h>
0013 #include <linux/regulator/consumer.h>
0014
0015 #define CPCAP_LED_NO_CURRENT 0x0001
0016
0017 struct cpcap_led_info {
0018 u16 reg;
0019 u16 mask;
0020 u16 limit;
0021 u16 init_mask;
0022 u16 init_val;
0023 };
0024
0025 static const struct cpcap_led_info cpcap_led_red = {
0026 .reg = CPCAP_REG_REDC,
0027 .mask = 0x03FF,
0028 .limit = 31,
0029 };
0030
0031 static const struct cpcap_led_info cpcap_led_green = {
0032 .reg = CPCAP_REG_GREENC,
0033 .mask = 0x03FF,
0034 .limit = 31,
0035 };
0036
0037 static const struct cpcap_led_info cpcap_led_blue = {
0038 .reg = CPCAP_REG_BLUEC,
0039 .mask = 0x03FF,
0040 .limit = 31,
0041 };
0042
0043
0044 static const struct cpcap_led_info cpcap_led_adl = {
0045 .reg = CPCAP_REG_ADLC,
0046 .mask = 0x000F,
0047 .limit = 1,
0048 .init_mask = 0x7FFF,
0049 .init_val = 0x5FF0,
0050 };
0051
0052
0053 static const struct cpcap_led_info cpcap_led_cp = {
0054 .reg = CPCAP_REG_CLEDC,
0055 .mask = 0x0007,
0056 .limit = 1,
0057 .init_mask = 0x03FF,
0058 .init_val = 0x0008,
0059 };
0060
0061 struct cpcap_led {
0062 struct led_classdev led;
0063 const struct cpcap_led_info *info;
0064 struct device *dev;
0065 struct regmap *regmap;
0066 struct mutex update_lock;
0067 struct regulator *vdd;
0068 bool powered;
0069
0070 u32 current_limit;
0071 };
0072
0073 static u16 cpcap_led_val(u8 current_limit, u8 duty_cycle)
0074 {
0075 current_limit &= 0x1f;
0076 duty_cycle &= 0x0f;
0077
0078 return current_limit << 4 | duty_cycle;
0079 }
0080
0081 static int cpcap_led_set_power(struct cpcap_led *led, bool status)
0082 {
0083 int err;
0084
0085 if (status == led->powered)
0086 return 0;
0087
0088 if (status)
0089 err = regulator_enable(led->vdd);
0090 else
0091 err = regulator_disable(led->vdd);
0092
0093 if (err) {
0094 dev_err(led->dev, "regulator failure: %d", err);
0095 return err;
0096 }
0097
0098 led->powered = status;
0099
0100 return 0;
0101 }
0102
0103 static int cpcap_led_set(struct led_classdev *ledc, enum led_brightness value)
0104 {
0105 struct cpcap_led *led = container_of(ledc, struct cpcap_led, led);
0106 int brightness;
0107 int err;
0108
0109 mutex_lock(&led->update_lock);
0110
0111 if (value > LED_OFF) {
0112 err = cpcap_led_set_power(led, true);
0113 if (err)
0114 goto exit;
0115 }
0116
0117 if (value == LED_OFF) {
0118
0119 err = regmap_update_bits(led->regmap,
0120 led->info->reg, led->info->mask, CPCAP_LED_NO_CURRENT);
0121 if (err) {
0122 dev_err(led->dev, "regmap failed: %d", err);
0123 goto exit;
0124 }
0125
0126 brightness = cpcap_led_val(value, LED_OFF);
0127 } else {
0128 brightness = cpcap_led_val(value, LED_ON);
0129 }
0130
0131 err = regmap_update_bits(led->regmap, led->info->reg, led->info->mask,
0132 brightness);
0133 if (err) {
0134 dev_err(led->dev, "regmap failed: %d", err);
0135 goto exit;
0136 }
0137
0138 if (value == LED_OFF) {
0139 err = cpcap_led_set_power(led, false);
0140 if (err)
0141 goto exit;
0142 }
0143
0144 exit:
0145 mutex_unlock(&led->update_lock);
0146 return err;
0147 }
0148
0149 static const struct of_device_id cpcap_led_of_match[] = {
0150 { .compatible = "motorola,cpcap-led-red", .data = &cpcap_led_red },
0151 { .compatible = "motorola,cpcap-led-green", .data = &cpcap_led_green },
0152 { .compatible = "motorola,cpcap-led-blue", .data = &cpcap_led_blue },
0153 { .compatible = "motorola,cpcap-led-adl", .data = &cpcap_led_adl },
0154 { .compatible = "motorola,cpcap-led-cp", .data = &cpcap_led_cp },
0155 {},
0156 };
0157 MODULE_DEVICE_TABLE(of, cpcap_led_of_match);
0158
0159 static int cpcap_led_probe(struct platform_device *pdev)
0160 {
0161 struct cpcap_led *led;
0162 int err;
0163
0164 led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
0165 if (!led)
0166 return -ENOMEM;
0167 platform_set_drvdata(pdev, led);
0168 led->info = device_get_match_data(&pdev->dev);
0169 led->dev = &pdev->dev;
0170
0171 if (led->info->reg == 0x0000) {
0172 dev_err(led->dev, "Unsupported LED");
0173 return -ENODEV;
0174 }
0175
0176 led->regmap = dev_get_regmap(pdev->dev.parent, NULL);
0177 if (!led->regmap)
0178 return -ENODEV;
0179
0180 led->vdd = devm_regulator_get(&pdev->dev, "vdd");
0181 if (IS_ERR(led->vdd)) {
0182 err = PTR_ERR(led->vdd);
0183 dev_err(led->dev, "Couldn't get regulator: %d", err);
0184 return err;
0185 }
0186
0187 err = device_property_read_string(&pdev->dev, "label", &led->led.name);
0188 if (err) {
0189 dev_err(led->dev, "Couldn't read LED label: %d", err);
0190 return err;
0191 }
0192
0193 if (led->info->init_mask) {
0194 err = regmap_update_bits(led->regmap, led->info->reg,
0195 led->info->init_mask, led->info->init_val);
0196 if (err) {
0197 dev_err(led->dev, "regmap failed: %d", err);
0198 return err;
0199 }
0200 }
0201
0202 mutex_init(&led->update_lock);
0203
0204 led->led.max_brightness = led->info->limit;
0205 led->led.brightness_set_blocking = cpcap_led_set;
0206 err = devm_led_classdev_register(&pdev->dev, &led->led);
0207 if (err) {
0208 dev_err(led->dev, "Couldn't register LED: %d", err);
0209 return err;
0210 }
0211
0212 return 0;
0213 }
0214
0215 static struct platform_driver cpcap_led_driver = {
0216 .probe = cpcap_led_probe,
0217 .driver = {
0218 .name = "cpcap-led",
0219 .of_match_table = cpcap_led_of_match,
0220 },
0221 };
0222 module_platform_driver(cpcap_led_driver);
0223
0224 MODULE_DESCRIPTION("CPCAP LED driver");
0225 MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");
0226 MODULE_LICENSE("GPL");