Path: blob/master/drivers/firmware/samsung/exynos-acpm-pmic.c
29267 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/array_size.h>7#include <linux/bitfield.h>8#include <linux/errno.h>9#include <linux/firmware/samsung/exynos-acpm-protocol.h>10#include <linux/ktime.h>11#include <linux/types.h>1213#include "exynos-acpm.h"14#include "exynos-acpm-pmic.h"1516#define ACPM_PMIC_CHANNEL GENMASK(15, 12)17#define ACPM_PMIC_TYPE GENMASK(11, 8)18#define ACPM_PMIC_REG GENMASK(7, 0)1920#define ACPM_PMIC_RETURN GENMASK(31, 24)21#define ACPM_PMIC_MASK GENMASK(23, 16)22#define ACPM_PMIC_VALUE GENMASK(15, 8)23#define ACPM_PMIC_FUNC GENMASK(7, 0)2425#define ACPM_PMIC_BULK_SHIFT 826#define ACPM_PMIC_BULK_MASK GENMASK(7, 0)27#define ACPM_PMIC_BULK_MAX_COUNT 82829enum exynos_acpm_pmic_func {30ACPM_PMIC_READ,31ACPM_PMIC_WRITE,32ACPM_PMIC_UPDATE,33ACPM_PMIC_BULK_READ,34ACPM_PMIC_BULK_WRITE,35};3637static const int acpm_pmic_linux_errmap[] = {38[0] = 0, /* ACPM_PMIC_SUCCESS */39[1] = -EACCES, /* Read register can't be accessed or issues to access it. */40[2] = -EACCES, /* Write register can't be accessed or issues to access it. */41};4243static int acpm_pmic_to_linux_err(int err)44{45if (err >= 0 && err < ARRAY_SIZE(acpm_pmic_linux_errmap))46return acpm_pmic_linux_errmap[err];47return -EIO;48}4950static inline u32 acpm_pmic_set_bulk(u32 data, unsigned int i)51{52return (data & ACPM_PMIC_BULK_MASK) << (ACPM_PMIC_BULK_SHIFT * i);53}5455static inline u32 acpm_pmic_get_bulk(u32 data, unsigned int i)56{57return (data >> (ACPM_PMIC_BULK_SHIFT * i)) & ACPM_PMIC_BULK_MASK;58}5960static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen,61unsigned int acpm_chan_id)62{63xfer->txd = cmd;64xfer->rxd = cmd;65xfer->txlen = cmdlen;66xfer->rxlen = cmdlen;67xfer->acpm_chan_id = acpm_chan_id;68}6970static void acpm_pmic_init_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan)71{72cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |73FIELD_PREP(ACPM_PMIC_REG, reg) |74FIELD_PREP(ACPM_PMIC_CHANNEL, chan);75cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_READ);76cmd[3] = ktime_to_ms(ktime_get());77}7879int acpm_pmic_read_reg(const struct acpm_handle *handle,80unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,81u8 *buf)82{83struct acpm_xfer xfer;84u32 cmd[4] = {0};85int ret;8687acpm_pmic_init_read_cmd(cmd, type, reg, chan);88acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);8990ret = acpm_do_xfer(handle, &xfer);91if (ret)92return ret;9394*buf = FIELD_GET(ACPM_PMIC_VALUE, xfer.rxd[1]);9596return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]));97}9899static void acpm_pmic_init_bulk_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan,100u8 count)101{102cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |103FIELD_PREP(ACPM_PMIC_REG, reg) |104FIELD_PREP(ACPM_PMIC_CHANNEL, chan);105cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_BULK_READ) |106FIELD_PREP(ACPM_PMIC_VALUE, count);107}108109int acpm_pmic_bulk_read(const struct acpm_handle *handle,110unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,111u8 count, u8 *buf)112{113struct acpm_xfer xfer;114u32 cmd[4] = {0};115int i, ret;116117if (count > ACPM_PMIC_BULK_MAX_COUNT)118return -EINVAL;119120acpm_pmic_init_bulk_read_cmd(cmd, type, reg, chan, count);121acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);122123ret = acpm_do_xfer(handle, &xfer);124if (ret)125return ret;126127ret = acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]));128if (ret)129return ret;130131for (i = 0; i < count; i++) {132if (i < 4)133buf[i] = acpm_pmic_get_bulk(xfer.rxd[2], i);134else135buf[i] = acpm_pmic_get_bulk(xfer.rxd[3], i - 4);136}137138return 0;139}140141static void acpm_pmic_init_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan,142u8 value)143{144cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |145FIELD_PREP(ACPM_PMIC_REG, reg) |146FIELD_PREP(ACPM_PMIC_CHANNEL, chan);147cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_WRITE) |148FIELD_PREP(ACPM_PMIC_VALUE, value);149cmd[3] = ktime_to_ms(ktime_get());150}151152int acpm_pmic_write_reg(const struct acpm_handle *handle,153unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,154u8 value)155{156struct acpm_xfer xfer;157u32 cmd[4] = {0};158int ret;159160acpm_pmic_init_write_cmd(cmd, type, reg, chan, value);161acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);162163ret = acpm_do_xfer(handle, &xfer);164if (ret)165return ret;166167return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]));168}169170static void acpm_pmic_init_bulk_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan,171u8 count, const u8 *buf)172{173int i;174175cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |176FIELD_PREP(ACPM_PMIC_REG, reg) |177FIELD_PREP(ACPM_PMIC_CHANNEL, chan);178cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_BULK_WRITE) |179FIELD_PREP(ACPM_PMIC_VALUE, count);180181for (i = 0; i < count; i++) {182if (i < 4)183cmd[2] |= acpm_pmic_set_bulk(buf[i], i);184else185cmd[3] |= acpm_pmic_set_bulk(buf[i], i - 4);186}187}188189int acpm_pmic_bulk_write(const struct acpm_handle *handle,190unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,191u8 count, const u8 *buf)192{193struct acpm_xfer xfer;194u32 cmd[4] = {0};195int ret;196197if (count > ACPM_PMIC_BULK_MAX_COUNT)198return -EINVAL;199200acpm_pmic_init_bulk_write_cmd(cmd, type, reg, chan, count, buf);201acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);202203ret = acpm_do_xfer(handle, &xfer);204if (ret)205return ret;206207return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]));208}209210static void acpm_pmic_init_update_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan,211u8 value, u8 mask)212{213cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) |214FIELD_PREP(ACPM_PMIC_REG, reg) |215FIELD_PREP(ACPM_PMIC_CHANNEL, chan);216cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_UPDATE) |217FIELD_PREP(ACPM_PMIC_VALUE, value) |218FIELD_PREP(ACPM_PMIC_MASK, mask);219cmd[3] = ktime_to_ms(ktime_get());220}221222int acpm_pmic_update_reg(const struct acpm_handle *handle,223unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan,224u8 value, u8 mask)225{226struct acpm_xfer xfer;227u32 cmd[4] = {0};228int ret;229230acpm_pmic_init_update_cmd(cmd, type, reg, chan, value, mask);231acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id);232233ret = acpm_do_xfer(handle, &xfer);234if (ret)235return ret;236237return acpm_pmic_to_linux_err(FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]));238}239240241