Skip to content

Commit ea08204

Browse files
dgerlachnmenon
authored andcommitted
soc: ti: wkup_m3_ipc: Add support for i2c voltage scaling
Allow loading of a binary containing i2c scaling sequences to be provided to the wkup_m3 firmware in order to properly scale voltage rails on the PMIC during low power modes like DeepSleep0. Proper binary format is determined by the FW in use. Code expects firmware to have 0x0C57 present as the first two bytes followed by one byte defining offset to sleep sequence followed by one byte defining offset to wake sequence and then lastly both sequences. Each sequence is a series of I2C transfers in the form: u8 length | u8 chip address | u8 byte0/reg address | u8 byte1 | u8 byteN .. The length indicates the number of bytes to transfer, including the register address. The length of each transfer is limited by the I2C buffer size of 32 bytes. Based on previous work by Russ Dill. [dfustini: replace FW_ACTION_HOTPLUG with FW_ACTION_UEVENT] Signed-off-by: Dave Gerlach <d-gerlach@ti.com> Signed-off-by: Keerthy <j-keerthy@ti.com> [dfustini: add NULL argument to rproc_da_to_va() call] Signed-off-by: Drew Fustini <dfustini@baylibre.com> Signed-off-by: Nishanth Menon <nm@ti.com> Link: https://lore.kernel.org/r/20220426200741.712842-3-dfustini@baylibre.com
1 parent 1dcbae8 commit ea08204

2 files changed

Lines changed: 101 additions & 1 deletion

File tree

drivers/soc/ti/wkup_m3_ipc.c

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
#include <linux/err.h>
11+
#include <linux/firmware.h>
1112
#include <linux/kernel.h>
1213
#include <linux/kthread.h>
1314
#include <linux/interrupt.h>
@@ -55,6 +56,12 @@
5556
#define M3_STATE_MSG_FOR_LP 3
5657
#define M3_STATE_MSG_FOR_RESET 4
5758

59+
#define WKUP_M3_SD_FW_MAGIC 0x570C
60+
61+
#define WKUP_M3_DMEM_START 0x80000
62+
#define WKUP_M3_AUXDATA_OFFSET 0x1000
63+
#define WKUP_M3_AUXDATA_SIZE 0xFF
64+
5865
static struct wkup_m3_ipc *m3_ipc_state;
5966

