Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Atmel AT91 SAM9 SoCs reset code
0003  *
0004  * Copyright (C) 2007 Atmel Corporation.
0005  * Copyright (C) 2011 Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
0006  * Copyright (C) 2014 Free Electrons
0007  *
0008  * This file is licensed under the terms of the GNU General Public
0009  * License version 2.  This program is licensed "as is" without any
0010  * warranty of any kind, whether express or implied.
0011  */
0012 
0013 #include <linux/clk.h>
0014 #include <linux/io.h>
0015 #include <linux/module.h>
0016 #include <linux/of.h>
0017 #include <linux/of_address.h>
0018 #include <linux/platform_device.h>
0019 #include <linux/printk.h>
0020 
0021 #include <soc/at91/at91sam9_ddrsdr.h>
0022 
0023 #define AT91_SHDW_CR    0x00        /* Shut Down Control Register */
0024 #define AT91_SHDW_SHDW      BIT(0)          /* Shut Down command */
0025 #define AT91_SHDW_KEY       (0xa5 << 24)        /* KEY Password */
0026 
0027 #define AT91_SHDW_MR    0x04        /* Shut Down Mode Register */
0028 #define AT91_SHDW_WKMODE0   GENMASK(2, 0)       /* Wake-up 0 Mode Selection */
0029 #define AT91_SHDW_CPTWK0_MAX    0xf         /* Maximum Counter On Wake Up 0 */
0030 #define AT91_SHDW_CPTWK0    (AT91_SHDW_CPTWK0_MAX << 4) /* Counter On Wake Up 0 */
0031 #define AT91_SHDW_CPTWK0_(x)    ((x) << 4)
0032 #define AT91_SHDW_RTTWKEN   BIT(16)         /* Real Time Timer Wake-up Enable */
0033 #define AT91_SHDW_RTCWKEN   BIT(17)         /* Real Time Clock Wake-up Enable */
0034 
0035 #define AT91_SHDW_SR    0x08        /* Shut Down Status Register */
0036 #define AT91_SHDW_WAKEUP0   BIT(0)          /* Wake-up 0 Status */
0037 #define AT91_SHDW_RTTWK     BIT(16)         /* Real-time Timer Wake-up */
0038 #define AT91_SHDW_RTCWK     BIT(17)         /* Real-time Clock Wake-up [SAM9RL] */
0039 
0040 enum wakeup_type {
0041     AT91_SHDW_WKMODE0_NONE      = 0,
0042     AT91_SHDW_WKMODE0_HIGH      = 1,
0043     AT91_SHDW_WKMODE0_LOW       = 2,
0044     AT91_SHDW_WKMODE0_ANYLEVEL  = 3,
0045 };
0046 
0047 static const char *shdwc_wakeup_modes[] = {
0048     [AT91_SHDW_WKMODE0_NONE]    = "none",
0049     [AT91_SHDW_WKMODE0_HIGH]    = "high",
0050     [AT91_SHDW_WKMODE0_LOW]     = "low",
0051     [AT91_SHDW_WKMODE0_ANYLEVEL]    = "any",
0052 };
0053 
0054 static struct shdwc {
0055     struct clk *sclk;
0056     void __iomem *shdwc_base;
0057     void __iomem *mpddrc_base;
0058 } at91_shdwc;
0059 
0060 static void __init at91_wakeup_status(struct platform_device *pdev)
0061 {
0062     const char *reason;
0063     u32 reg = readl(at91_shdwc.shdwc_base + AT91_SHDW_SR);
0064 
0065     /* Simple power-on, just bail out */
0066     if (!reg)
0067         return;
0068 
0069     if (reg & AT91_SHDW_RTTWK)
0070         reason = "RTT";
0071     else if (reg & AT91_SHDW_RTCWK)
0072         reason = "RTC";
0073     else
0074         reason = "unknown";
0075 
0076     dev_info(&pdev->dev, "Wake-Up source: %s\n", reason);
0077 }
0078 
0079 static void at91_poweroff(void)
0080 {
0081     asm volatile(
0082         /* Align to cache lines */
0083         ".balign 32\n\t"
0084 
0085         /* Ensure AT91_SHDW_CR is in the TLB by reading it */
0086         "   ldr r6, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
0087 
0088         /* Power down SDRAM0 */
0089         "   tst %0, #0\n\t"
0090         "   beq 1f\n\t"
0091         "   str %1, [%0, #" __stringify(AT91_DDRSDRC_LPR) "]\n\t"
0092         /* Shutdown CPU */
0093         "1: str %3, [%2, #" __stringify(AT91_SHDW_CR) "]\n\t"
0094 
0095         "   b   .\n\t"
0096         :
0097         : "r" (at91_shdwc.mpddrc_base),
0098           "r" cpu_to_le32(AT91_DDRSDRC_LPDDR2_PWOFF),
0099           "r" (at91_shdwc.shdwc_base),
0100           "r" cpu_to_le32(AT91_SHDW_KEY | AT91_SHDW_SHDW)
0101         : "r6");
0102 }
0103 
0104 static int at91_poweroff_get_wakeup_mode(struct device_node *np)
0105 {
0106     const char *pm;
0107     unsigned int i;
0108     int err;
0109 
0110     err = of_property_read_string(np, "atmel,wakeup-mode", &pm);
0111     if (err < 0)
0112         return AT91_SHDW_WKMODE0_ANYLEVEL;
0113 
0114     for (i = 0; i < ARRAY_SIZE(shdwc_wakeup_modes); i++)
0115         if (!strcasecmp(pm, shdwc_wakeup_modes[i]))
0116             return i;
0117 
0118     return -ENODEV;
0119 }
0120 
0121 static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
0122 {
0123     struct device_node *np = pdev->dev.of_node;
0124     int wakeup_mode;
0125     u32 mode = 0, tmp;
0126 
0127     wakeup_mode = at91_poweroff_get_wakeup_mode(np);
0128     if (wakeup_mode < 0) {
0129         dev_warn(&pdev->dev, "shdwc unknown wakeup mode\n");
0130         return;
0131     }
0132 
0133     if (!of_property_read_u32(np, "atmel,wakeup-counter", &tmp)) {
0134         if (tmp > AT91_SHDW_CPTWK0_MAX) {
0135             dev_warn(&pdev->dev,
0136                  "shdwc wakeup counter 0x%x > 0x%x reduce it to 0x%x\n",
0137                  tmp, AT91_SHDW_CPTWK0_MAX, AT91_SHDW_CPTWK0_MAX);
0138             tmp = AT91_SHDW_CPTWK0_MAX;
0139         }
0140         mode |= AT91_SHDW_CPTWK0_(tmp);
0141     }
0142 
0143     if (of_property_read_bool(np, "atmel,wakeup-rtc-timer"))
0144             mode |= AT91_SHDW_RTCWKEN;
0145 
0146     if (of_property_read_bool(np, "atmel,wakeup-rtt-timer"))
0147             mode |= AT91_SHDW_RTTWKEN;
0148 
0149     writel(wakeup_mode | mode, at91_shdwc.shdwc_base + AT91_SHDW_MR);
0150 }
0151 
0152 static int __init at91_poweroff_probe(struct platform_device *pdev)
0153 {
0154     struct resource *res;
0155     struct device_node *np;
0156     u32 ddr_type;
0157     int ret;
0158 
0159     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0160     at91_shdwc.shdwc_base = devm_ioremap_resource(&pdev->dev, res);
0161     if (IS_ERR(at91_shdwc.shdwc_base))
0162         return PTR_ERR(at91_shdwc.shdwc_base);
0163 
0164     at91_shdwc.sclk = devm_clk_get(&pdev->dev, NULL);
0165     if (IS_ERR(at91_shdwc.sclk))
0166         return PTR_ERR(at91_shdwc.sclk);
0167 
0168     ret = clk_prepare_enable(at91_shdwc.sclk);
0169     if (ret) {
0170         dev_err(&pdev->dev, "Could not enable slow clock\n");
0171         return ret;
0172     }
0173 
0174     at91_wakeup_status(pdev);
0175 
0176     if (pdev->dev.of_node)
0177         at91_poweroff_dt_set_wakeup_mode(pdev);
0178 
0179     np = of_find_compatible_node(NULL, NULL, "atmel,sama5d3-ddramc");
0180     if (np) {
0181         at91_shdwc.mpddrc_base = of_iomap(np, 0);
0182         of_node_put(np);
0183 
0184         if (!at91_shdwc.mpddrc_base) {
0185             ret = -ENOMEM;
0186             goto clk_disable;
0187         }
0188 
0189         ddr_type = readl(at91_shdwc.mpddrc_base + AT91_DDRSDRC_MDR) &
0190                  AT91_DDRSDRC_MD;
0191         if (ddr_type != AT91_DDRSDRC_MD_LPDDR2 &&
0192             ddr_type != AT91_DDRSDRC_MD_LPDDR3) {
0193             iounmap(at91_shdwc.mpddrc_base);
0194             at91_shdwc.mpddrc_base = NULL;
0195         }
0196     }
0197 
0198     pm_power_off = at91_poweroff;
0199 
0200     return 0;
0201 
0202 clk_disable:
0203     clk_disable_unprepare(at91_shdwc.sclk);
0204     return ret;
0205 }
0206 
0207 static int __exit at91_poweroff_remove(struct platform_device *pdev)
0208 {
0209     if (pm_power_off == at91_poweroff)
0210         pm_power_off = NULL;
0211 
0212     if (at91_shdwc.mpddrc_base)
0213         iounmap(at91_shdwc.mpddrc_base);
0214 
0215     clk_disable_unprepare(at91_shdwc.sclk);
0216 
0217     return 0;
0218 }
0219 
0220 static const struct of_device_id at91_poweroff_of_match[] = {
0221     { .compatible = "atmel,at91sam9260-shdwc", },
0222     { .compatible = "atmel,at91sam9rl-shdwc", },
0223     { .compatible = "atmel,at91sam9x5-shdwc", },
0224     { /*sentinel*/ }
0225 };
0226 MODULE_DEVICE_TABLE(of, at91_poweroff_of_match);
0227 
0228 static struct platform_driver at91_poweroff_driver = {
0229     .remove = __exit_p(at91_poweroff_remove),
0230     .driver = {
0231         .name = "at91-poweroff",
0232         .of_match_table = at91_poweroff_of_match,
0233     },
0234 };
0235 module_platform_driver_probe(at91_poweroff_driver, at91_poweroff_probe);
0236 
0237 MODULE_AUTHOR("Atmel Corporation");
0238 MODULE_DESCRIPTION("Shutdown driver for Atmel SoCs");
0239 MODULE_LICENSE("GPL v2");