cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

at91-poweroff.c (6359B)


      1/*
      2 * Atmel AT91 SAM9 SoCs reset code
      3 *
      4 * Copyright (C) 2007 Atmel Corporation.
      5 * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
      6 * Copyright (C) 2014 Free Electrons
      7 *
      8 * This file is licensed under the terms of the GNU General Public
      9 * License version 2.  This program is licensed "as is" without any
     10 * warranty of any kind, whether express or implied.
     11 */
     12
     13#include <linux/clk.h>
     14#include <linux/io.h>
     15#include <linux/module.h>
     16#include <linux/of.h>
     17#include <linux/of_address.h>
     18#include <linux/platform_device.h>
     19#include <linux/printk.h>
     20
     21#include <soc/at91/at91sam9_ddrsdr.h>
     22
     23#define AT91_SHDW_CR	0x00		/* Shut Down Control Register */
     24#define AT91_SHDW_SHDW		BIT(0)			/* Shut Down command */
     25#define AT91_SHDW_KEY		(0xa5 << 24)		/* KEY Password */
     26
     27#define AT91_SHDW_MR	0x04		/* Shut Down Mode Register */
     28#define AT91_SHDW_WKMODE0	GENMASK(2, 0)		/* Wake-up 0 Mode Selection */
     29#define AT91_SHDW_CPTWK0_MAX	0xf			/* Maximum Counter On Wake Up 0 */
     30#define AT91_SHDW_CPTWK0	(AT91_SHDW_CPTWK0_MAX << 4) /* Counter On Wake Up 0 */
     31#define AT91_SHDW_CPTWK0_(x)	((x) << 4)
     32#define AT91_SHDW_RTTWKEN	BIT(16)			/* Real Time Timer Wake-up Enable */
     33#define AT91_SHDW_RTCWKEN	BIT(17)			/* Real Time Clock Wake-up Enable */
     34
     35#define AT91_SHDW_SR	0x08		/* Shut Down Status Register */
     36#define AT91_SHDW_WAKEUP0	BIT(0)			/* Wake-up 0 Status */
     37#define AT91_SHDW_RTTWK		BIT(16)			/* Real-time Timer Wake-up */
     38#define AT91_SHDW_RTCWK		BIT(17)			/* Real-time Clock Wake-up [SAM9RL] */
     39
     40enum wakeup_type {
     41	AT91_SHDW_WKMODE0_NONE		= 0,
     42	AT91_SHDW_WKMODE0_HIGH		= 1,
     43	AT91_SHDW_WKMODE0_LOW		= 2,
     44	AT91_SHDW_WKMODE0_ANYLEVEL	= 3,
     45};
     46
     47static const char *shdwc_wakeup_modes[] = {
     48	[AT91_SHDW_WKMODE0_NONE]	= "none",
     49	[AT91_SHDW_WKMODE0_HIGH]	= "high",
     50	[AT91_SHDW_WKMODE0_LOW]		= "low",
     51	[AT91_SHDW_WKMODE0_ANYLEVEL]	= "any",
     52};
     53
     54static struct shdwc {
     55	struct clk *sclk;
     56	void __iomem *shdwc_base;
     57	void __iomem *mpddrc_base;
     58} at91_shdwc;
     59
     60static void __init at91_wakeup_status(struct platform_device *pdev)
     61{
     62	const char *reason;
     63	u32 reg = readl(at91_shdwc.shdwc_base + AT91_SHDW_SR);
     64
     65	/* Simple power-on, just bail out */
     66	if (!reg)
     67		return;
     68
     69	if (reg & AT91_SHDW_RTTWK)
     70		reason = "RTT";
     71	else if (reg & AT91_SHDW_RTCWK)
     72		reason = "RTC";
     73	else
     74		reason = "unknown";
     75
     76	dev_info(&pdev->dev, "Wake-Up source: %s\n", reason);
     77}
     78
     79static void at91_poweroff(void)
     80{
     81	asm volatile(
     82		/* Align to cache lines */
     83		".balign 32\n\t"
     84
     85		/* Ensure AT91_SHDW_CR is in the TLB by reading it */
     86		"	ldr	r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
     87
     88		/* Power down SDRAM0 */
     89		"	tst	%0, #0\n\t"
     90		"	beq	1f\n\t"
     91		"	str	%1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
     92		/* Shutdown CPU */
     93		"1:	str	%3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
     94
     95		"	b	.\n\t"
     96		:
     97		: "r" (at91_shdwc.mpddrc_base),
     98		  "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
     99		  "r" (at91_shdwc.shdwc_base),
    100		  "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
    101		: "r6");
    102}
    103
    104static int at91_poweroff_get_wakeup_mode(struct device_node *np)
    105{
    106	const char *pm;
    107	unsigned int i;
    108	int err;
    109
    110	err = of_property_read_string(np, "atmel,wakeup-mode", &pm);
    111	if (err < 0)
    112		return AT91_SHDW_WKMODE0_ANYLEVEL;
    113
    114	for (i = 0; i < ARRAY_SIZE(shdwc_wakeup_modes); i++)
    115		if (!strcasecmp(pm, shdwc_wakeup_modes[i]))
    116			return i;
    117
    118	return -ENODEV;
    119}
    120
    121static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
    122{
    123	struct device_node *np = pdev->dev.of_node;
    124	int wakeup_mode;
    125	u32 mode = 0, tmp;
    126
    127	wakeup_mode = at91_poweroff_get_wakeup_mode(np);
    128	if (wakeup_mode < 0) {
    129		dev_warn(&pdev->dev, "shdwc unknown wakeup mode\n");
    130		return;
    131	}
    132
    133	if (!of_property_read_u32(np, "atmel,wakeup-counter", &tmp)) {
    134		if (tmp > AT91_SHDW_CPTWK0_MAX) {
    135			dev_warn(&pdev->dev,
    136				 "shdwc wakeup counter 0x%x > 0x%x reduce it to 0x%x\n",
    137				 tmp, AT91_SHDW_CPTWK0_MAX, AT91_SHDW_CPTWK0_MAX);
    138			tmp = AT91_SHDW_CPTWK0_MAX;
    139		}
    140		mode |= AT91_SHDW_CPTWK0_(tmp);
    141	}
    142
    143	if (of_property_read_bool(np, "atmel,wakeup-rtc-timer"))
    144			mode |= AT91_SHDW_RTCWKEN;
    145
    146	if (of_property_read_bool(np, "atmel,wakeup-rtt-timer"))
    147			mode |= AT91_SHDW_RTTWKEN;
    148
    149	writel(wakeup_mode | mode, at91_shdwc.shdwc_base + AT91_SHDW_MR);
    150}
    151
    152static int __init at91_poweroff_probe(struct platform_device *pdev)
    153{
    154	struct resource *res;
    155	struct device_node *np;
    156	u32 ddr_type;
    157	int ret;
    158
    159	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    160	at91_shdwc.shdwc_base = devm_ioremap_resource(&pdev->dev, res);
    161	if (IS_ERR(at91_shdwc.shdwc_base))
    162		return PTR_ERR(at91_shdwc.shdwc_base);
    163
    164	at91_shdwc.sclk = devm_clk_get(&pdev->dev, NULL);
    165	if (IS_ERR(at91_shdwc.sclk))
    166		return PTR_ERR(at91_shdwc.sclk);
    167
    168	ret = clk_prepare_enable(at91_shdwc.sclk);
    169	if (ret) {
    170		dev_err(&pdev->dev, "Could not enable slow clock\n");
    171		return ret;
    172	}
    173
    174	at91_wakeup_status(pdev);
    175
    176	if (pdev->dev.of_node)
    177		at91_poweroff_dt_set_wakeup_mode(pdev);
    178
    179	np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
    180	if (np) {
    181		at91_shdwc.mpddrc_base = of_iomap(np, 0);
    182		of_node_put(np);
    183
    184		if (!at91_shdwc.mpddrc_base) {
    185			ret = -ENOMEM;
    186			goto clk_disable;
    187		}
    188
    189		ddr_type = readl(at91_shdwc.mpddrc_base + AT91_DDRSDRC_MDR) &
    190				 AT91_DDRSDRC_MD;
    191		if (ddr_type != AT91_DDRSDRC_MD_LPDDR2 &&
    192		    ddr_type != AT91_DDRSDRC_MD_LPDDR3) {
    193			iounmap(at91_shdwc.mpddrc_base);
    194			at91_shdwc.mpddrc_base = NULL;
    195		}
    196	}
    197
    198	pm_power_off = at91_poweroff;
    199
    200	return 0;
    201
    202clk_disable:
    203	clk_disable_unprepare(at91_shdwc.sclk);
    204	return ret;
    205}
    206
    207static int __exit at91_poweroff_remove(struct platform_device *pdev)
    208{
    209	if (pm_power_off == at91_poweroff)
    210		pm_power_off = NULL;
    211
    212	if (at91_shdwc.mpddrc_base)
    213		iounmap(at91_shdwc.mpddrc_base);
    214
    215	clk_disable_unprepare(at91_shdwc.sclk);
    216
    217	return 0;
    218}
    219
    220static const struct of_device_id at91_poweroff_of_match[] = {
    221	{ .compatible = "atmel,at91sam9260-shdwc", },
    222	{ .compatible = "atmel,at91sam9rl-shdwc", },
    223	{ .compatible = "atmel,at91sam9x5-shdwc", },
    224	{ /*sentinel*/ }
    225};
    226MODULE_DEVICE_TABLE(of, at91_poweroff_of_match);
    227
    228static struct platform_driver at91_poweroff_driver = {
    229	.remove = __exit_p(at91_poweroff_remove),
    230	.driver = {
    231		.name = "at91-poweroff",
    232		.of_match_table = at91_poweroff_of_match,
    233	},
    234};
    235module_platform_driver_probe(at91_poweroff_driver, at91_poweroff_probe);
    236
    237MODULE_AUTHOR("Atmel Corporation");
    238MODULE_DESCRIPTION("Shutdown driver for Atmel SoCs");
    239MODULE_LICENSE("GPL v2");