6067
static const struct wkup_m3_wakeup_src wakeups[] = {
@@ -75,6 +82,81 @@ static const struct wkup_m3_wakeup_src wakeups[] = {
7582
{.irq_nr = 0, .src = "Unknown"},
7683
};
7784

85+
/**
86+
* wkup_m3_copy_aux_data - Copy auxiliary data to special region of m3 dmem
87+
* @data - pointer to data
88+
* @sz - size of data to copy (limit 256 bytes)
89+
*
90+
* Copies any additional blob of data to the wkup_m3 dmem to be used by the
91+
* firmware
92+
*/
93+
static unsigned long wkup_m3_copy_aux_data(struct wkup_m3_ipc *m3_ipc,
94+
const void *data, int sz)
95+
{
96+
unsigned long aux_data_dev_addr;
97+
void *aux_data_addr;
98+
99+
aux_data_dev_addr = WKUP_M3_DMEM_START + WKUP_M3_AUXDATA_OFFSET;
100+
aux_data_addr = rproc_da_to_va(m3_ipc->rproc,
101+
aux_data_dev_addr,
102+
WKUP_M3_AUXDATA_SIZE,
103+
NULL);
104+
memcpy(aux_data_addr, data, sz);
105+
106+
return WKUP_M3_AUXDATA_OFFSET;
107+
}
108+
109+
static void wkup_m3_scale_data_fw_cb(const struct firmware *fw, void *context)
110+
{
111+
unsigned long val, aux_base;
112+
struct wkup_m3_scale_data_header hdr;
113+
struct wkup_m3_ipc *m3_ipc = context;
114+
struct device *dev = m3_ipc->dev;
115+
116+
if (!fw) {
117+
dev_err(dev, "Voltage scale fw name given but file missing.\n");
118+
return;
119+
}
120+
121+
memcpy(&hdr, fw->data, sizeof(hdr));
122+
123+
if (hdr.magic != WKUP_M3_SD_FW_MAGIC) {
124+
dev_err(dev, "PM: Voltage Scale Data binary does not appear valid.\n");
125+
goto release_sd_fw;
126+
}
127+
128+
aux_base = wkup_m3_copy_aux_data(m3_ipc, fw->data + sizeof(hdr),
129+
fw->size - sizeof(hdr));
130+
131+
val = (aux_base + hdr.sleep_offset);
132+
val |= ((aux_base + hdr.wake_offset) << 16);
133+
134+
m3_ipc->volt_scale_offsets = val;
135+
136+
release_sd_fw:
137+
release_firmware(fw);
138+
};
139+
140+
static int wkup_m3_init_scale_data(struct wkup_m3_ipc *m3_ipc,
141+
struct device *dev)
142+
{
143+
int ret = 0;
144+
145+
/*
146+
* If no name is provided, user has already been warned, pm will
147+
* still work so return 0
148+
*/
149+
150+
if (!m3_ipc->sd_fw_name)
151+
return ret;
152+
153+
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
154+
m3_ipc->sd_fw_name, dev, GFP_ATOMIC,
155+
m3_ipc, wkup_m3_scale_data_fw_cb);
156+
157+
return ret;
158+
}
159+
78160
static void am33xx_txev_eoi(struct wkup_m3_ipc *m3_ipc)
79161
{
80162
writel(AM33XX_M3_TXEV_ACK,
@@ -139,6 +221,7 @@ static irqreturn_t wkup_m3_txev_handler(int irq, void *ipc_data)
139221
}
140222

141223
m3_ipc->state = M3_STATE_INITED;
224+
wkup_m3_init_scale_data(m3_ipc, dev);
142225
complete(&m3_ipc->sync_complete);
143226
break;
144227
case M3_STATE_MSG_FOR_RESET:
@@ -300,12 +383,15 @@ static int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state)
300383
switch (state) {
301384
case WKUP_M3_DEEPSLEEP:
302385
m3_power_state = IPC_CMD_DS0;
386+
wkup_m3_ctrl_ipc_write(m3_ipc, m3_ipc->volt_scale_offsets, 5);
303387
break;
304388
case WKUP_M3_STANDBY:
305389
m3_power_state = IPC_CMD_STANDBY;
390+
wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5);
306391
break;
307392
case WKUP_M3_IDLE:
308393
m3_power_state = IPC_CMD_IDLE;
394+
wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5);
309395
break;
310396
default:
311397
return 1;
@@ -319,7 +405,6 @@ static int wkup_m3_prepare_low_power(struct wkup_m3_ipc *m3_ipc, int state)
319405
m3_ipc->isolation_conf, 4);
320406
wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 2);
321407
wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 3);
322-
wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 5);
323408
wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 6);
324409
wkup_m3_ctrl_ipc_write(m3_ipc, DS_IPC_DEFAULT, 7);
325410

@@ -526,6 +611,12 @@ static int wkup_m3_ipc_probe(struct platform_device *pdev)
526611
if (of_find_property(np, "ti,set-io-isolation", NULL))
527612
wkup_m3_set_io_isolation(m3_ipc);
528613

614+
ret = of_property_read_string(np, "firmware-name",
615+
&m3_ipc->sd_fw_name);
616+
if (ret) {
617+
dev_dbg(dev, "Voltage scaling data blob not provided from DT.\n");
618+
};
619+
529620
/*
530621
* Wait for firmware loading completion in a thread so we
531622
* can boot the wkup_m3 as soon as it's ready without holding

include/linux/wkup_m3_ipc.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ struct wkup_m3_ipc {
3737
int isolation_conf;
3838
int state;
3939

40+
unsigned long volt_scale_offsets;
41+
const char *sd_fw_name;
42+
4043
struct completion sync_complete;
4144
struct mbox_client mbox_client;
4245
struct mbox_chan *mbox;
@@ -50,6 +53,12 @@ struct wkup_m3_wakeup_src {
5053
char src[10];
5154
};
5255

56+
struct wkup_m3_scale_data_header {
57+
u16 magic;
58+
u8 sleep_offset;
59+
u8 wake_offset;
60+
} __packed;
61+
5362
struct wkup_m3_ipc_ops {
5463
void (*set_mem_type)(struct wkup_m3_ipc *m3_ipc, int mem_type);
5564
void (*set_resume_address)(struct wkup_m3_ipc *m3_ipc, void *addr);

0 commit comments

Comments
 (0)