// SPDX-License-Identifier: GPL-2.0-only
/*
* LP5521/LP5523/LP55231/LP5562 Common Driver
*
* Copyright 2012 Texas Instruments
*
* Author: Milo(Woogyom) Kim <milo.kim@ti.com>
*
* Derived from leds-lp5521.c, leds-lp5523.c
*/
#include <linux/bitfield.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/i2c.h>
#include <linux/iopoll.h>
#include <linux/leds.h>
#include <linux/module.h>
#include <linux/platform_data/leds-lp55xx.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
#include <dt-bindings/leds/leds-lp55xx.h>
#include "leds-lp55xx-common.h"
/* OP MODE require at least 153 us to clear regs */
#define LP55XX_CMD_SLEEP 200
#define LP55xx_PROGRAM_PAGES 16
#define LP55xx_MAX_PROGRAM_LENGTH (LP55xx_BYTES_PER_PAGE * 4) /* 128 bytes (4 pages) */
/*
* Program Memory Operations
* Same Mask for each engine for both mode and exec
* ENG1 GENMASK(3, 2)
* ENG2 GENMASK(5, 4)
* ENG3 GENMASK(7, 6)
*/
#define LP55xx_MODE_DISABLE_ALL_ENG 0x0
#define LP55xx_MODE_ENG_MASK GENMASK(1, 0)
#define LP55xx_MODE_DISABLE_ENG FIELD_PREP_CONST(LP55xx_MODE_ENG_MASK, 0x0)
#define LP55xx_MODE_LOAD_ENG FIELD_PREP_CONST(LP55xx_MODE_ENG_MASK, 0x1)
#define LP55xx_MODE_RUN_ENG FIELD_PREP_CONST(LP55xx_MODE_ENG_MASK, 0x2)
#define LP55xx_MODE_HALT_ENG FIELD_PREP_CONST(LP55xx_MODE_ENG_MASK, 0x3)
#define LP55xx_MODE_ENGn_SHIFT(n, shift) ((shift) + (2 * (3 - (n))))
#define LP55xx_MODE_ENGn_MASK(n, shift) (LP55xx_MODE_ENG_MASK << LP55xx_MODE_ENGn_SHIFT(n, shift))
#define LP55xx_MODE_ENGn_GET(n, mode, shift) \
(((mode) >> LP55xx_MODE_ENGn_SHIFT(n, shift)) & LP55xx_MODE_ENG_MASK)
#define LP55xx_EXEC_ENG_MASK GENMASK(1, 0)
#define LP55xx_EXEC_HOLD_ENG FIELD_PREP_CONST(LP55xx_EXEC_ENG_MASK, 0x0)
#define LP55xx_EXEC_STEP_ENG FIELD_PREP_CONST(LP55xx_EXEC_ENG_MASK, 0x1)
#define LP55xx_EXEC_RUN_ENG FIELD_PREP_CONST(LP55xx_EXEC_ENG_MASK, 0x2)
#define LP55xx_EXEC_ONCE_ENG FIELD_PREP_CONST(LP55xx_EXEC_ENG_MASK, 0x3)
#define LP55xx_EXEC_ENGn_SHIFT(n, shift) ((shift) + (2 * (3 - (n))))
#define LP55xx_EXEC_ENGn_MASK(n, shift) (LP55xx_EXEC_ENG_MASK << LP55xx_EXEC_ENGn_SHIFT(n, shift))
/* Memory Page Selection */
#define LP55xx_REG_PROG_PAGE_SEL 0x4f
/* If supported, each ENGINE have an equal amount of pages offset from page 0 */
#define LP55xx_PAGE_OFFSET(n, pages) (((n) - 1) * (pages))
#define LED_ACTIVE(mux, led) (!!((mux) & (0x0001 << (led))))
/* MASTER FADER common property */
#define LP55xx_FADER_MAPPING_MASK GENMASK(7, 6)
/* External clock rate */
#define LP55XX_CLK_32K 32768
static struct lp55xx_led *cdev_to_lp55xx_led(struct led_classdev *cdev)
{
return container_of(cdev, struct lp55xx_led, cdev);
}
static struct lp55xx_led *dev_to_lp55xx_led(struct device *dev)
{
return cdev_to_lp55xx_led(dev_get_drvdata(dev));
}
static struct lp55xx_led *mcled_cdev_to_led(struct led_classdev_mc *mc_cdev)
{
return container_of(mc_cdev, struct lp55xx_led, mc_cdev);
}
static void lp55xx_wait_opmode_done(struct lp55xx_chip *chip)
{
const struct lp55xx_device_config *cfg = chip->cfg;
int __always_unused ret;
u8 val;
/*
* Recent chip supports BUSY bit for engine.
* Check support by checking if val is not 0.
* For legacy device, sleep at least 153 us.
*/
if (cfg->engine_busy.val) {
read_poll_timeout(lp55xx_read, ret, !(val & cfg->engine_busy.mask),
LP55XX_CMD_SLEEP, LP55XX_CMD_SLEEP * 10, false,
chip, cfg->engine_busy.addr, &val);
} else {
usleep_range(LP55XX_CMD_SLEEP, LP55XX_CMD_SLEEP * 2);
}
}
void lp55xx_stop_all_engine(struct lp55xx_chip *chip)
{
const struct lp55xx_device_config *cfg = chip->cfg;
lp55xx_write(