blob: 77ff0852409844d9e88c88d319eb632f530f99ed [file] [log] [blame]
Cliff Huang7e653d82022-01-21 17:36:20 -08001/* SPDX-License-Identifier: GPL-2.0-or-later */
2
3#include <acpi/acpigen.h>
4#include <acpi/acpi_device.h>
Paweł Anikiel1a593902023-10-16 14:22:15 +00005#include <gpio.h>
6#include <stdio.h>
Cliff Huang7e653d82022-01-21 17:36:20 -08007#include "chip.h"
Cliff Huangca344f22023-03-02 10:08:17 -08008#include "soc/intel/common/block/include/intelblocks/acpi.h"
Cliff Huang7e653d82022-01-21 17:36:20 -08009#include "soc/intel/common/block/pcie/rtd3/chip.h"
10
11/* FCPO# to RESET# delay time during WWAN ON */
12#define FM350GL_TN2B 20
13/* RESET# to PERST# delay time during WWAN ON */
14#define FM350GL_TB2R 80
15/* The delay between de-assertion of PERST# to change of PDS state from 0 to 1 during WWAN ON */
16#define FM350GL_TR2P 0
17/* RESET# to FCPO# delay time during WWAN OFF */
18#define FM350GL_TB2F 10
19/* Time to allow the WWAN module to fully discharge any residual voltages before FCPO# could be
20 de-asserted again. */
21#define FM350GL_TFDI 500
22/* The delay between assertion and de-assertion RESET# during FLDR */
23#define FM350GL_TBTG 10
24/* The delay between de-assertion of RESET# and change of PDS state from 0 to 1 after FLDR */
25#define FM350GL_TBTP 170
26/* PERST# to RESET# delay time during WWAN OFF */
27#define FM350GL_TR2B 10
28/* 20s HW initialization needed after de-assertion of PERST#
29 However, it is not required and is not proper place to ensure HW initialization in ACPI. The
30 delay here is to ensure the following reset or RTD3 _OFF method won't be called immediately.
31 */
32#define FM350GL_TIME_HW_INIT 100
33
34enum reset_type {
35 RESET_TYPE_WARM = 0,
36 RESET_TYPE_COLD = 1
37};
38
39/*
40 * Returns the RTD3 PM methods requested and available to the device.
41 */
42static enum acpi_pcie_rp_pm_emit
43wwan_fm350gl_get_rtd3_method_support(const struct drivers_wwan_fm_config *config)
44{
45 const struct soc_intel_common_block_pcie_rtd3_config *rtd3_config;
46
47 rtd3_config = config_of(config->rtd3dev);
48
49 return rtd3_config->ext_pm_support;
50}
51
52/*
53 * Generate first half reset flow (FHRF) method.
Subrata Banikb5fc0c42022-12-13 15:23:38 +053054 * Arg0 = 0; RESET_TYPE_WARM: warm reset
55 * Arg0 = 1; RESET_TYPE_COLD: cold reset
Cliff Huang7e653d82022-01-21 17:36:20 -080056 */
57static void wwan_fm350gl_acpi_method_fhrf(const struct device *parent_dev,
58 const struct drivers_wwan_fm_config *config)
59{
60 acpigen_write_method_serialized("FHRF", 1);
61 {
Cliff Huangca344f22023-03-02 10:08:17 -080062 char mutex_path[128];
63 const struct soc_intel_common_block_pcie_rtd3_config *rtd3_config;
64
65 rtd3_config = config_of(config->rtd3dev);
66 if (rtd3_config->use_rp_mutex) {
Jamie Ryu6c0961a2023-06-08 10:41:11 -070067 snprintf(mutex_path, sizeof(mutex_path), "%s",
68 acpi_device_path_join(parent_dev, RP_MUTEX_NAME));
Cliff Huangca344f22023-03-02 10:08:17 -080069 /* Acquire root port mutex in case FHRF is called directly and not called from _RST */
70 acpigen_write_acquire(mutex_path, ACPI_MUTEX_NO_TIMEOUT);
71 }
72
Cliff Huang7e653d82022-01-21 17:36:20 -080073 /* LOCAL0 = PERST# */
74 acpigen_get_tx_gpio(&config->perst_gpio);
75 acpigen_write_if_lequal_op_int(LOCAL0_OP, 0);
76 {
Angel Pons92d44992022-02-13 13:37:04 +010077 if (wwan_fm350gl_get_rtd3_method_support(config) &
Cliff Huang7e653d82022-01-21 17:36:20 -080078 ACPI_PCIE_RP_EMIT_L23) {
79 acpigen_emit_namestring(acpi_device_path_join(parent_dev,
80 "DL23"));
81 }
82 /* assert PERST# pin */
83 acpigen_enable_tx_gpio(&config->perst_gpio);
84 }
85 acpigen_write_if_end(); /* If */
86 acpigen_write_sleep(FM350GL_TR2B);
87 /* assert RESET# pin */
88 acpigen_enable_tx_gpio(&config->reset_gpio);
89 /* warm reset */
90 acpigen_write_if_lequal_op_int(ARG0_OP, RESET_TYPE_WARM);
91 {
92 acpigen_write_sleep(FM350GL_TBTG);
93 }
94 /* cold reset */
95 acpigen_write_else();
96 {
97 acpigen_write_if_lequal_op_int(ARG0_OP, RESET_TYPE_COLD);
98 {
99 /* disable source clock */
Angel Pons92d44992022-02-13 13:37:04 +0100100 if (wwan_fm350gl_get_rtd3_method_support(config) &
Cliff Huang7e653d82022-01-21 17:36:20 -0800101 ACPI_PCIE_RP_EMIT_SRCK) {
102 acpigen_emit_namestring(acpi_device_path_join(
103 parent_dev, "SRCK"));
104 acpigen_emit_byte(ZERO_OP);
105 }
106 acpigen_write_sleep(FM350GL_TB2F);
107 /* assert FCPO# pin */
108 acpigen_enable_tx_gpio(&config->fcpo_gpio);
109 acpigen_write_sleep(FM350GL_TFDI);
110 }
111 acpigen_write_if_end(); /* If */
112 }
113 acpigen_pop_len(); /* Else */
Cliff Huangca344f22023-03-02 10:08:17 -0800114
115 if (rtd3_config->use_rp_mutex)
116 acpigen_write_release(mutex_path);
Cliff Huang7e653d82022-01-21 17:36:20 -0800117 }
118 acpigen_write_method_end(); /* Method */
119}
120
121/*
122 * Generate second half reset flow (SHRF) method.
123 */
124static void wwan_fm350gl_acpi_method_shrf(const struct device *parent_dev,
125 const struct drivers_wwan_fm_config *config)
126{
127 acpigen_write_method_serialized("SHRF", 0);
128 {
Cliff Huangca344f22023-03-02 10:08:17 -0800129 char mutex_path[128];
130 const struct soc_intel_common_block_pcie_rtd3_config *rtd3_config;
131
132 rtd3_config = config_of(config->rtd3dev);
133 if (rtd3_config->use_rp_mutex) {
Jamie Ryu6c0961a2023-06-08 10:41:11 -0700134 snprintf(mutex_path, sizeof(mutex_path), "%s",
135 acpi_device_path_join(parent_dev, RP_MUTEX_NAME));
Cliff Huangca344f22023-03-02 10:08:17 -0800136 /* Acquire root port mutex */
137 acpigen_write_acquire(mutex_path, ACPI_MUTEX_NO_TIMEOUT);
138 }
139
Cliff Huang7e653d82022-01-21 17:36:20 -0800140 /* call rtd3 method to Disable ModPHY Power Gating. */
Angel Pons92d44992022-02-13 13:37:04 +0100141 if (wwan_fm350gl_get_rtd3_method_support(config) &
Cliff Huang7e653d82022-01-21 17:36:20 -0800142 ACPI_PCIE_RP_EMIT_PSD0) {
143 acpigen_emit_namestring(acpi_device_path_join(parent_dev,
144 "PSD0"));
145 }
146 /* call rtd3 method to Enable SRC Clock. */
Angel Pons92d44992022-02-13 13:37:04 +0100147 if (wwan_fm350gl_get_rtd3_method_support(config) &
Cliff Huang7e653d82022-01-21 17:36:20 -0800148 ACPI_PCIE_RP_EMIT_SRCK) {
149 acpigen_emit_namestring(acpi_device_path_join(parent_dev,
150 "SRCK"));
151 acpigen_emit_byte(ONE_OP);
152 }
153 /* De-assert FCPO# GPIO. */
154 acpigen_disable_tx_gpio(&config->fcpo_gpio);
155 acpigen_write_sleep(FM350GL_TN2B);
156 /* De-assert RESET# GPIO. */
157 acpigen_disable_tx_gpio(&config->reset_gpio);
158 acpigen_write_sleep(FM350GL_TB2R);
159 /* De-assert PERST# GPIO. */
160 acpigen_disable_tx_gpio(&config->perst_gpio);
161 /* Call rtd3 method to trigger L2/L3 ready exit flow in root port */
Angel Pons92d44992022-02-13 13:37:04 +0100162 if (wwan_fm350gl_get_rtd3_method_support(config) &
Cliff Huang7e653d82022-01-21 17:36:20 -0800163 ACPI_PCIE_RP_EMIT_L23) {
164 acpigen_emit_namestring(acpi_device_path_join(parent_dev,
165 "L23D"));
166 }
167 acpigen_write_sleep(FM350GL_TIME_HW_INIT);
Cliff Huangca344f22023-03-02 10:08:17 -0800168
169 if (rtd3_config->use_rp_mutex)
170 acpigen_write_release(mutex_path);
Cliff Huang7e653d82022-01-21 17:36:20 -0800171 }
172 acpigen_write_method_end(); /* Method */
173}
174
175/*
176 * Generate _RST method. This is to perform a soft reset. It is added under
177 * PXSX. This is called during device driver removal.
178 */
179static void wwan_fm350gl_acpi_method_rst(const struct device *parent_dev,
180 const struct drivers_wwan_fm_config *config)
181{
182 acpigen_write_method_serialized("_RST", 0);
183 {
Cliff Huangca344f22023-03-02 10:08:17 -0800184 char mutex_path[128];
185 const struct soc_intel_common_block_pcie_rtd3_config *rtd3_config;
186
187 rtd3_config = config_of(config->rtd3dev);
188 if (rtd3_config->use_rp_mutex) {
Jamie Ryu6c0961a2023-06-08 10:41:11 -0700189 snprintf(mutex_path, sizeof(mutex_path), "%s",
190 acpi_device_path_join(parent_dev, RP_MUTEX_NAME));
Cliff Huangca344f22023-03-02 10:08:17 -0800191 /* Acquire root port mutex */
192 acpigen_write_acquire(mutex_path, ACPI_MUTEX_NO_TIMEOUT);
193 }
194
Cliff Huang7e653d82022-01-21 17:36:20 -0800195 /* Perform 1st Half of FLDR Flow for soft reset: FHRF(0) */
196 acpigen_emit_namestring("FHRF");
197 acpigen_emit_byte(RESET_TYPE_WARM);
198 /* Perform 2nd Half of FLDR Flow: SHRF() */
199 acpigen_emit_namestring("SHRF");
200 /* Indicates that the following _Off will be skipped. */
201 acpigen_emit_byte(INCREMENT_OP);
202 acpigen_emit_namestring(acpi_device_path_join(parent_dev, "RTD3.OFSK"));
Cliff Huangca344f22023-03-02 10:08:17 -0800203
204 if (rtd3_config->use_rp_mutex)
205 acpigen_write_release(mutex_path);
Cliff Huang7e653d82022-01-21 17:36:20 -0800206 }
207 acpigen_write_method_end(); /* Method */
208}
209
210/*
211 * Generate _RST method. This is to perform a cold reset. This reset will be
212 * included under PXSX.MRST. This method is used during device firmware update.
213 */
214static void wwan_fm350gl_acpi_method_mrst_rst(const struct device *parent_dev,
215 const struct drivers_wwan_fm_config *config)
216{
217 acpigen_write_method_serialized("_RST", 0);
218 {
Cliff Huangca344f22023-03-02 10:08:17 -0800219 char mutex_path[128];
220 const struct soc_intel_common_block_pcie_rtd3_config *rtd3_config;
221
222 rtd3_config = config_of(config->rtd3dev);
223 if (rtd3_config->use_rp_mutex) {
Jamie Ryu6c0961a2023-06-08 10:41:11 -0700224 snprintf(mutex_path, sizeof(mutex_path), "%s",
225 acpi_device_path_join(parent_dev, RP_MUTEX_NAME));
Cliff Huangca344f22023-03-02 10:08:17 -0800226 /* Acquire root port mutex */
227 acpigen_write_acquire(mutex_path, ACPI_MUTEX_NO_TIMEOUT);
228 }
229
Cliff Huang7e653d82022-01-21 17:36:20 -0800230 /* Perform 1st Half of FLDR Flow for cold reset: FHRF (1) */
231 acpigen_emit_namestring("FHRF");
232 acpigen_emit_byte(RESET_TYPE_COLD);
233 /* Perform 2nd Half of FLDR Flow: SHRF () */
234 acpigen_emit_namestring("SHRF");
235 /* Indicate kernel ACPI PM to skip _off RTD3 after reset at the end of
236 driver removal */
237 acpigen_emit_byte(INCREMENT_OP);
238 acpigen_emit_namestring(acpi_device_path_join(parent_dev, "RTD3.OFSK"));
Cliff Huangca344f22023-03-02 10:08:17 -0800239
240 if (rtd3_config->use_rp_mutex)
241 acpigen_write_release(mutex_path);
Cliff Huang7e653d82022-01-21 17:36:20 -0800242 }
243 acpigen_write_method_end(); /* Method */
244}
245
Cliff Huang96bb0ba2022-02-11 17:46:00 -0800246/*
247 * Generate DPTS (Device Prepare To Seep) Method. This is called in
248 * \.SB.MPTS Method.
249 */
250static void wwan_fm350gl_acpi_method_dpts(const struct device *parent_dev,
251 const struct drivers_wwan_fm_config *config)
252{
253 acpigen_write_method_serialized("DPTS", 1);
254 {
255 /* Perform 1st Half of FLDR Flow for cold reset: FHRF (1) */
256 acpigen_emit_namestring("FHRF");
257 acpigen_emit_byte(RESET_TYPE_COLD);
258 }
259 acpigen_write_method_end(); /* Method */
260}
261
Cliff Huang7e653d82022-01-21 17:36:20 -0800262static const char *wwan_fm350gl_acpi_name(const struct device *dev)
263{
264 /* Attached device name must be "PXSX" for the Linux Kernel to recognize it. */
265 return "PXSX";
266}
267
Paweł Anikiel1a593902023-10-16 14:22:15 +0000268static void
269wwan_fm350gl_acpi_event_interrupts(const struct acpi_gpio *wake_gpio)
270{
271 acpigen_write_name("_AEI");
272 acpigen_write_resourcetemplate_header();
273 acpi_device_write_gpio(wake_gpio);
274 acpigen_write_resourcetemplate_footer();
275}
276
277static void
278wwan_fm350gl_acpi_event_method(const struct device *dev,
279 const struct acpi_gpio *wake_gpio)
280{
281 char name[5];
282 uint16_t pin;
283
284 pin = wake_gpio->pins[0];
285 if (CONFIG(GENERIC_GPIO_LIB))
286 pin = gpio_acpi_pin(pin);
287
288 if (pin > 0xff) {
289 printk(BIOS_ERR, "%s: pins above 0xFF are unsupported (pin %u)\n",
290 __func__, pin);
291 return;
292 }
293
294 snprintf(name, sizeof(name), "_%c%02X",
295 wake_gpio->irq.mode == ACPI_IRQ_EDGE_TRIGGERED ? 'E' : 'L', pin);
296
297 acpigen_write_method_serialized(name, 0);
298 acpigen_notify(acpi_device_path(dev), 0x02); /* NOTIFY_DEVICE_WAKE */
299 acpigen_write_method_end();
300}
301
302static void wwan_fm350gl_acpi_gpio_events(const struct device *dev)
303{
304 const struct drivers_wwan_fm_config *config = config_of(dev);
305 const struct acpi_gpio *wake_gpio = &config->wake_gpio;
306
307 /* Write into GPIO controller's scope */
308 if (CONFIG(GENERIC_GPIO_LIB))
309 acpigen_write_scope(wake_gpio->resource ? : gpio_acpi_path(wake_gpio->pins[0]));
310 else
311 acpigen_write_scope(wake_gpio->resource);
312 wwan_fm350gl_acpi_event_interrupts(wake_gpio);
313 wwan_fm350gl_acpi_event_method(dev, wake_gpio);
314 acpigen_write_scope_end();
315}
316
Cliff Huang7e653d82022-01-21 17:36:20 -0800317static void wwan_fm350gl_acpi_fill_ssdt(const struct device *dev)
318{
319 const struct drivers_wwan_fm_config *config = config_of(dev);
320 const struct device *parent = dev->bus->dev;
321 const char *scope = acpi_device_path(parent);
Cliff Huangca344f22023-03-02 10:08:17 -0800322 const struct soc_intel_common_block_pcie_rtd3_config *rtd3_config;
Cliff Huang7e653d82022-01-21 17:36:20 -0800323
324 if (!is_dev_enabled(parent)) {
325 printk(BIOS_ERR, "%s: root port not enabled\n", __func__);
326 return;
327 }
328 if (!scope) {
329 printk(BIOS_ERR, "%s: root port scope not found\n", __func__);
330 return;
331 }
332 if (!config->fcpo_gpio.pin_count && !config->reset_gpio.pin_count &&
333 !config->perst_gpio.pin_count) {
334 printk(BIOS_ERR, "%s: FCPO, RESET, PERST GPIO required for %s.\n",
335 __func__, scope);
336 return;
337 }
Cliff Huangca344f22023-03-02 10:08:17 -0800338
339 rtd3_config = config_of(config->rtd3dev);
340 if (!rtd3_config->use_rp_mutex)
341 printk(BIOS_WARNING, "%s: RTD3 must use root port mutex.\n",
342 __func__);
343
Cliff Huang7e653d82022-01-21 17:36:20 -0800344 printk(BIOS_INFO, "%s: Enable WWAN for %s (%s)\n", scope, dev_path(parent),
345 config->desc ?: dev->chip_ops->name);
346 acpigen_write_scope(scope);
347 {
348 acpigen_write_device(wwan_fm350gl_acpi_name(dev));
349 {
350 acpigen_write_ADR(0);
351 if (config->name)
352 acpigen_write_name_string("_DDN", config->name);
353 if (config->desc)
354 acpigen_write_name_unicode("_STR", config->desc);
355 wwan_fm350gl_acpi_method_fhrf(parent, config);
356 wwan_fm350gl_acpi_method_shrf(parent, config);
357 wwan_fm350gl_acpi_method_rst(parent, config);
Cliff Huang96bb0ba2022-02-11 17:46:00 -0800358 wwan_fm350gl_acpi_method_dpts(parent, config);
Tim Wawrzynczak02927032022-02-28 08:57:09 -0700359
Kapil Porwal0b20a172022-11-26 20:10:47 +0530360 if (config->add_acpi_dma_property)
361 acpi_device_add_dma_property(NULL);
Tim Wawrzynczak02927032022-02-28 08:57:09 -0700362
Cliff Huang7e653d82022-01-21 17:36:20 -0800363 /* NOTE: the 5G driver will call MRST._RST to trigger a cold reset
364 * during firmware update.
365 */
366 acpigen_write_device("MRST");
367 {
368 acpigen_write_ADR(0);
369 wwan_fm350gl_acpi_method_mrst_rst(parent, config);
370 }
Tim Wawrzynczak02927032022-02-28 08:57:09 -0700371
Cliff Huang7e653d82022-01-21 17:36:20 -0800372 acpigen_write_device_end(); /* Device */
373 }
374 acpigen_write_device_end(); /* Device */
375 }
376 acpigen_write_scope_end(); /* Scope */
Paweł Anikiel1a593902023-10-16 14:22:15 +0000377
378 if (config->wake_gpio.pin_count && config->wake_gpio.type == ACPI_GPIO_TYPE_INTERRUPT)
379 wwan_fm350gl_acpi_gpio_events(dev);
Cliff Huang7e653d82022-01-21 17:36:20 -0800380}
381
382static struct device_operations wwan_fm350gl_ops = {
383 .read_resources = noop_read_resources,
384 .set_resources = noop_set_resources,
385 .acpi_fill_ssdt = wwan_fm350gl_acpi_fill_ssdt,
Cliff Huang96bb0ba2022-02-11 17:46:00 -0800386 .acpi_name = wwan_fm350gl_acpi_name
Cliff Huang7e653d82022-01-21 17:36:20 -0800387};
388
389static void wwan_fm350gl_acpi_enable(struct device *dev)
390{
391 dev->ops = &wwan_fm350gl_ops;
392}
393
394struct chip_operations drivers_wwan_fm_ops = {
Nicholas Sudsgaardbfb11be2024-01-30 09:53:46 +0900395 .name = "Fibocom FM-350-GL",
Cliff Huang7e653d82022-01-21 17:36:20 -0800396 .enable_dev = wwan_fm350gl_acpi_enable
397};