diff --git a/drivers/leds/leds-aw2013.c b/drivers/leds/leds-aw2013.c
index 216755d6010f..a51b72de21ac 100644
--- a/drivers/leds/leds-aw2013.c
+++ b/drivers/leds/leds-aw2013.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0+
-// Driver for Awinic AW2013 3-channel LED driver
+// Driver for Awinic AW2013/AW2027 3-channel LED drivers
#include <linux/i2c.h>
#include <linux/leds.h>
@@ -20,13 +20,15 @@
#define AW2013_GCR 0x01
#define AW2013_GCR_ENABLE BIT(0)
+#define AW20XX_GCR2 0x04
+
/* LED channel enable register */
#define AW2013_LCTR 0x30
#define AW2013_LCTR_LE(x) BIT((x))
/* LED channel control registers */
#define AW2013_LCFG(x) (0x31 + (x))
-#define AW2013_LCFG_IMAX_MASK (BIT(0) | BIT(1)) // Should be 0-3
+#define AW2013_IMAX_MASK (BIT(0) | BIT(1)) // Should be 0-3
#define AW2013_LCFG_MD BIT(4)
#define AW2013_LCFG_FI BIT(5)
#define AW2013_LCFG_FO BIT(6)
@@ -51,6 +53,16 @@
#define AW2013_TIME_STEP 130 /* ms */
+/* AW2027-specific defines */
+#define AW2016_23_27_33_CHIPID 0x09 /* vendor shared same chipid between several chips */
+#define AW2027_REG_MAX 0x7F /* copied from vendor driver, but only up to 0x3F is documented */
+#define AW2027_LOCAL_IMAX_MASK GENMASK(3, 0)
+
+#define AW2027_GLOBAL_IMAX_15MA 0
+#define AW2027_GLOBAL_IMAX_30MA 1
+#define AW2027_GLOBAL_IMAX_5MA 2
+#define AW2027_GLOBAL_IMAX_10MA 3
+
struct aw2013;
struct aw2013_led {@@ -60,23 +72,87 @@ struct aw2013_led {
unsigned int imax;
};
+struct aw2013_chipdef {
+ u8 chip_id;
+ const struct regmap_config *regmap_cfg;
+ u32 default_imax;
+ bool gcr2_imax;
+ unsigned int current_levels;
+ unsigned int current_max;
+};
+
struct aw2013 {
struct mutex mutex; /* held when writing to registers */
struct regulator_bulk_data regulators[2];
struct i2c_client *client;
struct aw2013_led leds[AW2013_MAX_LEDS];
struct regmap *regmap;
+ const struct aw2013_chipdef *cdef;
int num_leds;
bool enabled;
};
-static int aw2013_chip_init(struct aw2013 *chip)
+static const struct regmap_config aw2013_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AW2013_REG_MAX,
+};
+
+static const struct regmap_config aw2027_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = AW2027_REG_MAX,
+};
+
+static const struct aw2013_chipdef aw2013_chipdef = {
+ .chip_id = AW2013_RSTR_CHIP_ID,
+ .regmap_cfg = &aw2013_regmap_config,
+ .default_imax = 1, // 5mA
+ .gcr2_imax = false,
+ .current_levels = 4,
+ .current_max = 15000,
+};
+
+static const struct aw2013_chipdef aw2027_chipdef = {
+ .chip_id = AW2016_23_27_33_CHIPID,
+ .regmap_cfg = &aw2027_regmap_config,
+ .default_imax = 3, // 6mA
+ .gcr2_imax = true,
+ .current_levels = 16,
+ .current_max = 30000,
+};
+
+/**
+ * aw2013_microamp_to_imax() - Finds the closest current step to the given microamp
+ * @microamp: Desired current in microamps
+ * @cdef: Chip definition
+ *
+ * Return: The closest current-step to the given microamp
+ */
+static inline u32 aw2013_microamp_to_imax(u32 microamp, const struct aw2013_chipdef *cdef)
+{
+ return min_t(u32, (microamp * (cdef->current_levels - 1))
+ / cdef->current_max,
+ cdef->current_levels - 1);
+}
+
+static int aw2027_set_imax(struct aw2013 *chip)
{
int i, ret;
- ret = regmap_write(chip->regmap, AW2013_GCR, AW2013_GCR_ENABLE);
+ /*
+ * AW2027 supports 4-step global imax, and also a 16-step control for limiting
+ * individual current per LED. This doesn't map to the single value the
+ * devicetree provides super well, so set global imax to maximum, and
+ * local imax to whatever gets us closest to the value in the devicetree.
+ */
+ ret = regmap_update_bits(chip->regmap,
+ AW20XX_GCR2,
+ AW2013_IMAX_MASK,
+ AW2027_GLOBAL_IMAX_30MA);
if (ret) {
- dev_err(&chip->client->dev, "Failed to enable the chip: %d\n",
+ dev_err(&chip->client->dev,
+ "Failed to set maximum global current: %d\n",
ret);
return ret;
}@@ -84,7 +160,7 @@ static int aw2013_chip_init(struct aw2013 *chip)
for (i = 0; i < chip->num_leds; i++) {
ret = regmap_update_bits(chip->regmap,
AW2013_LCFG(chip->leds[i].num),
- AW2013_LCFG_IMAX_MASK,
+ AW2027_LOCAL_IMAX_MASK,
chip->leds[i].imax);
if (ret) {
dev_err(&chip->client->dev,@@ -97,6 +173,38 @@ static int aw2013_chip_init(struct aw2013 *chip)
return ret;
}
+static int aw2013_chip_init(struct aw2013 *chip)
+{
+ int i, ret;
+
+ ret = regmap_write(chip->regmap, AW2013_GCR, AW2013_GCR_ENABLE);
+ if (ret) {
+ dev_err(&chip->client->dev, "Failed to enable the chip: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (chip->cdef->gcr2_imax) {
+ ret = aw2027_set_imax(chip);
+ } else {
+ /* AW2013 only supports 4-step individual current per LED */
+ for (i = 0; i < chip->num_leds; i++) {
+ ret = regmap_update_bits(chip->regmap,
+ AW2013_LCFG(chip->leds[i].num),
+ AW2013_IMAX_MASK,
+ chip->leds[i].imax);
+ if (ret) {
+ dev_err(&chip->client->dev,
+ "Failed to set maximum current for led %d: %d\n",
+ chip->leds[i].num, ret);
+ return ret;
+ }
+ }
+ }
+
+ return ret;
+}
+
static void aw2013_chip_disable(struct aw2013 *chip)
{
int ret;@@ -292,9 +400,9 @@ static int aw2013_probe_dt(struct aw2013 *chip)
init_data.fwnode = of_fwnode_handle(child);
if (!of_property_read_u32(child, "led-max-microamp", &imax)) {
- led->imax = min_t(u32, imax / 5000, 3);
+ led->imax = aw2013_microamp_to_imax(imax, chip->cdef);
} else {
- led->imax = 1; // 5mA
+ led->imax = chip->cdef->default_imax;
dev_info(&chip->client->dev,
"DT property led-max-microamp is missing\n");
}@@ -323,18 +431,18 @@ static void aw2013_chip_disable_action(void *data)
aw2013_chip_disable(data);
}
-static const struct regmap_config aw2013_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = AW2013_REG_MAX,
-};
static int aw2013_probe(struct i2c_client *client)
{
struct aw2013 *chip;
+ const struct aw2013_chipdef *cdef;
int ret;
unsigned int chipid;
+ cdef = device_get_match_data(&client->dev);
+ if (!cdef)
+ return -ENODEV;
+
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;@@ -346,9 +454,10 @@ static int aw2013_probe(struct i2c_client *client)
mutex_lock(&chip->mutex);
chip->client = client;
+ chip->cdef = cdef;
i2c_set_clientdata(client, chip);
- chip->regmap = devm_regmap_init_i2c(client, &aw2013_regmap_config);
+ chip->regmap = devm_regmap_init_i2c(client, chip->cdef->regmap_cfg);
if (IS_ERR(chip->regmap)) {
ret = PTR_ERR(chip->regmap);
dev_err(&client->dev, "Failed to allocate register map: %d\n",@@ -382,8 +491,7 @@ static int aw2013_probe(struct i2c_client *client)
ret);
goto error_reg;
}
-
- if (chipid != AW2013_RSTR_CHIP_ID) {
+ if (chipid != chip->cdef->chip_id) {
dev_err(&client->dev, "Chip reported wrong ID: %x\n",
chipid);
ret = -ENODEV;@@ -420,7 +528,8 @@ static int aw2013_probe(struct i2c_client *client)
}
static const struct of_device_id aw2013_match_table[] = {
- { .compatible = "awinic,aw2013", },
+ { .compatible = "awinic,aw2013", .data = &aw2013_chipdef },
+ { .compatible = "awinic,aw2027", .data = &aw2027_chipdef },
{ /* sentinel */ },
};
@@ -437,5 +546,5 @@ static struct i2c_driver aw2013_driver = {
module_i2c_driver(aw2013_driver);
MODULE_AUTHOR("Nikita Travkin <nikitos.tr@gmail.com>");
-MODULE_DESCRIPTION("AW2013 LED driver");
+MODULE_DESCRIPTION("AW2013/AW2027 LED driver");
MODULE_LICENSE("GPL v2");
--
2.56.0