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-sama5d2_shdwc.c (11273B)


      1/*
      2 * Atmel SAMA5D2-Compatible Shutdown Controller (SHDWC) driver.
      3 * Found on some SoCs as the sama5d2 (obviously).
      4 *
      5 * Copyright (C) 2015 Atmel Corporation,
      6 *                    Nicolas Ferre <nicolas.ferre@atmel.com>
      7 *
      8 * Evolved from driver at91-poweroff.c.
      9 *
     10 * This file is licensed under the terms of the GNU General Public
     11 * License version 2.  This program is licensed "as is" without any
     12 * warranty of any kind, whether express or implied.
     13 *
     14 * TODO:
     15 * - addition to status of other wake-up inputs [1 - 15]
     16 * - Analog Comparator wake-up alarm
     17 * - Serial RX wake-up alarm
     18 * - low power debouncer
     19 */
     20
     21#include <linux/clk.h>
     22#include <linux/clk/at91_pmc.h>
     23#include <linux/io.h>
     24#include <linux/module.h>
     25#include <linux/of.h>
     26#include <linux/of_address.h>
     27#include <linux/platform_device.h>
     28#include <linux/printk.h>
     29
     30#include <soc/at91/at91sam9_ddrsdr.h>
     31
     32#define SLOW_CLOCK_FREQ	32768
     33
     34#define AT91_SHDW_CR	0x00		/* Shut Down Control Register */
     35#define AT91_SHDW_SHDW		BIT(0)			/* Shut Down command */
     36#define AT91_SHDW_KEY		(0xa5UL << 24)		/* KEY Password */
     37
     38#define AT91_SHDW_MR	0x04		/* Shut Down Mode Register */
     39#define AT91_SHDW_WKUPDBC_SHIFT	24
     40#define AT91_SHDW_WKUPDBC_MASK	GENMASK(26, 24)
     41#define AT91_SHDW_WKUPDBC(x)	(((x) << AT91_SHDW_WKUPDBC_SHIFT) \
     42						& AT91_SHDW_WKUPDBC_MASK)
     43
     44#define AT91_SHDW_SR	0x08		/* Shut Down Status Register */
     45#define AT91_SHDW_WKUPIS_SHIFT	16
     46#define AT91_SHDW_WKUPIS_MASK	GENMASK(31, 16)
     47#define AT91_SHDW_WKUPIS(x)	((1 << (x)) << AT91_SHDW_WKUPIS_SHIFT \
     48						& AT91_SHDW_WKUPIS_MASK)
     49
     50#define AT91_SHDW_WUIR	0x0c		/* Shutdown Wake-up Inputs Register */
     51#define AT91_SHDW_WKUPEN_MASK	GENMASK(15, 0)
     52#define AT91_SHDW_WKUPEN(x)	((1 << (x)) & AT91_SHDW_WKUPEN_MASK)
     53#define AT91_SHDW_WKUPT_SHIFT	16
     54#define AT91_SHDW_WKUPT_MASK	GENMASK(31, 16)
     55#define AT91_SHDW_WKUPT(x)	((1 << (x)) << AT91_SHDW_WKUPT_SHIFT \
     56						& AT91_SHDW_WKUPT_MASK)
     57
     58#define SHDW_WK_PIN(reg, cfg)	((reg) & AT91_SHDW_WKUPIS((cfg)->wkup_pin_input))
     59#define SHDW_RTCWK(reg, cfg)	(((reg) >> ((cfg)->sr_rtcwk_shift)) & 0x1)
     60#define SHDW_RTTWK(reg, cfg)	(((reg) >> ((cfg)->sr_rttwk_shift)) & 0x1)
     61#define SHDW_RTCWKEN(cfg)	(1 << ((cfg)->mr_rtcwk_shift))
     62#define SHDW_RTTWKEN(cfg)	(1 << ((cfg)->mr_rttwk_shift))
     63
     64#define DBC_PERIOD_US(x)	DIV_ROUND_UP_ULL((1000000 * (x)), \
     65							SLOW_CLOCK_FREQ)
     66
     67#define SHDW_CFG_NOT_USED	(32)
     68
     69struct shdwc_reg_config {
     70	u8 wkup_pin_input;
     71	u8 mr_rtcwk_shift;
     72	u8 mr_rttwk_shift;
     73	u8 sr_rtcwk_shift;
     74	u8 sr_rttwk_shift;
     75};
     76
     77struct pmc_reg_config {
     78	u8 mckr;
     79};
     80
     81struct ddrc_reg_config {
     82	u32 type_offset;
     83	u32 type_mask;
     84};
     85
     86struct reg_config {
     87	struct shdwc_reg_config shdwc;
     88	struct pmc_reg_config pmc;
     89	struct ddrc_reg_config ddrc;
     90};
     91
     92struct shdwc {
     93	const struct reg_config *rcfg;
     94	struct clk *sclk;
     95	void __iomem *shdwc_base;
     96	void __iomem *mpddrc_base;
     97	void __iomem *pmc_base;
     98};
     99
    100/*
    101 * Hold configuration here, cannot be more than one instance of the driver
    102 * since pm_power_off itself is global.
    103 */
    104static struct shdwc *at91_shdwc;
    105
    106static const unsigned long long sdwc_dbc_period[] = {
    107	0, 3, 32, 512, 4096, 32768,
    108};
    109
    110static void __init at91_wakeup_status(struct platform_device *pdev)
    111{
    112	struct shdwc *shdw = platform_get_drvdata(pdev);
    113	const struct reg_config *rcfg = shdw->rcfg;
    114	u32 reg;
    115	char *reason = "unknown";
    116
    117	reg = readl(shdw->shdwc_base + AT91_SHDW_SR);
    118
    119	dev_dbg(&pdev->dev, "%s: status = %#x\n", __func__, reg);
    120
    121	/* Simple power-on, just bail out */
    122	if (!reg)
    123		return;
    124
    125	if (SHDW_WK_PIN(reg, &rcfg->shdwc))
    126		reason = "WKUP pin";
    127	else if (SHDW_RTCWK(reg, &rcfg->shdwc))
    128		reason = "RTC";
    129	else if (SHDW_RTTWK(reg, &rcfg->shdwc))
    130		reason = "RTT";
    131
    132	pr_info("AT91: Wake-Up source: %s\n", reason);
    133}
    134
    135static void at91_poweroff(void)
    136{
    137	asm volatile(
    138		/* Align to cache lines */
    139		".balign 32\n\t"
    140
    141		/* Ensure AT91_SHDW_CR is in the TLB by reading it */
    142		"	ldr	r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
    143
    144		/* Power down SDRAM0 */
    145		"	tst	%0, #0\n\t"
    146		"	beq	1f\n\t"
    147		"	str	%1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
    148
    149		/* Switch the master clock source to slow clock. */
    150		"1:	ldr	r6, [%4, %5]\n\t"
    151		"	bic	r6, r6,  #" __stringify(AT91_PMC_CSS) "\n\t"
    152		"	str	r6, [%4, %5]\n\t"
    153		/* Wait for clock switch. */
    154		"2:	ldr	r6, [%4, #" __stringify(AT91_PMC_SR) "]\n\t"
    155		"	tst	r6, #"	    __stringify(AT91_PMC_MCKRDY) "\n\t"
    156		"	beq	2b\n\t"
    157
    158		/* Shutdown CPU */
    159		"	str	%3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
    160
    161		"	b	.\n\t"
    162		:
    163		: "r" (at91_shdwc->mpddrc_base),
    164		  "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
    165		  "r" (at91_shdwc->shdwc_base),
    166		  "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW),
    167		  "r" (at91_shdwc->pmc_base),
    168		  "r" (at91_shdwc->rcfg->pmc.mckr)
    169		: "r6");
    170}
    171
    172static u32 at91_shdwc_debouncer_value(struct platform_device *pdev,
    173				      u32 in_period_us)
    174{
    175	int i;
    176	int max_idx = ARRAY_SIZE(sdwc_dbc_period) - 1;
    177	unsigned long long period_us;
    178	unsigned long long max_period_us = DBC_PERIOD_US(sdwc_dbc_period[max_idx]);
    179
    180	if (in_period_us > max_period_us) {
    181		dev_warn(&pdev->dev,
    182			 "debouncer period %u too big, reduced to %llu us\n",
    183			 in_period_us, max_period_us);
    184		return max_idx;
    185	}
    186
    187	for (i = max_idx - 1; i > 0; i--) {
    188		period_us = DBC_PERIOD_US(sdwc_dbc_period[i]);
    189		dev_dbg(&pdev->dev, "%s: ref[%d] = %llu\n",
    190						__func__, i, period_us);
    191		if (in_period_us > period_us)
    192			break;
    193	}
    194
    195	return i + 1;
    196}
    197
    198static u32 at91_shdwc_get_wakeup_input(struct platform_device *pdev,
    199				       struct device_node *np)
    200{
    201	struct device_node *cnp;
    202	u32 wk_input_mask;
    203	u32 wuir = 0;
    204	u32 wk_input;
    205
    206	for_each_child_of_node(np, cnp) {
    207		if (of_property_read_u32(cnp, "reg", &wk_input)) {
    208			dev_warn(&pdev->dev, "reg property is missing for %pOF\n",
    209				 cnp);
    210			continue;
    211		}
    212
    213		wk_input_mask = 1 << wk_input;
    214		if (!(wk_input_mask & AT91_SHDW_WKUPEN_MASK)) {
    215			dev_warn(&pdev->dev,
    216				 "wake-up input %d out of bounds ignore\n",
    217				 wk_input);
    218			continue;
    219		}
    220		wuir |= wk_input_mask;
    221
    222		if (of_property_read_bool(cnp, "atmel,wakeup-active-high"))
    223			wuir |= AT91_SHDW_WKUPT(wk_input);
    224
    225		dev_dbg(&pdev->dev, "%s: (child %d) wuir = %#x\n",
    226						__func__, wk_input, wuir);
    227	}
    228
    229	return wuir;
    230}
    231
    232static void at91_shdwc_dt_configure(struct platform_device *pdev)
    233{
    234	struct shdwc *shdw = platform_get_drvdata(pdev);
    235	const struct reg_config *rcfg = shdw->rcfg;
    236	struct device_node *np = pdev->dev.of_node;
    237	u32 mode = 0, tmp, input;
    238
    239	if (!np) {
    240		dev_err(&pdev->dev, "device node not found\n");
    241		return;
    242	}
    243
    244	if (!of_property_read_u32(np, "debounce-delay-us", &tmp))
    245		mode |= AT91_SHDW_WKUPDBC(at91_shdwc_debouncer_value(pdev, tmp));
    246
    247	if (of_property_read_bool(np, "atmel,wakeup-rtc-timer"))
    248		mode |= SHDW_RTCWKEN(&rcfg->shdwc);
    249
    250	if (of_property_read_bool(np, "atmel,wakeup-rtt-timer"))
    251		mode |= SHDW_RTTWKEN(&rcfg->shdwc);
    252
    253	dev_dbg(&pdev->dev, "%s: mode = %#x\n", __func__, mode);
    254	writel(mode, shdw->shdwc_base + AT91_SHDW_MR);
    255
    256	input = at91_shdwc_get_wakeup_input(pdev, np);
    257	writel(input, shdw->shdwc_base + AT91_SHDW_WUIR);
    258}
    259
    260static const struct reg_config sama5d2_reg_config = {
    261	.shdwc = {
    262		.wkup_pin_input = 0,
    263		.mr_rtcwk_shift = 17,
    264		.mr_rttwk_shift	= SHDW_CFG_NOT_USED,
    265		.sr_rtcwk_shift = 5,
    266		.sr_rttwk_shift = SHDW_CFG_NOT_USED,
    267	},
    268	.pmc = {
    269		.mckr		= 0x30,
    270	},
    271	.ddrc = {
    272		.type_offset	= AT91_DDRSDRC_MDR,
    273		.type_mask	= AT91_DDRSDRC_MD
    274	},
    275};
    276
    277static const struct reg_config sam9x60_reg_config = {
    278	.shdwc = {
    279		.wkup_pin_input = 0,
    280		.mr_rtcwk_shift = 17,
    281		.mr_rttwk_shift = 16,
    282		.sr_rtcwk_shift = 5,
    283		.sr_rttwk_shift = 4,
    284	},
    285	.pmc = {
    286		.mckr		= 0x28,
    287	},
    288	.ddrc = {
    289		.type_offset	= AT91_DDRSDRC_MDR,
    290		.type_mask	= AT91_DDRSDRC_MD
    291	},
    292};
    293
    294static const struct reg_config sama7g5_reg_config = {
    295	.shdwc = {
    296		.wkup_pin_input = 0,
    297		.mr_rtcwk_shift = 17,
    298		.mr_rttwk_shift = 16,
    299		.sr_rtcwk_shift = 5,
    300		.sr_rttwk_shift = 4,
    301	},
    302	.pmc = {
    303		.mckr		= 0x28,
    304	},
    305};
    306
    307static const struct of_device_id at91_shdwc_of_match[] = {
    308	{
    309		.compatible = "atmel,sama5d2-shdwc",
    310		.data = &sama5d2_reg_config,
    311	},
    312	{
    313		.compatible = "microchip,sam9x60-shdwc",
    314		.data = &sam9x60_reg_config,
    315	},
    316	{
    317		.compatible = "microchip,sama7g5-shdwc",
    318		.data = &sama7g5_reg_config,
    319	}, {
    320		/*sentinel*/
    321	}
    322};
    323MODULE_DEVICE_TABLE(of, at91_shdwc_of_match);
    324
    325static const struct of_device_id at91_pmc_ids[] = {
    326	{ .compatible = "atmel,sama5d2-pmc" },
    327	{ .compatible = "microchip,sam9x60-pmc" },
    328	{ .compatible = "microchip,sama7g5-pmc" },
    329	{ /* Sentinel. */ }
    330};
    331
    332static int __init at91_shdwc_probe(struct platform_device *pdev)
    333{
    334	struct resource *res;
    335	const struct of_device_id *match;
    336	struct device_node *np;
    337	u32 ddr_type;
    338	int ret;
    339
    340	if (!pdev->dev.of_node)
    341		return -ENODEV;
    342
    343	if (at91_shdwc)
    344		return -EBUSY;
    345
    346	at91_shdwc = devm_kzalloc(&pdev->dev, sizeof(*at91_shdwc), GFP_KERNEL);
    347	if (!at91_shdwc)
    348		return -ENOMEM;
    349
    350	platform_set_drvdata(pdev, at91_shdwc);
    351
    352	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    353	at91_shdwc->shdwc_base = devm_ioremap_resource(&pdev->dev, res);
    354	if (IS_ERR(at91_shdwc->shdwc_base))
    355		return PTR_ERR(at91_shdwc->shdwc_base);
    356
    357	match = of_match_node(at91_shdwc_of_match, pdev->dev.of_node);
    358	at91_shdwc->rcfg = match->data;
    359
    360	at91_shdwc->sclk = devm_clk_get(&pdev->dev, NULL);
    361	if (IS_ERR(at91_shdwc->sclk))
    362		return PTR_ERR(at91_shdwc->sclk);
    363
    364	ret = clk_prepare_enable(at91_shdwc->sclk);
    365	if (ret) {
    366		dev_err(&pdev->dev, "Could not enable slow clock\n");
    367		return ret;
    368	}
    369
    370	at91_wakeup_status(pdev);
    371
    372	at91_shdwc_dt_configure(pdev);
    373
    374	np = of_find_matching_node(NULL, at91_pmc_ids);
    375	if (!np) {
    376		ret = -ENODEV;
    377		goto clk_disable;
    378	}
    379
    380	at91_shdwc->pmc_base = of_iomap(np, 0);
    381	of_node_put(np);
    382
    383	if (!at91_shdwc->pmc_base) {
    384		ret = -ENOMEM;
    385		goto clk_disable;
    386	}
    387
    388	if (at91_shdwc->rcfg->ddrc.type_mask) {
    389		np = of_find_compatible_node(NULL, NULL,
    390					     "atmel,sama5d3-ddramc");
    391		if (!np) {
    392			ret = -ENODEV;
    393			goto unmap;
    394		}
    395
    396		at91_shdwc->mpddrc_base = of_iomap(np, 0);
    397		of_node_put(np);
    398
    399		if (!at91_shdwc->mpddrc_base) {
    400			ret = -ENOMEM;
    401			goto unmap;
    402		}
    403
    404		ddr_type = readl(at91_shdwc->mpddrc_base +
    405				 at91_shdwc->rcfg->ddrc.type_offset) &
    406				 at91_shdwc->rcfg->ddrc.type_mask;
    407		if (ddr_type != AT91_DDRSDRC_MD_LPDDR2 &&
    408		    ddr_type != AT91_DDRSDRC_MD_LPDDR3) {
    409			iounmap(at91_shdwc->mpddrc_base);
    410			at91_shdwc->mpddrc_base = NULL;
    411		}
    412	}
    413
    414	pm_power_off = at91_poweroff;
    415
    416	return 0;
    417
    418unmap:
    419	iounmap(at91_shdwc->pmc_base);
    420clk_disable:
    421	clk_disable_unprepare(at91_shdwc->sclk);
    422
    423	return ret;
    424}
    425
    426static int __exit at91_shdwc_remove(struct platform_device *pdev)
    427{
    428	struct shdwc *shdw = platform_get_drvdata(pdev);
    429
    430	if (pm_power_off == at91_poweroff)
    431		pm_power_off = NULL;
    432
    433	/* Reset values to disable wake-up features  */
    434	writel(0, shdw->shdwc_base + AT91_SHDW_MR);
    435	writel(0, shdw->shdwc_base + AT91_SHDW_WUIR);
    436
    437	if (shdw->mpddrc_base)
    438		iounmap(shdw->mpddrc_base);
    439	iounmap(shdw->pmc_base);
    440
    441	clk_disable_unprepare(shdw->sclk);
    442
    443	return 0;
    444}
    445
    446static struct platform_driver at91_shdwc_driver = {
    447	.remove = __exit_p(at91_shdwc_remove),
    448	.driver = {
    449		.name = "at91-shdwc",
    450		.of_match_table = at91_shdwc_of_match,
    451	},
    452};
    453module_platform_driver_probe(at91_shdwc_driver, at91_shdwc_probe);
    454
    455MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>");
    456MODULE_DESCRIPTION("Atmel shutdown controller driver");
    457MODULE_LICENSE("GPL v2");