1/* 2 * linux/arch/arm/plat-pxa/mfp.c 3 * 4 * 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 <plat/mfp.h> 23 24#define MFPR_SIZE (PAGE_SIZE) 25 26/* MFPR register bit definitions */ 27#define MFPR_PULL_SEL (0x1 << 15) 28#define MFPR_PULLUP_EN (0x1 << 14) 29#define MFPR_PULLDOWN_EN (0x1 << 13) 30#define MFPR_SLEEP_SEL (0x1 << 9) 31#define MFPR_SLEEP_OE_N (0x1 << 7) 32#define MFPR_EDGE_CLEAR (0x1 << 6) 33#define MFPR_EDGE_FALL_EN (0x1 << 5) 34#define MFPR_EDGE_RISE_EN (0x1 << 4) 35 36#define MFPR_SLEEP_DATA(x) ((x) << 8) 37#define MFPR_DRIVE(x) (((x) & 0x7) << 10) 38#define MFPR_AF_SEL(x) (((x) & 0x7) << 0) 39 40#define MFPR_EDGE_NONE (0) 41#define MFPR_EDGE_RISE (MFPR_EDGE_RISE_EN) 42#define MFPR_EDGE_FALL (MFPR_EDGE_FALL_EN) 43#define MFPR_EDGE_BOTH (MFPR_EDGE_RISE | MFPR_EDGE_FALL) 44 45/* 46 * Table that determines the low power modes outputs, with actual settings 47 * used in parentheses for don't-care values. Except for the float output, 48 * the configured driven and pulled levels match, so if there is a need for 49 * non-LPM pulled output, the same configuration could probably be used. 50 * 51 * Output value sleep_oe_n sleep_data pullup_en pulldown_en pull_sel 52 * (bit 7) (bit 8) (bit 14) (bit 13) (bit 15) 53 * 54 * Input 0 X(0) X(0) X(0) 0 55 * Drive 0 0 0 0 X(1) 0 56 * Drive 1 0 1 X(1) 0 0 57 * Pull hi (1) 1 X(1) 1 0 0 58 * Pull lo (0) 1 X(0) 0 1 0 59 * Z (float) 1 X(0) 0 0 0 60 */ 61#define MFPR_LPM_INPUT (0) 62#define MFPR_LPM_DRIVE_LOW (MFPR_SLEEP_DATA(0) | MFPR_PULLDOWN_EN) 63#define MFPR_LPM_DRIVE_HIGH (MFPR_SLEEP_DATA(1) | MFPR_PULLUP_EN) 64#define MFPR_LPM_PULL_LOW (MFPR_LPM_DRIVE_LOW | MFPR_SLEEP_OE_N) 65#define MFPR_LPM_PULL_HIGH (MFPR_LPM_DRIVE_HIGH | MFPR_SLEEP_OE_N) 66#define MFPR_LPM_FLOAT (MFPR_SLEEP_OE_N) 67#define MFPR_LPM_MASK (0xe080) 68 69/* 70 * The pullup and pulldown state of the MFP pin at run mode is by default 71 * determined by the selected alternate function. In case that some buggy 72 * devices need to override this default behavior, the definitions below 73 * indicates the setting of corresponding MFPR bits 74 * 75 * Definition pull_sel pullup_en pulldown_en 76 * MFPR_PULL_NONE 0 0 0 77 * MFPR_PULL_LOW 1 0 1 78 * MFPR_PULL_HIGH 1 1 0 79 * MFPR_PULL_BOTH 1 1 1 80 * MFPR_PULL_FLOAT 1 0 0 81 */ 82#define MFPR_PULL_NONE (0) 83#define MFPR_PULL_LOW (MFPR_PULL_SEL | MFPR_PULLDOWN_EN) 84#define MFPR_PULL_BOTH (MFPR_PULL_LOW | MFPR_PULLUP_EN) 85#define MFPR_PULL_HIGH (MFPR_PULL_SEL | MFPR_PULLUP_EN) 86#define MFPR_PULL_FLOAT (MFPR_PULL_SEL) 87 88/* mfp_spin_lock is used to ensure that MFP register configuration 89 * (most likely a read-modify-write operation) is atomic, and that 90 * mfp_table[] is consistent 91 */ 92static DEFINE_SPINLOCK(mfp_spin_lock); 93 94static void __iomem *mfpr_mmio_base; 95 96struct mfp_pin { 97 unsigned long config; /* -1 for not configured */ 98 unsigned long mfpr_off; /* MFPRxx Register offset */ 99 unsigned long mfpr_run; /* Run-Mode Register Value */ 100 unsigned long mfpr_lpm; /* Low Power Mode Register Value */ 101}; 102 103static struct mfp_pin mfp_table[MFP_PIN_MAX]; 104 105/* mapping of MFP_LPM_* definitions to MFPR_LPM_* register bits */ 106static const unsigned long mfpr_lpm[] = { 107 MFPR_LPM_INPUT, 108 MFPR_LPM_DRIVE_LOW, 109 MFPR_LPM_DRIVE_HIGH, 110 MFPR_LPM_PULL_LOW, 111 MFPR_LPM_PULL_HIGH, 112 MFPR_LPM_FLOAT, 113 MFPR_LPM_INPUT, 114}; 115 116/* mapping of MFP_PULL_* definitions to MFPR_PULL_* register bits */ 117static const unsigned long mfpr_pull[] = { 118 MFPR_PULL_NONE, 119 MFPR_PULL_LOW, 120 MFPR_PULL_HIGH, 121 MFPR_PULL_BOTH, 122 MFPR_PULL_FLOAT, 123}; 124 125/* mapping of MFP_LPM_EDGE_* definitions to MFPR_EDGE_* register bits */ 126static const unsigned long mfpr_edge[] = { 127 MFPR_EDGE_NONE, 128 MFPR_EDGE_RISE, 129 MFPR_EDGE_FALL, 130 MFPR_EDGE_BOTH, 131}; 132 133#define mfpr_readl(off) \ 134 __raw_readl(mfpr_mmio_base + (off)) 135 136#define mfpr_writel(off, val) \ 137 __raw_writel(val, mfpr_mmio_base + (off)) 138 139#define mfp_configured(p) ((p)->config != -1) 140 141/* 142 * perform a read-back of any MFPR register to make sure the 143 * previous writings are finished 144 */ 145#define mfpr_sync() (void)__raw_readl(mfpr_mmio_base + 0) 146 147static inline void __mfp_config_run(struct mfp_pin *p) 148{ 149 if (mfp_configured(p)) 150 mfpr_writel(p->mfpr_off, p->mfpr_run); 151} 152 153static inline void __mfp_config_lpm(struct mfp_pin *p) 154{ 155 if (mfp_configured(p)) { 156 unsigned long mfpr_clr = (p->mfpr_run & ~MFPR_EDGE_BOTH) | MFPR_EDGE_CLEAR; 157 if (mfpr_clr != p->mfpr_run) 158 mfpr_writel(p->mfpr_off, mfpr_clr); 159 if (p->mfpr_lpm != mfpr_clr) 160 mfpr_writel(p->mfpr_off, p->mfpr_lpm); 161 } 162} 163 164void mfp_config(unsigned long *mfp_cfgs, int num) 165{ 166 unsigned long flags; 167 int i; 168 169 spin_lock_irqsave(&mfp_spin_lock, flags); 170 171 for (i = 0; i < num; i++, mfp_cfgs++) { 172 unsigned long tmp, c = *mfp_cfgs; 173 struct mfp_pin *p; 174 int pin, af, drv, lpm, edge, pull; 175 176 pin = MFP_PIN(c); 177 BUG_ON(pin >= MFP_PIN_MAX); 178 p = &mfp_table[pin]; 179 180 af = MFP_AF(c); 181 drv = MFP_DS(c); 182 lpm = MFP_LPM_STATE(c); 183 edge = MFP_LPM_EDGE(c); 184 pull = MFP_PULL(c); 185 186 /* run-mode pull settings will conflict with MFPR bits of 187 * low power mode state, calculate mfpr_run and mfpr_lpm 188 * individually if pull != MFP_PULL_NONE 189 */ 190 tmp = MFPR_AF_SEL(af) | MFPR_DRIVE(drv); 191 192 if (likely(pull == MFP_PULL_NONE)) { 193 p->mfpr_run = tmp | mfpr_lpm[lpm] | mfpr_edge[edge]; 194 p->mfpr_lpm = p->mfpr_run; 195 } else { 196 p->mfpr_lpm = tmp | mfpr_lpm[lpm] | mfpr_edge[edge]; 197 p->mfpr_run = tmp | mfpr_pull[pull]; 198 } 199 200 p->config = c; __mfp_config_run(p); 201 } 202 203 mfpr_sync(); 204 spin_unlock_irqrestore(&mfp_spin_lock, flags); 205} 206 207unsigned long mfp_read(int mfp) 208{ 209 unsigned long val, flags; 210 211 BUG_ON(mfp < 0 || mfp >= MFP_PIN_MAX); 212 213 spin_lock_irqsave(&mfp_spin_lock, flags); 214 val = mfpr_readl(mfp_table[mfp].mfpr_off); 215 spin_unlock_irqrestore(&mfp_spin_lock, flags); 216 217 return val; 218} 219 220void mfp_write(int mfp, unsigned long val) 221{ 222 unsigned long flags; 223 224 BUG_ON(mfp < 0 || mfp >= MFP_PIN_MAX); 225 226 spin_lock_irqsave(&mfp_spin_lock, flags); 227 mfpr_writel(mfp_table[mfp].mfpr_off, val); 228 mfpr_sync(); 229 spin_unlock_irqrestore(&mfp_spin_lock, flags); 230} 231 232void __init mfp_init_base(unsigned long mfpr_base) 233{ 234 int i; 235 236 /* initialize the table with default - unconfigured */ 237 for (i = 0; i < ARRAY_SIZE(mfp_table); i++) 238 mfp_table[i].config = -1; 239 240 mfpr_mmio_base = (void __iomem *)mfpr_base; 241} 242 243void __init mfp_init_addr(struct mfp_addr_map *map) 244{ 245 struct mfp_addr_map *p; 246 unsigned long offset, flags; 247 int i; 248 249 spin_lock_irqsave(&mfp_spin_lock, flags); 250 251 for (p = map; p->start != MFP_PIN_INVALID; p++) { 252 offset = p->offset; 253 i = p->start; 254 255 do { 256 mfp_table[i].mfpr_off = offset; 257 mfp_table[i].mfpr_run = 0; 258 mfp_table[i].mfpr_lpm = 0; 259 offset += 4; i++; 260 } while ((i <= p->end) && (p->end != -1)); 261 } 262 263 spin_unlock_irqrestore(&mfp_spin_lock, flags); 264} 265 266void mfp_config_lpm(void) 267{ 268 struct mfp_pin *p = &mfp_table[0]; 269 int pin; 270 271 for (pin = 0; pin < ARRAY_SIZE(mfp_table); pin++, p++) 272 __mfp_config_lpm(p); 273} 274 275void mfp_config_run(void) 276{ 277 struct mfp_pin *p = &mfp_table[0]; 278 int pin; 279 280 for (pin = 0; pin < ARRAY_SIZE(mfp_table); pin++, p++) 281 __mfp_config_run(p); 282} 283