1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright 2020 Samsung Electronics Co., Ltd. 4 * Copyright 2020 Google LLC. 5 * Copyright 2024 Linaro Ltd. 6 */ 7 #include <linux/bitfield.h> 8 #include <linux/firmware/samsung/exynos-acpm-protocol.h> 9 #include <linux/ktime.h> 10 #include <linux/types.h> 11 12 #include "exynos-acpm.h" 13 #include "exynos-acpm-pmic.h" 14 15 #define ACPM_PMIC_CHANNEL GENMASK(15, 12) 16 #define ACPM_PMIC_TYPE GENMASK(11, 8) 17 #define ACPM_PMIC_REG GENMASK(7, 0) 18 19 #define ACPM_PMIC_RETURN GENMASK(31, 24) 20 #define ACPM_PMIC_MASK GENMASK(23, 16) 21 #define ACPM_PMIC_VALUE GENMASK(15, 8) 22 #define ACPM_PMIC_FUNC GENMASK(7, 0) 23 24 #define ACPM_PMIC_BULK_SHIFT 8 25 #define ACPM_PMIC_BULK_MASK GENMASK(7, 0) 26 #define ACPM_PMIC_BULK_MAX_COUNT 8 27 28 enum exynos_acpm_pmic_func { 29 ACPM_PMIC_READ, 30 ACPM_PMIC_WRITE, 31 ACPM_PMIC_UPDATE, 32 ACPM_PMIC_BULK_READ, 33 ACPM_PMIC_BULK_WRITE, 34 }; 35 36 static inline u32 acpm_pmic_set_bulk(u32 data, unsigned int i) 37 { 38 return (data & ACPM_PMIC_BULK_MASK) << (ACPM_PMIC_BULK_SHIFT * i); 39 } 40 41 static inline u32 acpm_pmic_get_bulk(u32 data, unsigned int i) 42 { 43 return (data >> (ACPM_PMIC_BULK_SHIFT * i)) & ACPM_PMIC_BULK_MASK; 44 } 45 46 static void acpm_pmic_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen, 47 unsigned int acpm_chan_id) 48 { 49 xfer->txd = cmd; 50 xfer->rxd = cmd; 51 xfer->txlen = cmdlen; 52 xfer->rxlen = cmdlen; 53 xfer->acpm_chan_id = acpm_chan_id; 54 } 55 56 static void acpm_pmic_init_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan) 57 { 58 cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) | 59 FIELD_PREP(ACPM_PMIC_REG, reg) | 60 FIELD_PREP(ACPM_PMIC_CHANNEL, chan); 61 cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_READ); 62 cmd[3] = ktime_to_ms(ktime_get()); 63 } 64 65 int acpm_pmic_read_reg(const struct acpm_handle *handle, 66 unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, 67 u8 *buf) 68 { 69 struct acpm_xfer xfer; 70 u32 cmd[4] = {0}; 71 int ret; 72 73 acpm_pmic_init_read_cmd(cmd, type, reg, chan); 74 acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); 75 76 ret = acpm_do_xfer(handle, &xfer); 77 if (ret) 78 return ret; 79 80 *buf = FIELD_GET(ACPM_PMIC_VALUE, xfer.rxd[1]); 81 82 return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); 83 } 84 85 static void acpm_pmic_init_bulk_read_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, 86 u8 count) 87 { 88 cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) | 89 FIELD_PREP(ACPM_PMIC_REG, reg) | 90 FIELD_PREP(ACPM_PMIC_CHANNEL, chan); 91 cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_BULK_READ) | 92 FIELD_PREP(ACPM_PMIC_VALUE, count); 93 } 94 95 int acpm_pmic_bulk_read(const struct acpm_handle *handle, 96 unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, 97 u8 count, u8 *buf) 98 { 99 struct acpm_xfer xfer; 100 u32 cmd[4] = {0}; 101 int i, ret; 102 103 if (count > ACPM_PMIC_BULK_MAX_COUNT) 104 return -EINVAL; 105 106 acpm_pmic_init_bulk_read_cmd(cmd, type, reg, chan, count); 107 acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); 108 109 ret = acpm_do_xfer(handle, &xfer); 110 if (ret) 111 return ret; 112 113 ret = FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); 114 if (ret) 115 return ret; 116 117 for (i = 0; i < count; i++) { 118 if (i < 4) 119 buf[i] = acpm_pmic_get_bulk(xfer.rxd[2], i); 120 else 121 buf[i] = acpm_pmic_get_bulk(xfer.rxd[3], i - 4); 122 } 123 124 return 0; 125 } 126 127 static void acpm_pmic_init_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, 128 u8 value) 129 { 130 cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) | 131 FIELD_PREP(ACPM_PMIC_REG, reg) | 132 FIELD_PREP(ACPM_PMIC_CHANNEL, chan); 133 cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_WRITE) | 134 FIELD_PREP(ACPM_PMIC_VALUE, value); 135 cmd[3] = ktime_to_ms(ktime_get()); 136 } 137 138 int acpm_pmic_write_reg(const struct acpm_handle *handle, 139 unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, 140 u8 value) 141 { 142 struct acpm_xfer xfer; 143 u32 cmd[4] = {0}; 144 int ret; 145 146 acpm_pmic_init_write_cmd(cmd, type, reg, chan, value); 147 acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); 148 149 ret = acpm_do_xfer(handle, &xfer); 150 if (ret) 151 return ret; 152 153 return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); 154 } 155 156 static void acpm_pmic_init_bulk_write_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, 157 u8 count, const u8 *buf) 158 { 159 int i; 160 161 cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) | 162 FIELD_PREP(ACPM_PMIC_REG, reg) | 163 FIELD_PREP(ACPM_PMIC_CHANNEL, chan); 164 cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_BULK_WRITE) | 165 FIELD_PREP(ACPM_PMIC_VALUE, count); 166 167 for (i = 0; i < count; i++) { 168 if (i < 4) 169 cmd[2] |= acpm_pmic_set_bulk(buf[i], i); 170 else 171 cmd[3] |= acpm_pmic_set_bulk(buf[i], i - 4); 172 } 173 } 174 175 int acpm_pmic_bulk_write(const struct acpm_handle *handle, 176 unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, 177 u8 count, const u8 *buf) 178 { 179 struct acpm_xfer xfer; 180 u32 cmd[4] = {0}; 181 int ret; 182 183 if (count > ACPM_PMIC_BULK_MAX_COUNT) 184 return -EINVAL; 185 186 acpm_pmic_init_bulk_write_cmd(cmd, type, reg, chan, count, buf); 187 acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); 188 189 ret = acpm_do_xfer(handle, &xfer); 190 if (ret) 191 return ret; 192 193 return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); 194 } 195 196 static void acpm_pmic_init_update_cmd(u32 cmd[4], u8 type, u8 reg, u8 chan, 197 u8 value, u8 mask) 198 { 199 cmd[0] = FIELD_PREP(ACPM_PMIC_TYPE, type) | 200 FIELD_PREP(ACPM_PMIC_REG, reg) | 201 FIELD_PREP(ACPM_PMIC_CHANNEL, chan); 202 cmd[1] = FIELD_PREP(ACPM_PMIC_FUNC, ACPM_PMIC_UPDATE) | 203 FIELD_PREP(ACPM_PMIC_VALUE, value) | 204 FIELD_PREP(ACPM_PMIC_MASK, mask); 205 cmd[3] = ktime_to_ms(ktime_get()); 206 } 207 208 int acpm_pmic_update_reg(const struct acpm_handle *handle, 209 unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, 210 u8 value, u8 mask) 211 { 212 struct acpm_xfer xfer; 213 u32 cmd[4] = {0}; 214 int ret; 215 216 acpm_pmic_init_update_cmd(cmd, type, reg, chan, value, mask); 217 acpm_pmic_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id); 218 219 ret = acpm_do_xfer(handle, &xfer); 220 if (ret) 221 return ret; 222 223 return FIELD_GET(ACPM_PMIC_RETURN, xfer.rxd[1]); 224 } 225