1/* 2 * R-Car SYSC Power management support 3 * 4 * Copyright (C) 2014 Magnus Damm 5 * 6 * This file is subject to the terms and conditions of the GNU General Public 7 * License. See the file "COPYING" in the main directory of this archive 8 * for more details. 9 */ 10 11#include <linux/delay.h> 12#include <linux/err.h> 13#include <linux/mm.h> 14#include <linux/spinlock.h> 15#include <asm/io.h> 16#include "pm-rcar.h" 17 18/* SYSC */ 19#define SYSCSR 0x00 20#define SYSCISR 0x04 21#define SYSCISCR 0x08 22 23#define PWRSR_OFFS 0x00 24#define PWROFFCR_OFFS 0x04 25#define PWRONCR_OFFS 0x0c 26#define PWRER_OFFS 0x14 27 28#define SYSCSR_RETRIES 100 29#define SYSCSR_DELAY_US 1 30 31#define SYSCISR_RETRIES 1000 32#define SYSCISR_DELAY_US 1 33 34static void __iomem *rcar_sysc_base; 35static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ 36 37static int rcar_sysc_pwr_on_off(struct rcar_sysc_ch *sysc_ch, 38 int sr_bit, int reg_offs) 39{ 40 int k; 41 42 for (k = 0; k < SYSCSR_RETRIES; k++) { 43 if (ioread32(rcar_sysc_base + SYSCSR) & (1 << sr_bit)) 44 break; 45 udelay(SYSCSR_DELAY_US); 46 } 47 48 if (k == SYSCSR_RETRIES) 49 return -EAGAIN; 50 51 iowrite32(1 << sysc_ch->chan_bit, 52 rcar_sysc_base + sysc_ch->chan_offs + reg_offs); 53 54 return 0; 55} 56 57static int rcar_sysc_pwr_off(struct rcar_sysc_ch *sysc_ch) 58{ 59 return rcar_sysc_pwr_on_off(sysc_ch, 0, PWROFFCR_OFFS); 60} 61 62static int rcar_sysc_pwr_on(struct rcar_sysc_ch *sysc_ch) 63{ 64 return rcar_sysc_pwr_on_off(sysc_ch, 1, PWRONCR_OFFS); 65} 66 67static int rcar_sysc_update(struct rcar_sysc_ch *sysc_ch, 68 int (*on_off_fn)(struct rcar_sysc_ch *)) 69{ 70 unsigned int isr_mask = 1 << sysc_ch->isr_bit; 71 unsigned int chan_mask = 1 << sysc_ch->chan_bit; 72 unsigned int status; 73 unsigned long flags; 74 int ret = 0; 75 int k; 76 77 spin_lock_irqsave(&rcar_sysc_lock, flags); 78 79 iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); 80 81 do { 82 ret = on_off_fn(sysc_ch); 83 if (ret) 84 goto out; 85 86 status = ioread32(rcar_sysc_base + 87 sysc_ch->chan_offs + PWRER_OFFS); 88 } while (status & chan_mask); 89 90 for (k = 0; k < SYSCISR_RETRIES; k++) { 91 if (ioread32(rcar_sysc_base + SYSCISR) & isr_mask) 92 break; 93 udelay(SYSCISR_DELAY_US); 94 } 95 96 if (k == SYSCISR_RETRIES) 97 ret = -EIO; 98 99 iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); 100 101 out: 102 spin_unlock_irqrestore(&rcar_sysc_lock, flags); 103 104 pr_debug("sysc power domain %d: %08x -> %d\n", 105 sysc_ch->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret); 106 return ret; 107} 108 109int rcar_sysc_power_down(struct rcar_sysc_ch *sysc_ch) 110{ 111 return rcar_sysc_update(sysc_ch, rcar_sysc_pwr_off); 112} 113 114int rcar_sysc_power_up(struct rcar_sysc_ch *sysc_ch) 115{ 116 return rcar_sysc_update(sysc_ch, rcar_sysc_pwr_on); 117} 118 119bool rcar_sysc_power_is_off(struct rcar_sysc_ch *sysc_ch) 120{ 121 unsigned int st; 122 123 st = ioread32(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS); 124 if (st & (1 << sysc_ch->chan_bit)) 125 return true; 126 127 return false; 128} 129 130void __iomem *rcar_sysc_init(phys_addr_t base) 131{ 132 rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE); 133 if (!rcar_sysc_base) 134 panic("unable to ioremap R-Car SYSC hardware block\n"); 135 136 return rcar_sysc_base; 137} 138