Path: blob/master/drivers/firmware/samsung/exynos-acpm-pmic.c
26427 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* Copyright 2020 Samsung Electronics Co., Ltd.3* Copyright 2020 Google LLC.4* Copyright 2024 Linaro Ltd.5*/6#include <linux/bitfield.h>7#include <linux/firmware/samsung/exynos-acpm-protocol.h>8#include <linux/ktime.h>9#include <linux/types.h>1011#include "exynos-acpm.h"12#include "exynos-acpm-pmic.h"1314#define ACPM_PMIC_CHANNEL GENMASK(15, 12)15#define ACPM_PMIC_TYPE GENMASK(11, 8)16#define ACPM_PMIC_REG GENMASK(7, 0)1718#define ACPM_PMIC_RETURN GENMASK(31, 24)19#define ACPM_PMIC_MASK GENMASK(23, 16)20#define ACPM_PMIC_VALUE GENMASK(15, 8)21#define ACPM_PMIC_FUNC GENMASK(7, 0)2223#define ACPM_PMIC_BULK_SHIFT 824#define ACPM_PMIC_BULK_MASK GENMASK(7, 0)25#define ACPM_PMIC_BULK_MAX_COUNT 82627enum exynos_acpm_pmic_func {28ACPM_PMIC_READ,29ACPM_PMIC_WRITE,30ACPM_PMIC_UPDATE,31ACPM_PMIC_BULK_READ,32ACPM_PMIC_BULK_WRITE,33};3435static inline u32 acpm_pmic_set_bulk(u32 data, unsigned int i)36{37return (data & ACPM_PMIC_BULK_MASK) << (ACPM_PMIC_BULK_SHIFT * i);38}3940static inline u32 acpm_pmic_get_bulk(u32 data, unsigned int i)41{42return (data >> (ACPM_PMIC_BULK_SHIFT * i)) & ACPM_PMIC_BULK_MASK;43}4445static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen,46unsigned int acpm_chan_id)47{48xfer->txd = cmd;49xfer->rxd = cmd;50xfer->txlen = cmdlen;51xfer->rxlen = cmdlen;52xfer->acpm_chan_id = acpm_chan_id;53}5455static void acpm_pmic_init_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan)56{57cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |58FIELD_PREP(ACPM_PMIC_REG, reg) |59FIELD_PREP(ACPM_PMIC_CHANNEL, chan);60cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_READ);61cmd[3] = ktime_to_ms(ktime_get());62}6364int acpm_pmic_read_reg(const struct acpm_handle *handle,65unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,66u8 *buf)67{68struct acpm_xfer xfer;69u32 cmd[4] = {0};70int ret;7172acpm_pmic_init_read_cmd(cmd, type, reg, chan);73acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);7475ret = acpm_do_xfer(handle, &xfer);76if (ret)77return ret;7879*buf = FIELD_GET(ACPM_PMIC_VALUE, xfer.rxd[1]);8081return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]);82}8384static void acpm_pmic_init_bulk_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan,85u8 count)86{87cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |88FIELD_PREP(ACPM_PMIC_REG, reg) |89FIELD_PREP(ACPM_PMIC_CHANNEL, chan);90cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_BULK_READ) |91FIELD_PREP(ACPM_PMIC_VALUE, count);92}9394int acpm_pmic_bulk_read(const struct acpm_handle *handle,95unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,96u8 count, u8 *buf)97{98struct acpm_xfer xfer;99u32 cmd[4] = {0};100int i, ret;101102if (count > ACPM_PMIC_BULK_MAX_COUNT)103return -EINVAL;104105acpm_pmic_init_bulk_read_cmd(cmd, type, reg, chan, count);106acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);107108ret = acpm_do_xfer(handle, &xfer);109if (ret)110return ret;111112ret = FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]);113if (ret)114return ret;115116for (i = 0; i < count; i++) {117if (i < 4)118buf[i] = acpm_pmic_get_bulk(xfer.rxd[2], i);119else120buf[i] = acpm_pmic_get_bulk(xfer.rxd[3], i - 4);121}122123return 0;124}125126static void acpm_pmic_init_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan,127u8 value)128{129cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |130FIELD_PREP(ACPM_PMIC_REG, reg) |131FIELD_PREP(ACPM_PMIC_CHANNEL, chan);132cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_WRITE) |133FIELD_PREP(ACPM_PMIC_VALUE, value);134cmd[3] = ktime_to_ms(ktime_get());135}136137int acpm_pmic_write_reg(const struct acpm_handle *handle,138unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,139u8 value)140{141struct acpm_xfer xfer;142u32 cmd[4] = {0};143int ret;144145acpm_pmic_init_write_cmd(cmd, type, reg, chan, value);146acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);147148ret = acpm_do_xfer(handle, &xfer);149if (ret)150return ret;151152return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]);153}154155static void acpm_pmic_init_bulk_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan,156u8 count, const u8 *buf)157{158int i;159160cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |161FIELD_PREP(ACPM_PMIC_REG, reg) |162FIELD_PREP(ACPM_PMIC_CHANNEL, chan);163cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_BULK_WRITE) |164FIELD_PREP(ACPM_PMIC_VALUE, count);165166for (i = 0; i < count; i++) {167if (i < 4)168cmd[2] |= acpm_pmic_set_bulk(buf[i], i);169else170cmd[3] |= acpm_pmic_set_bulk(buf[i], i - 4);171}172}173174int acpm_pmic_bulk_write(const struct acpm_handle *handle,175unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,176u8 count, const u8 *buf)177{178struct acpm_xfer xfer;179u32 cmd[4] = {0};180int ret;181182if (count > ACPM_PMIC_BULK_MAX_COUNT)183return -EINVAL;184185acpm_pmic_init_bulk_write_cmd(cmd, type, reg, chan, count, buf);186acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);187188ret = acpm_do_xfer(handle, &xfer);189if (ret)190return ret;191192return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]);193}194195static void acpm_pmic_init_update_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan,196u8 value, u8 mask)197{198cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |199FIELD_PREP(ACPM_PMIC_REG, reg) |200FIELD_PREP(ACPM_PMIC_CHANNEL, chan);201cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_UPDATE) |202FIELD_PREP(ACPM_PMIC_VALUE, value) |203FIELD_PREP(ACPM_PMIC_MASK, mask);204cmd[3] = ktime_to_ms(ktime_get());205}206207int acpm_pmic_update_reg(const struct acpm_handle *handle,208unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,209u8 value, u8 mask)210{211struct acpm_xfer xfer;212u32 cmd[4] = {0};213int ret;214215acpm_pmic_init_update_cmd(cmd, type, reg, chan, value, mask);216acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);217218ret = acpm_do_xfer(handle, &xfer);219if (ret)220return ret;221222return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]);223}224225226