• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
34 static void __iomem *rcar_sysc_base;
35 static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */
36 
rcar_sysc_pwr_on_off(struct rcar_sysc_ch * sysc_ch,int sr_bit,int reg_offs)37 static 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 
rcar_sysc_pwr_off(struct rcar_sysc_ch * sysc_ch)57 static 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 
rcar_sysc_pwr_on(struct rcar_sysc_ch * sysc_ch)62 static 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 
rcar_sysc_update(struct rcar_sysc_ch * sysc_ch,int (* on_off_fn)(struct rcar_sysc_ch *))67 static 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 
rcar_sysc_power_down(struct rcar_sysc_ch * sysc_ch)109 int rcar_sysc_power_down(struct rcar_sysc_ch *sysc_ch)
110 {
111 	return rcar_sysc_update(sysc_ch, rcar_sysc_pwr_off);
112 }
113 
rcar_sysc_power_up(struct rcar_sysc_ch * sysc_ch)114 int rcar_sysc_power_up(struct rcar_sysc_ch *sysc_ch)
115 {
116 	return rcar_sysc_update(sysc_ch, rcar_sysc_pwr_on);
117 }
118 
rcar_sysc_power_is_off(struct rcar_sysc_ch * sysc_ch)119 bool 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 
rcar_sysc_init(phys_addr_t base)130 void __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