// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2020 Samsung Electronics Co., Ltd. * Copyright 2020 Google LLC. * Copyright 2024 Linaro Ltd. */ #include #include #include #include #include "exynos-acpm.h" #include "exynos-acpm-pmic.h" #define ACPM_PMIC_CHANNEL GENMASK(15, 12) #define ACPM_PMIC_TYPE GENMASK(11, 8) #define ACPM_PMIC_REG GENMASK(7, 0) #define ACPM_PMIC_RETURN GENMASK(31, 24) #define ACPM_PMIC_MASK GENMASK(23, 16) #define ACPM_PMIC_VALUE GENMASK(15, 8) #define ACPM_PMIC_FUNC GENMASK(7, 0) #define ACPM_PMIC_BULK_SHIFT 8 #define ACPM_PMIC_BULK_MASK GENMASK(7, 0) #define ACPM_PMIC_BULK_MAX_COUNT 8 enum exynos_acpm_pmic_func { ACPM_PMIC_READ, ACPM_PMIC_WRITE, ACPM_PMIC_UPDATE, ACPM_PMIC_BULK_READ, ACPM_PMIC_BULK_WRITE, }; static inline u32 acpm_pmic_set_bulk(u32 data, unsigned int i) { return (data & ACPM_PMIC_BULK_MASK) << (ACPM_PMIC_BULK_SHIFT * i); } static inline u32 acpm_pmic_get_bulk(u32 data, unsigned int i) { return (data >> (ACPM_PMIC_BULK_SHIFT * i)) & ACPM_PMIC_BULK_MASK; } static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd, unsigned int acpm_chan_id) { xfer->txd = cmd; xfer->rxd = cmd; xfer->txlen = sizeof(cmd); xfer->rxlen = sizeof(cmd); xfer->acpm_chan_id = acpm_chan_id; } static void acpm_pmic_init_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan) { cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) | FIELD_PREP(ACPM_PMIC_REG, reg) | FIELD_PREP(ACPM_PMIC_CHANNEL, chan); cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_READ); cmd[3] = ktime_to_ms(ktime_get()); } int acpm_pmic_read_reg(const struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 *buf) { struct acpm_xfer xfer; u32 cmd[4] = {0}; int ret; acpm_pmic_init_read_cmd(cmd, type, reg, chan); acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) return ret; *buf = FIELD_GET(ACPM_PMIC_VALUE, xfer.rxd[1]); return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); } static void acpm_pmic_init_bulk_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, u8 count) { cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) | FIELD_PREP(ACPM_PMIC_REG, reg) | FIELD_PREP(ACPM_PMIC_CHANNEL, chan); cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_BULK_READ) | FIELD_PREP(ACPM_PMIC_VALUE, count); } int acpm_pmic_bulk_read(const struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 count, u8 *buf) { struct acpm_xfer xfer; u32 cmd[4] = {0}; int i, ret; if (count > ACPM_PMIC_BULK_MAX_COUNT) return -EINVAL; acpm_pmic_init_bulk_read_cmd(cmd, type, reg, chan, count); acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) return ret; ret = FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); if (ret) return ret; for (i = 0; i < count; i++) { if (i < 4) buf[i] = acpm_pmic_get_bulk(xfer.rxd[2], i); else buf[i] = acpm_pmic_get_bulk(xfer.rxd[3], i - 4); } return 0; } static void acpm_pmic_init_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, u8 value) { cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) | FIELD_PREP(ACPM_PMIC_REG, reg) | FIELD_PREP(ACPM_PMIC_CHANNEL, chan); cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_WRITE) | FIELD_PREP(ACPM_PMIC_VALUE, value); cmd[3] = ktime_to_ms(ktime_get()); } int acpm_pmic_write_reg(const struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 value) { struct acpm_xfer xfer; u32 cmd[4] = {0}; int ret; acpm_pmic_init_write_cmd(cmd, type, reg, chan, value); acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) return ret; return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); } static void acpm_pmic_init_bulk_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, u8 count, const u8 *buf) { int i; cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) | FIELD_PREP(ACPM_PMIC_REG, reg) | FIELD_PREP(ACPM_PMIC_CHANNEL, chan); cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_BULK_WRITE) | FIELD_PREP(ACPM_PMIC_VALUE, count); for (i = 0; i < count; i++) { if (i < 4) cmd[2] |= acpm_pmic_set_bulk(buf[i], i); else cmd[3] |= acpm_pmic_set_bulk(buf[i], i - 4); } } int acpm_pmic_bulk_write(const struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 count, const u8 *buf) { struct acpm_xfer xfer; u32 cmd[4] = {0}; int ret; if (count > ACPM_PMIC_BULK_MAX_COUNT) return -EINVAL; acpm_pmic_init_bulk_write_cmd(cmd, type, reg, chan, count, buf); acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) return ret; return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); } static void acpm_pmic_init_update_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, u8 value, u8 mask) { cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) | FIELD_PREP(ACPM_PMIC_REG, reg) | FIELD_PREP(ACPM_PMIC_CHANNEL, chan); cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_UPDATE) | FIELD_PREP(ACPM_PMIC_VALUE, value) | FIELD_PREP(ACPM_PMIC_MASK, mask); cmd[3] = ktime_to_ms(ktime_get()); } int acpm_pmic_update_reg(const struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, u8 value, u8 mask) { struct acpm_xfer xfer; u32 cmd[4] = {0}; int ret; acpm_pmic_init_update_cmd(cmd, type, reg, chan, value, mask); acpm_pmic_set_xfer(&xfer, cmd, acpm_chan_id); ret = acpm_do_xfer(handle, &xfer); if (ret) return ret; return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); }