blob: 1914db18b935addca21f12a3bf807c0b51f08b25 [file] [log] [blame]
/*
* LED driver : leds-ktd202x.c
*
* Copyright (C) 2017 Google, Inc.
* June Tate-Gans <jtgans@google.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/string.h>
enum ktd202x_registers {
REG0_RESERVED = 0,
REG0_RISEFALL_SCALE,
REG0_ENABLE,
REG0_RESET,
REG0_TSLOT_CONTROL,
REG1_LOG_SCURVE,
REG1_TFLASH,
REG2_PWM1,
REG3_PWM2,
REG4_CH1_ENABLE,
REG4_CH2_ENABLE,
REG4_CH3_ENABLE,
REG4_CH4_ENABLE,
REG5_TFALL,
REG5_TRISE,
REG6_LED1,
REG7_LED2,
REG8_LED3,
REG9_LED4,
REG_MAX,
};
#define REG_LED_BASE REG6_LED1
#define REG_CHANNEL_BASE REG4_CH1_ENABLE
static const struct {
uint8_t addr;
uint8_t mask;
uint8_t shift;
uint8_t max;
const char* name;
} ktd202x_regs[REG_MAX] = {
/* REG0 - Reset/Control */
/* [7] - Reserved */
/* [6:5] - Rise/fall scaling */
/* (1x normal, 2x slower, 4x slower, 8x fast) */
/* [4:3] - Enable control, 11 means always on. */
/* See p13 of the aug 2016 datasheet. */
/* [2] - Reset / Offset cancel, with [2] set, then [1:0] set for */
/* reset mode */
/* [1:0] - Timer slot control with [2] clear */
[REG0_RESERVED] = {0x00, 0x80, 7, 0x00, "reserved"},
[REG0_RISEFALL_SCALE] = {0x00, 0x60, 5, 0x03, "risefall_scale"},
[REG0_ENABLE] = {0x00, 0x18, 3, 0x03, "enable"},
[REG0_RESET] = {0x00, 0x07, 0, 0x00, "reset" },
[REG0_TSLOT_CONTROL] = {0x00, 0x07, 0, 0x04, "tslot_control" },
/* REG1 - Flash period (MSB toggles log/s-curve) */
/* [7] - Log / S-curve */
/* [6:0] - T_flash period (1111111 is one shot) see p14 of the aug */
/* 2016 datasheet. */
[REG1_LOG_SCURVE] = {0x01, 0x80, 7, 0x01, "ramp"},
[REG1_TFLASH] = {0x01, 0x7F, 0, 0x7F, "tflash"},
[REG2_PWM1] = {0x02, 0xFF, 0, 0xFF, "pwm1"},
[REG3_PWM2] = {0x03, 0xFF, 0, 0xFF, "pwm2"},
/* REG4 - Channel enable */
/* [7:6] - LED4 enable (0 off, 1 on, 2 PWM1, 3 PWM2) */
/* [5:4] - LED3 enable */
/* [3:2] - LED2 enable */
/* [1:0] - LED1 enable */
[REG4_CH1_ENABLE] = {0x04, 0x03, 0, 0x04, "ch1_enable"},
[REG4_CH2_ENABLE] = {0x04, 0x0C, 2, 0x04, "ch2_enable"},
[REG4_CH3_ENABLE] = {0x04, 0x30, 4, 0x04, "ch3_enable"},
[REG4_CH4_ENABLE] = {0x04, 0xC0, 6, 0x04, "ch4_enable"},
/* REG5 - Ramp Register */
/* [7:4] - T_fall */
/* [3:0] - T_rise */
[REG5_TFALL] = {0x05, 0xF0, 4, 0x0F, "trise"},
[REG5_TRISE] = {0x05, 0x0F, 0, 0x0F, "tfall"},
[REG6_LED1] = {0x06, 0xFF, 0, 0xFF, "led1"},
[REG7_LED2] = {0x07, 0xFF, 0, 0xFF, "led2"},
[REG8_LED3] = {0x08, 0xFF, 0, 0xFF, "led3"},
[REG9_LED4] = {0x09, 0xFF, 0, 0xFF, "led4"},
};
static int ktd202x_get_register_by_name(const char* name)
{
int i;
for (i = 0; i < REG_MAX; ++i) {
if (strcmp(name, ktd202x_regs[i].name) == 0)
return i;
}
return -1;
}
/*
* The KTD202x series of chips do not handle i2c reads correctly. This driver
* caches internal register states instead.
*/
bool ktd202x_check_readable_register(struct device *dev, unsigned int reg) {
return false;
}
static const struct reg_default ktd202x_register_defaults[] = {
{ 0x00, 0x00 }, /* Reset/Control */
{ 0x01, 0x00 }, /* Flash Period */
{ 0x02, 0x01 }, /* PWM1 Timer */
{ 0x03, 0x01 }, /* PWM2 Timer */
{ 0x04, 0x00 }, /* Channel Enable */
{ 0x05, 0x00 }, /* Ramp Rate */
{ 0x06, 0x4f }, /* LED1 Brightness */
{ 0x07, 0x4f }, /* LED2 Brightness */
{ 0x08, 0x4f }, /* LED3 Brightness */
{ 0x09, 0x4f }, /* LED4 Brightness */
};
static const struct regmap_config ktd202x_i2c_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0x09,
.cache_type = REGCACHE_FLAT,
.can_multi_write = true,
.readable_reg = ktd202x_check_readable_register,
.reg_defaults = ktd202x_register_defaults,
.num_reg_defaults = ARRAY_SIZE(ktd202x_register_defaults),
};
/* REG0[6:5] */
enum Reg0RiseFallScalingModes {
KTD202X_RISEFALL_NORMAL = 0x0,
KTD202X_RISEFALL_2X_SLOW = 0x1,
KTD202X_RISEFALL_4X_SLOW = 0x2,
KTD202X_RISEFALL_8X_FAST = 0x3,
};
/* REG0[4:3] */
enum Reg0EnableControl {
KTD202X_ENABLE_SCL_SDA_HIGH = 0x0,
KTD202X_ENABLE_SDA_TOGGLING = 0x1,
KTD202X_ENABLE_SCL_HIGH = 0x2,
KTD202X_ENABLE_ALWAYS_ON = 0x3,
};
/* REG0[2:0] becomes reset control when MSB is high */
enum Reg0TimerSlotControl {
KTD202X_TCTRL_TSLOT1 = 0x0,
KTD202X_TCTRL_TSLOT2 = 0x1,
KTD202X_TCTRL_TSLOT3 = 0x2,
KTD202X_TCTRL_TSLOT4 = 0x3,
};
// REG2 - PWM1 Flash On Timer 1 (percent of T_flash period)
// REG3 - PWM2 Flash On Timer 2 (percent of T_flash period)
enum Reg4ChannelEnable {
KTD202X_CHANNEL_OFF = 0x0,
KTD202X_CHANNEL_ON = 0x1,
KTD202X_CHANNEL_PWM1 = 0x2,
KTD202X_CHANNEL_PWM2 = 0x3,
};
// REG6 - LED1 Output, 8-bit, 0.125mA (0) to 24mA (255), 0.125mA steps
// REG7 - LED2 Output, 8-bit, 0.125mA (0) to 24mA (255), 0.125mA steps
// REG8 - LED3 Output, 8-bit, 0.125mA (0) to 24mA (255), 0.125mA steps
// REG9 - LED4 Output, 8-bit, 0.125mA (0) to 24mA (255), 0.125mA steps
#define KTD202X_LED_MAX_BRIGHTNESS 255
struct ktd202x_channel {
struct led_classdev cdev;
uint8_t channel_no;
};
struct ktd202x_context {
struct i2c_client *client;
struct regmap *regmap;
struct ktd202x_channel led_channel[4];
};
static struct ktd202x_channel *
to_ktd202x_channel(struct led_classdev* led_cdev) {
struct ktd202x_channel *ch = container_of(led_cdev,
struct ktd202x_channel, cdev);
return ch;
}
static struct ktd202x_context *
to_ktd202x_context(struct led_classdev* led_cdev) {
struct i2c_client* client = to_i2c_client(led_cdev->dev->parent);
struct ktd202x_context* context = i2c_get_clientdata(client);
return context;
}
static inline int ktd202x_update_bits(struct regmap *regmap,
unsigned int reg,
unsigned int data)
{
uint8_t output = data << ktd202x_regs[reg].shift;
int result = 0;
result = regmap_update_bits(regmap,
ktd202x_regs[reg].addr,
ktd202x_regs[reg].mask,
output);
if (result < 0) {
pr_err("ktd202x: regmap_update_bits failed: %d.", result);
}
return result;
}
static inline int ktd202x_get_bits(struct regmap *regmap,
int reg,
unsigned int *out)
{
int read_result = 0;
int result = regmap_read(regmap, ktd202x_regs[reg].addr, &read_result);
if (result < 0) {
pr_err("ktd202x: regmap_read failed %d.", result);
return result;
}
*out = (read_result & ktd202x_regs[reg].mask) >> ktd202x_regs[reg].shift;
return 0;
}
static int ktd202x_reset(struct ktd202x_context *ctx)
{
int ret;
/* Reset must occur by writing Reg0[2:0] = 0b111 on init. need a 200us
delay before the next command. Note that during reset, the chip NAKs
the request, so we throw away the result here. */
regmap_write(ctx->regmap, ktd202x_regs[REG0_RESET].addr, 0x07);
udelay(300);
ret = regmap_reinit_cache(ctx->regmap, &ktd202x_i2c_regmap_config);
if (ret < 0) {
dev_err(&ctx->client->dev, "Could not reinit cache: %d", ret);
return ret;
}
/* Ensure we always listen to i2c by forcing the device to always be
on */
ret = regmap_write(ctx->regmap, ktd202x_regs[REG0_ENABLE].addr, 0x03);
if (ret < 0) {
dev_err(&ctx->client->dev,
"Could not set chip power management: %d", ret);
return ret;
}
return 0;
}
static void ktd202x_brightness_set(struct led_classdev *led_cdev,
enum led_brightness brightness)
{
enum Reg4ChannelEnable state =
(brightness == LED_OFF) ? KTD202X_CHANNEL_OFF : KTD202X_CHANNEL_ON;
struct ktd202x_channel *ch = to_ktd202x_channel(led_cdev);
struct ktd202x_context *context = to_ktd202x_context(led_cdev);
int ret = 0;
ret = ktd202x_update_bits(context->regmap,
REG_LED_BASE + ch->channel_no, brightness);
if (ret < 0) {
dev_err(led_cdev->dev,
"failed to update brightness for channel %d: 0x%x\n",
ch->channel_no,
ret);
return;
}
ret = ktd202x_update_bits(context->regmap,
REG_CHANNEL_BASE + ch->channel_no, state);
if (ret < 0) {
dev_err(led_cdev->dev,
"failed to set channel enable to 0x%x for channel %d\n",
state,
ch->channel_no);
}
}
static enum led_brightness ktd202x_brightness_get(struct led_classdev *led_cdev)
{
struct ktd202x_channel *ch = to_ktd202x_channel(led_cdev);
struct ktd202x_context *context = to_ktd202x_context(led_cdev);
int ret = 0;
unsigned int result = 0;
ret = ktd202x_get_bits(context->regmap,
REG_CHANNEL_BASE + ch->channel_no,
&result);
if (ret < 0) {
dev_err(led_cdev->dev,
"failed to get bits for channel %d -- returning 0\n",
ch->channel_no);
return 0;
}
/* Channel isn't enabled, so effectively has a brightness of 0. */
if (result == 0) return 0;
/* Channel is enabled, so let's actually get the real brightness. */
ret = ktd202x_get_bits(context->regmap,
REG_LED_BASE + ch->channel_no,
&result);
if (ret < 0) {
dev_err(led_cdev->dev,
"failed to get bits for channel %d -- returning 0\n",
ch->channel_no);
return 0;
}
return result;
}
static const char *const channel_names[] = {
"ktd202x:led1",
"ktd202x:led2",
"ktd202x:led3",
"ktd202x:led4",
};
static int ktd202x_init(struct ktd202x_context *ctx)
{
struct ktd202x_channel *ch;
int i, err;
/* Reset before the LED classdev driver gets a chance to query for
brightness values. */
err = ktd202x_reset(ctx);
if (err < 0) return err;
for (i = 0; i < ARRAY_SIZE(channel_names); ++i) {
ch = &ctx->led_channel[i];
ch->channel_no = i;
ch->cdev.name = channel_names[i];
ch->cdev.brightness = LED_OFF;
ch->cdev.max_brightness = KTD202X_LED_MAX_BRIGHTNESS;
ch->cdev.brightness_set = ktd202x_brightness_set;
ch->cdev.brightness_get = ktd202x_brightness_get;
err = devm_led_classdev_register(&ctx->client->dev, &ch->cdev);
if (err < 0) {
dev_err(&ctx->client->dev,
"Led register failed for channel %d: %d\n",
i, err);
return err;
}
}
return 0;
}
static ssize_t ktd202x_show_register(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct ktd202x_context *ctx = dev_get_drvdata(dev);
int reg = ktd202x_get_register_by_name(attr->attr.name);
unsigned int value = 0;
int res;
if (!ctx || reg == -1) return -ENOENT;
res = ktd202x_get_bits(ctx->regmap, reg, &value);
if (res < 0) return res;
return sprintf(buf, "%d\n", value);
}
static ssize_t ktd202x_store_register(struct device *dev,
struct device_attribute *attr,
const char* buf, size_t count)
{
u8 value = 0;
struct ktd202x_context *ctx = dev_get_drvdata(dev);
int reg = ktd202x_get_register_by_name(attr->attr.name);
int result = kstrtou8(buf, 10, &value);
if (!ctx || reg == -1) return -ENOENT;
if (result != 0) return -ERANGE;
if (value > ktd202x_regs[reg].max) return -ERANGE;
result = ktd202x_update_bits(ctx->regmap, reg, value);
if (result < 0) return result;
return count;
}
static ssize_t ktd202x_reset_store(struct device *dev,
struct device_attribute *attr,
const char* buf, size_t count)
{
struct ktd202x_context *ctx = dev_get_drvdata(dev);
int err = ktd202x_reset(ctx);
if (err < 0) return err;
return count;
}
/*
* Parses "reg=value;" string where reg is a register name and value is in
* range 0-255.
*/
static int ktd202x_parse_reg_and_value(char **str, struct reg_default *parsed)
{
char *reg_name, *reg_value;
int reg;
u8 value;
reg_name = strsep(str, "=");
if (!reg_name)
return -EINVAL;
reg = ktd202x_get_register_by_name(reg_name);
if (reg == -1)
return -EINVAL;
reg_value = strsep(str, ";");
if (!reg_value)
return -EINVAL;
if (kstrtou8(reg_value, 10, &value) < 0)
return -EINVAL;
if (value > ktd202x_regs[reg].max)
return -EINVAL;
parsed->reg = reg;
parsed->def = value;
return 0;
}
/*
* Parses "reg=val;reg=val;...;reg=val;" command to update register values.
* For example, "led1=230;led2=100;trise=5;" sets led1 to 230, led2 to 100,
* and trise to 5.
*/
static ssize_t ktd202x_store_registers(struct device *dev,
struct device_attribute *attr,
const char* buf, size_t count)
{
struct ktd202x_context *ctx = dev_get_drvdata(dev);
struct reg_default parsed[128] = {0};
char config[128 + 1] = {0};
char *str = config;
int result, i, n = 0;
sscanf(buf, "%128s\n", config);
while (*str && n < ARRAY_SIZE(parsed)) {
result = ktd202x_parse_reg_and_value(&str, &parsed[n++]);
if (result < 0)
return result;
if (!str)
return -EINVAL;
dev_dbg(dev, "reg=%d, value=%d",
parsed[n - 1].reg, parsed[n - 1].def);
}
for (i = 0; i < n; ++i) {
result = ktd202x_update_bits(ctx->regmap,
parsed[i].reg, parsed[i].def);
if (result < 0)
return result;
}
return count;
}
static DEVICE_ATTR(reset, 0200, NULL, ktd202x_reset_store);
static DEVICE_ATTR(tflash, 0644, ktd202x_show_register, ktd202x_store_register);
static DEVICE_ATTR(ramp, 0644, ktd202x_show_register, ktd202x_store_register);
static DEVICE_ATTR(pwm1, 0644, ktd202x_show_register, ktd202x_store_register);
static DEVICE_ATTR(pwm2, 0644, ktd202x_show_register, ktd202x_store_register);
static DEVICE_ATTR(trise, 0644, ktd202x_show_register, ktd202x_store_register);
static DEVICE_ATTR(tfall, 0644, ktd202x_show_register, ktd202x_store_register);
static DEVICE_ATTR(ch1_enable, 0644, ktd202x_show_register,
ktd202x_store_register);
static DEVICE_ATTR(ch2_enable, 0644, ktd202x_show_register,
ktd202x_store_register);
static DEVICE_ATTR(ch3_enable, 0644, ktd202x_show_register,
ktd202x_store_register);
static DEVICE_ATTR(ch4_enable, 0644, ktd202x_show_register,
ktd202x_store_register);
static DEVICE_ATTR(led1, 0644, ktd202x_show_register, ktd202x_store_register);
static DEVICE_ATTR(led2, 0644, ktd202x_show_register, ktd202x_store_register);
static DEVICE_ATTR(led3, 0644, ktd202x_show_register, ktd202x_store_register);
static DEVICE_ATTR(led4, 0644, ktd202x_show_register, ktd202x_store_register);
static DEVICE_ATTR(registers, 0644, NULL, ktd202x_store_registers);
static struct attribute *ktd202x_dev_attrs[] = {
&dev_attr_reset.attr,
&dev_attr_tflash.attr,
&dev_attr_ramp.attr,
&dev_attr_pwm1.attr,
&dev_attr_pwm2.attr,
&dev_attr_trise.attr,
&dev_attr_tfall.attr,
&dev_attr_ch1_enable.attr,
&dev_attr_ch2_enable.attr,
&dev_attr_ch3_enable.attr,
&dev_attr_ch4_enable.attr,
&dev_attr_led1.attr,
&dev_attr_led2.attr,
&dev_attr_led3.attr,
&dev_attr_led4.attr,
&dev_attr_registers.attr,
NULL
};
static struct attribute_group ktd202x_dev_attr_group = {
.attrs = ktd202x_dev_attrs,
};
static int ktd202x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct ktd202x_context *ctx = NULL;
int err;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx) return -ENOMEM;
ctx->client = client;
ctx->regmap = devm_regmap_init_i2c(client, &ktd202x_i2c_regmap_config);
if (IS_ERR(ctx->regmap)) {
err = PTR_ERR(ctx->regmap);
dev_err(dev, "Failed to allocate register map: %d\n", err);
return err;
}
err = sysfs_create_group(&dev->kobj, &ktd202x_dev_attr_group);
if (err) {
dev_err(dev, "Failed to register attribute groups: %d\n", err);
return err;
}
i2c_set_clientdata(client, ctx);
ktd202x_init(ctx);
dev_info(dev, "Driver loaded for a %s.\n", id->name);
return 0;
}
static int ktd202x_remove(struct i2c_client *client)
{
struct ktd202x_context *ctx = i2c_get_clientdata(client);
ktd202x_reset(ctx);
sysfs_remove_group(&client->dev.kobj, &ktd202x_dev_attr_group);
return 0;
}
static const struct of_device_id ktd202x_of_match[] = {
{ .compatible = "kinetic,ktd2026" },
{ .compatible = "kinetic,ktd2027" },
{},
};
MODULE_DEVICE_TABLE(of, ktd202x_of_match);
static const struct i2c_device_id ktd202x_idtable[] = {
{ "ktd2026", 0 },
{ "ktd2027", 0 },
{},
};
MODULE_DEVICE_TABLE(i2c, ktd202x_idtable);
static struct i2c_driver ktd202x_driver = {
.driver = {
.name = "ktd202x",
.of_match_table = of_match_ptr(ktd202x_of_match),
},
.probe = ktd202x_probe,
.remove = ktd202x_remove,
.id_table = ktd202x_idtable,
};
module_i2c_driver(ktd202x_driver);
MODULE_AUTHOR("June Tate-Gans <jtgans@google.com>");
MODULE_DESCRIPTION("Kinetic KTD2026/7 LED driver");
MODULE_LICENSE("GPL v2");