diff options
Diffstat (limited to 'drivers/spi/spi-sprd-adi.c')
-rw-r--r-- | drivers/spi/spi-sprd-adi.c | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/drivers/spi/spi-sprd-adi.c b/drivers/spi/spi-sprd-adi.c index 74bbd045aac0..197d4b0d81af 100644 --- a/drivers/spi/spi-sprd-adi.c +++ b/drivers/spi/spi-sprd-adi.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * SPDX-License-Identifier: GPL-2.0 | 4 | * SPDX-License-Identifier: GPL-2.0 |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include <linux/delay.h> | ||
7 | #include <linux/hwspinlock.h> | 8 | #include <linux/hwspinlock.h> |
8 | #include <linux/init.h> | 9 | #include <linux/init.h> |
9 | #include <linux/io.h> | 10 | #include <linux/io.h> |
@@ -12,6 +13,7 @@ | |||
12 | #include <linux/of.h> | 13 | #include <linux/of.h> |
13 | #include <linux/of_device.h> | 14 | #include <linux/of_device.h> |
14 | #include <linux/platform_device.h> | 15 | #include <linux/platform_device.h> |
16 | #include <linux/reboot.h> | ||
15 | #include <linux/spi/spi.h> | 17 | #include <linux/spi/spi.h> |
16 | #include <linux/sizes.h> | 18 | #include <linux/sizes.h> |
17 | 19 | ||
@@ -67,6 +69,40 @@ | |||
67 | #define ADI_READ_TIMEOUT 2000 | 69 | #define ADI_READ_TIMEOUT 2000 |
68 | #define REG_ADDR_LOW_MASK GENMASK(11, 0) | 70 | #define REG_ADDR_LOW_MASK GENMASK(11, 0) |
69 | 71 | ||
72 | /* Registers definitions for PMIC watchdog controller */ | ||
73 | #define REG_WDG_LOAD_LOW 0x80 | ||
74 | #define REG_WDG_LOAD_HIGH 0x84 | ||
75 | #define REG_WDG_CTRL 0x88 | ||
76 | #define REG_WDG_LOCK 0xa0 | ||
77 | |||
78 | /* Bits definitions for register REG_WDG_CTRL */ | ||
79 | #define BIT_WDG_RUN BIT(1) | ||
80 | #define BIT_WDG_RST BIT(3) | ||
81 | |||
82 | /* Registers definitions for PMIC */ | ||
83 | #define PMIC_RST_STATUS 0xee8 | ||
84 | #define PMIC_MODULE_EN 0xc08 | ||
85 | #define PMIC_CLK_EN 0xc18 | ||
86 | #define BIT_WDG_EN BIT(2) | ||
87 | |||
88 | /* Definition of PMIC reset status register */ | ||
89 | #define HWRST_STATUS_RECOVERY 0x20 | ||
90 | #define HWRST_STATUS_NORMAL 0x40 | ||
91 | #define HWRST_STATUS_ALARM 0x50 | ||
92 | #define HWRST_STATUS_SLEEP 0x60 | ||
93 | #define HWRST_STATUS_FASTBOOT 0x30 | ||
94 | #define HWRST_STATUS_SPECIAL 0x70 | ||
95 | #define HWRST_STATUS_PANIC 0x80 | ||
96 | #define HWRST_STATUS_CFTREBOOT 0x90 | ||
97 | #define HWRST_STATUS_AUTODLOADER 0xa0 | ||
98 | #define HWRST_STATUS_IQMODE 0xb0 | ||
99 | #define HWRST_STATUS_SPRDISK 0xc0 | ||
100 | |||
101 | /* Use default timeout 50 ms that converts to watchdog values */ | ||
102 | #define WDG_LOAD_VAL ((50 * 1000) / 32768) | ||
103 | #define WDG_LOAD_MASK GENMASK(15, 0) | ||
104 | #define WDG_UNLOCK_KEY 0xe551 | ||
105 | |||
70 | struct sprd_adi { | 106 | struct sprd_adi { |
71 | struct spi_controller *ctlr; | 107 | struct spi_controller *ctlr; |
72 | struct device *dev; | 108 | struct device *dev; |
@@ -74,6 +110,7 @@ struct sprd_adi { | |||
74 | struct hwspinlock *hwlock; | 110 | struct hwspinlock *hwlock; |
75 | unsigned long slave_vbase; | 111 | unsigned long slave_vbase; |
76 | unsigned long slave_pbase; | 112 | unsigned long slave_pbase; |
113 | struct notifier_block restart_handler; | ||
77 | }; | 114 | }; |
78 | 115 | ||
79 | static int sprd_adi_check_paddr(struct sprd_adi *sadi, u32 paddr) | 116 | static int sprd_adi_check_paddr(struct sprd_adi *sadi, u32 paddr) |
@@ -270,6 +307,72 @@ static int sprd_adi_transfer_one(struct spi_controller *ctlr, | |||
270 | return 0; | 307 | return 0; |
271 | } | 308 | } |
272 | 309 | ||
310 | static int sprd_adi_restart_handler(struct notifier_block *this, | ||
311 | unsigned long mode, void *cmd) | ||
312 | { | ||
313 | struct sprd_adi *sadi = container_of(this, struct sprd_adi, | ||
314 | restart_handler); | ||
315 | u32 val, reboot_mode = 0; | ||
316 | |||
317 | if (!cmd) | ||
318 | reboot_mode = HWRST_STATUS_NORMAL; | ||
319 | else if (!strncmp(cmd, "recovery", 8)) | ||
320 | reboot_mode = HWRST_STATUS_RECOVERY; | ||
321 | else if (!strncmp(cmd, "alarm", 5)) | ||
322 | reboot_mode = HWRST_STATUS_ALARM; | ||
323 | else if (!strncmp(cmd, "fastsleep", 9)) | ||
324 | reboot_mode = HWRST_STATUS_SLEEP; | ||
325 | else if (!strncmp(cmd, "bootloader", 10)) | ||
326 | reboot_mode = HWRST_STATUS_FASTBOOT; | ||
327 | else if (!strncmp(cmd, "panic", 5)) | ||
328 | reboot_mode = HWRST_STATUS_PANIC; | ||
329 | else if (!strncmp(cmd, "special", 7)) | ||
330 | reboot_mode = HWRST_STATUS_SPECIAL; | ||
331 | else if (!strncmp(cmd, "cftreboot", 9)) | ||
332 | reboot_mode = HWRST_STATUS_CFTREBOOT; | ||
333 | else if (!strncmp(cmd, "autodloader", 11)) | ||
334 | reboot_mode = HWRST_STATUS_AUTODLOADER; | ||
335 | else if (!strncmp(cmd, "iqmode", 6)) | ||
336 | reboot_mode = HWRST_STATUS_IQMODE; | ||
337 | else if (!strncmp(cmd, "sprdisk", 7)) | ||
338 | reboot_mode = HWRST_STATUS_SPRDISK; | ||
339 | else | ||
340 | reboot_mode = HWRST_STATUS_NORMAL; | ||
341 | |||
342 | /* Record the reboot mode */ | ||
343 | sprd_adi_read(sadi, sadi->slave_pbase + PMIC_RST_STATUS, &val); | ||
344 | val |= reboot_mode; | ||
345 | sprd_adi_write(sadi, sadi->slave_pbase + PMIC_RST_STATUS, val); | ||
346 | |||
347 | /* Enable the interface clock of the watchdog */ | ||
348 | sprd_adi_read(sadi, sadi->slave_pbase + PMIC_MODULE_EN, &val); | ||
349 | val |= BIT_WDG_EN; | ||
350 | sprd_adi_write(sadi, sadi->slave_pbase + PMIC_MODULE_EN, val); | ||
351 | |||
352 | /* Enable the work clock of the watchdog */ | ||
353 | sprd_adi_read(sadi, sadi->slave_pbase + PMIC_CLK_EN, &val); | ||
354 | val |= BIT_WDG_EN; | ||
355 | sprd_adi_write(sadi, sadi->slave_pbase + PMIC_CLK_EN, val); | ||
356 | |||
357 | /* Unlock the watchdog */ | ||
358 | sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOCK, WDG_UNLOCK_KEY); | ||
359 | |||
360 | /* Load the watchdog timeout value, 50ms is always enough. */ | ||
361 | sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOAD_LOW, | ||
362 | WDG_LOAD_VAL & WDG_LOAD_MASK); | ||
363 | sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_LOAD_HIGH, 0); | ||
364 | |||
365 | /* Start the watchdog to reset system */ | ||
366 | sprd_adi_read(sadi, sadi->slave_pbase + REG_WDG_CTRL, &val); | ||
367 | val |= BIT_WDG_RUN | BIT_WDG_RST; | ||
368 | sprd_adi_write(sadi, sadi->slave_pbase + REG_WDG_CTRL, val); | ||
369 | |||
370 | mdelay(1000); | ||
371 | |||
372 | dev_emerg(sadi->dev, "Unable to restart system\n"); | ||
373 | return NOTIFY_DONE; | ||
374 | } | ||
375 | |||
273 | static void sprd_adi_hw_init(struct sprd_adi *sadi) | 376 | static void sprd_adi_hw_init(struct sprd_adi *sadi) |
274 | { | 377 | { |
275 | struct device_node *np = sadi->dev->of_node; | 378 | struct device_node *np = sadi->dev->of_node; |
@@ -383,6 +486,14 @@ static int sprd_adi_probe(struct platform_device *pdev) | |||
383 | goto free_hwlock; | 486 | goto free_hwlock; |
384 | } | 487 | } |
385 | 488 | ||
489 | sadi->restart_handler.notifier_call = sprd_adi_restart_handler; | ||
490 | sadi->restart_handler.priority = 128; | ||
491 | ret = register_restart_handler(&sadi->restart_handler); | ||
492 | if (ret) { | ||
493 | dev_err(&pdev->dev, "can not register restart handler\n"); | ||
494 | goto free_hwlock; | ||
495 | } | ||
496 | |||
386 | return 0; | 497 | return 0; |
387 | 498 | ||
388 | free_hwlock: | 499 | free_hwlock: |
@@ -397,6 +508,7 @@ static int sprd_adi_remove(struct platform_device *pdev) | |||
397 | struct spi_controller *ctlr = dev_get_drvdata(&pdev->dev); | 508 | struct spi_controller *ctlr = dev_get_drvdata(&pdev->dev); |
398 | struct sprd_adi *sadi = spi_controller_get_devdata(ctlr); | 509 | struct sprd_adi *sadi = spi_controller_get_devdata(ctlr); |
399 | 510 | ||
511 | unregister_restart_handler(&sadi->restart_handler); | ||
400 | hwspin_lock_free(sadi->hwlock); | 512 | hwspin_lock_free(sadi->hwlock); |
401 | return 0; | 513 | return 0; |
402 | } | 514 | } |