1 /* 2 * linux/arch/arm/mach-pxa/mfp.c 3 * 4 * PXA3xx Multi-Function Pin Support 5 * 6 * Copyright (C) 2007 Marvell Internation Ltd. 7 * 8 * 2007-08-21: eric miao <eric.miao@marvell.com> 9 * initial version 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License version 2 as 13 * published by the Free Software Foundation. 14 */ 15 16 #include <linux/module.h> 17 #include <linux/kernel.h> 18 #include <linux/init.h> 19 #include <linux/io.h> 20 #include <linux/sysdev.h> 21 22 #include <asm/hardware.h> 23 #include <asm/arch/mfp.h> 24 #include <asm/arch/mfp-pxa3xx.h> 25 #include <asm/arch/pxa3xx-regs.h> 26 27 /* mfp_spin_lock is used to ensure that MFP register configuration 28 * (most likely a read-modify-write operation) is atomic, and that 29 * mfp_table[] is consistent 30 */ 31 static DEFINE_SPINLOCK(mfp_spin_lock); 32 33 static void __iomem *mfpr_mmio_base = (void __iomem *)&__REG(MFPR_BASE); 34 35 struct pxa3xx_mfp_pin { 36 unsigned long config; /* -1 for not configured */ 37 unsigned long mfpr_off; /* MFPRxx Register offset */ 38 unsigned long mfpr_run; /* Run-Mode Register Value */ 39 unsigned long mfpr_lpm; /* Low Power Mode Register Value */ 40 }; 41 42 static struct pxa3xx_mfp_pin mfp_table[MFP_PIN_MAX]; 43 44 /* mapping of MFP_LPM_* definitions to MFPR_LPM_* register bits */ 45 static const unsigned long mfpr_lpm[] = { 46 MFPR_LPM_INPUT, 47 MFPR_LPM_DRIVE_LOW, 48 MFPR_LPM_DRIVE_HIGH, 49 MFPR_LPM_PULL_LOW, 50 MFPR_LPM_PULL_HIGH, 51 MFPR_LPM_FLOAT, 52 }; 53 54 /* mapping of MFP_PULL_* definitions to MFPR_PULL_* register bits */ 55 static const unsigned long mfpr_pull[] = { 56 MFPR_PULL_NONE, 57 MFPR_PULL_LOW, 58 MFPR_PULL_HIGH, 59 MFPR_PULL_BOTH, 60 }; 61 62 /* mapping of MFP_LPM_EDGE_* definitions to MFPR_EDGE_* register bits */ 63 static const unsigned long mfpr_edge[] = { 64 MFPR_EDGE_NONE, 65 MFPR_EDGE_RISE, 66 MFPR_EDGE_FALL, 67 MFPR_EDGE_BOTH, 68 }; 69 70 #define mfpr_readl(off) \ 71 __raw_readl(mfpr_mmio_base + (off)) 72 73 #define mfpr_writel(off, val) \ 74 __raw_writel(val, mfpr_mmio_base + (off)) 75 76 #define mfp_configured(p) ((p)->config != -1) 77 78 /* 79 * perform a read-back of any MFPR register to make sure the 80 * previous writings are finished 81 */ 82 #define mfpr_sync() (void)__raw_readl(mfpr_mmio_base + 0) 83 84 static inline void __mfp_config_run(struct pxa3xx_mfp_pin *p) 85 { 86 if (mfp_configured(p)) 87 mfpr_writel(p->mfpr_off, p->mfpr_run); 88 } 89 90 static inline void __mfp_config_lpm(struct pxa3xx_mfp_pin *p) 91 { 92 if (mfp_configured(p)) { 93 unsigned long mfpr_clr = (p->mfpr_run & ~MFPR_EDGE_BOTH) | MFPR_EDGE_CLEAR; 94 if (mfpr_clr != p->mfpr_run) 95 mfpr_writel(p->mfpr_off, mfpr_clr); 96 if (p->mfpr_lpm != mfpr_clr) 97 mfpr_writel(p->mfpr_off, p->mfpr_lpm); 98 } 99 } 100 101 void pxa3xx_mfp_config(unsigned long *mfp_cfgs, int num) 102 { 103 unsigned long flags; 104 int i; 105 106 spin_lock_irqsave(&mfp_spin_lock, flags); 107 108 for (i = 0; i < num; i++, mfp_cfgs++) { 109 unsigned long tmp, c = *mfp_cfgs; 110 struct pxa3xx_mfp_pin *p; 111 int pin, af, drv, lpm, edge, pull; 112 113 pin = MFP_PIN(c); 114 BUG_ON(pin >= MFP_PIN_MAX); 115 p = &mfp_table[pin]; 116 117 af = MFP_AF(c); 118 drv = MFP_DS(c); 119 lpm = MFP_LPM_STATE(c); 120 edge = MFP_LPM_EDGE(c); 121 pull = MFP_PULL(c); 122 123 /* run-mode pull settings will conflict with MFPR bits of 124 * low power mode state, calculate mfpr_run and mfpr_lpm 125 * individually if pull != MFP_PULL_NONE 126 */ 127 tmp = MFPR_AF_SEL(af) | MFPR_DRIVE(drv); 128 129 if (likely(pull == MFP_PULL_NONE)) { 130 p->mfpr_run = tmp | mfpr_lpm[lpm] | mfpr_edge[edge]; 131 p->mfpr_lpm = p->mfpr_run; 132 } else { 133 p->mfpr_lpm = tmp | mfpr_lpm[lpm] | mfpr_edge[edge]; 134 p->mfpr_run = tmp | mfpr_pull[pull]; 135 } 136 137 p->config = c; __mfp_config_run(p); 138 } 139 140 mfpr_sync(); 141 spin_unlock_irqrestore(&mfp_spin_lock, flags); 142 } 143 144 unsigned long pxa3xx_mfp_read(int mfp) 145 { 146 unsigned long val, flags; 147 148 BUG_ON(mfp >= MFP_PIN_MAX); 149 150 spin_lock_irqsave(&mfp_spin_lock, flags); 151 val = mfpr_readl(mfp_table[mfp].mfpr_off); 152 spin_unlock_irqrestore(&mfp_spin_lock, flags); 153 154 return val; 155 } 156 157 void pxa3xx_mfp_write(int mfp, unsigned long val) 158 { 159 unsigned long flags; 160 161 BUG_ON(mfp >= MFP_PIN_MAX); 162 163 spin_lock_irqsave(&mfp_spin_lock, flags); 164 mfpr_writel(mfp_table[mfp].mfpr_off, val); 165 mfpr_sync(); 166 spin_unlock_irqrestore(&mfp_spin_lock, flags); 167 } 168 169 void __init pxa3xx_mfp_init_addr(struct pxa3xx_mfp_addr_map *map) 170 { 171 struct pxa3xx_mfp_addr_map *p; 172 unsigned long offset, flags; 173 int i; 174 175 spin_lock_irqsave(&mfp_spin_lock, flags); 176 177 for (p = map; p->start != MFP_PIN_INVALID; p++) { 178 offset = p->offset; 179 i = p->start; 180 181 do { 182 mfp_table[i].mfpr_off = offset; 183 mfp_table[i].mfpr_run = 0; 184 mfp_table[i].mfpr_lpm = 0; 185 offset += 4; i++; 186 } while ((i <= p->end) && (p->end != -1)); 187 } 188 189 spin_unlock_irqrestore(&mfp_spin_lock, flags); 190 } 191 192 void __init pxa3xx_init_mfp(void) 193 { 194 int i; 195 196 for (i = 0; i < ARRAY_SIZE(mfp_table); i++) 197 mfp_table[i].config = -1; 198 } 199 200 #ifdef CONFIG_PM 201 /* 202 * Configure the MFPs appropriately for suspend/resume. 203 * FIXME: this should probably depend on which system state we're 204 * entering - for instance, we might not want to place MFP pins in 205 * a pull-down mode if they're an active low chip select, and we're 206 * just entering standby. 207 */ 208 static int pxa3xx_mfp_suspend(struct sys_device *d, pm_message_t state) 209 { 210 int pin; 211 212 for (pin = 0; pin < ARRAY_SIZE(mfp_table); pin++) { 213 struct pxa3xx_mfp_pin *p = &mfp_table[pin]; 214 __mfp_config_lpm(p); 215 } 216 return 0; 217 } 218 219 static int pxa3xx_mfp_resume(struct sys_device *d) 220 { 221 int pin; 222 223 for (pin = 0; pin < ARRAY_SIZE(mfp_table); pin++) { 224 struct pxa3xx_mfp_pin *p = &mfp_table[pin]; 225 __mfp_config_run(p); 226 } 227 228 /* clear RDH bit when MFP settings are restored 229 * 230 * NOTE: the last 3 bits DxS are write-1-to-clear so carefully 231 * preserve them here in case they will be referenced later 232 */ 233 ASCR &= ~(ASCR_RDH | ASCR_D1S | ASCR_D2S | ASCR_D3S); 234 235 return 0; 236 } 237 #else 238 #define pxa3xx_mfp_suspend NULL 239 #define pxa3xx_mfp_resume NULL 240 #endif 241 242 struct sysdev_class pxa3xx_mfp_sysclass = { 243 .name = "mfp", 244 .suspend = pxa3xx_mfp_suspend, 245 .resume = pxa3xx_mfp_resume, 246 }; 247 248 static int __init mfp_init_devicefs(void) 249 { 250 if (cpu_is_pxa3xx()) 251 return sysdev_class_register(&pxa3xx_mfp_sysclass); 252 253 return 0; 254 } 255 postcore_initcall(mfp_init_devicefs); 256