mfp.c 4.46 KB
Newer Older
eric miao's avatar
eric miao committed
1 2 3 4 5 6 7
/*
 * linux/arch/arm/mach-pxa/mfp.c
 *
 * PXA3xx Multi-Function Pin Support
 *
 * Copyright (C) 2007 Marvell Internation Ltd.
 *
8
 * 2007-08-21: eric miao <eric.miao@marvell.com>
eric miao's avatar
eric miao committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22
 *             initial version
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 */

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>

#include <asm/hardware.h>
#include <asm/arch/mfp.h>
23
#include <asm/arch/mfp-pxa3xx.h>
eric miao's avatar
eric miao committed
24 25 26 27 28 29 30 31

/* mfp_spin_lock is used to ensure that MFP register configuration
 * (most likely a read-modify-write operation) is atomic, and that
 * mfp_table[] is consistent
 */
static DEFINE_SPINLOCK(mfp_spin_lock);

static void __iomem *mfpr_mmio_base = (void __iomem *)&__REG(MFPR_BASE);
32 33 34 35 36 37 38 39

struct pxa3xx_mfp_pin {
	unsigned long	config;		/* -1 for not configured */
	unsigned long	mfpr_off;	/* MFPRxx Register offset */
	unsigned long	mfpr_run;	/* Run-Mode Register Value */
	unsigned long	mfpr_lpm;	/* Low Power Mode Register Value */
};

eric miao's avatar
eric miao committed
40 41
static struct pxa3xx_mfp_pin mfp_table[MFP_PIN_MAX];

42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
/* mapping of MFP_LPM_* definitions to MFPR_LPM_* register bits */
const static unsigned long mfpr_lpm[] = {
	MFPR_LPM_INPUT,
	MFPR_LPM_DRIVE_LOW,
	MFPR_LPM_DRIVE_HIGH,
	MFPR_LPM_PULL_LOW,
	MFPR_LPM_PULL_HIGH,
	MFPR_LPM_FLOAT,
};

/* mapping of MFP_PULL_* definitions to MFPR_PULL_* register bits */
const static unsigned long mfpr_pull[] = {
	MFPR_PULL_NONE,
	MFPR_PULL_LOW,
	MFPR_PULL_HIGH,
	MFPR_PULL_BOTH,
};

/* mapping of MFP_LPM_EDGE_* definitions to MFPR_EDGE_* register bits */
const static unsigned long mfpr_edge[] = {
	MFPR_EDGE_NONE,
	MFPR_EDGE_RISE,
	MFPR_EDGE_FALL,
	MFPR_EDGE_BOTH,
};

eric miao's avatar
eric miao committed
68 69 70 71 72 73
#define mfpr_readl(off)			\
	__raw_readl(mfpr_mmio_base + (off))

#define mfpr_writel(off, val)		\
	__raw_writel(val, mfpr_mmio_base + (off))

74 75
#define mfp_configured(p)	((p)->config != -1)

eric miao's avatar
eric miao committed
76 77 78 79 80 81
/*
 * perform a read-back of any MFPR register to make sure the
 * previous writings are finished
 */
#define mfpr_sync()	(void)__raw_readl(mfpr_mmio_base + 0)

82
static inline void __mfp_config_run(struct pxa3xx_mfp_pin *p)
eric miao's avatar
eric miao committed
83
{
84 85 86
	if (mfp_configured(p))
		mfpr_writel(p->mfpr_off, p->mfpr_run);
}
eric miao's avatar
eric miao committed
87

88 89 90 91
static inline void __mfp_config_lpm(struct pxa3xx_mfp_pin *p)
{
	if (mfp_configured(p) && p->mfpr_lpm != p->mfpr_run)
		mfpr_writel(p->mfpr_off, p->mfpr_lpm);
eric miao's avatar
eric miao committed
92 93
}

94
void pxa3xx_mfp_config(unsigned long *mfp_cfgs, int num)
eric miao's avatar
eric miao committed
95
{
96 97
	unsigned long flags;
	int i;
eric miao's avatar
eric miao committed
98 99 100

	spin_lock_irqsave(&mfp_spin_lock, flags);

101 102 103 104
	for (i = 0; i < num; i++, mfp_cfgs++) {
		unsigned long tmp, c = *mfp_cfgs;
		struct pxa3xx_mfp_pin *p;
		int pin, af, drv, lpm, edge, pull;
eric miao's avatar
eric miao committed
105

106
		pin = MFP_PIN(c);
eric miao's avatar
eric miao committed
107
		BUG_ON(pin >= MFP_PIN_MAX);
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
		p = &mfp_table[pin];

		af  = MFP_AF(c);
		drv = MFP_DS(c);
		lpm = MFP_LPM_STATE(c);
		edge = MFP_LPM_EDGE(c);
		pull = MFP_PULL(c);

		/* run-mode pull settings will conflict with MFPR bits of
		 * low power mode state,  calculate mfpr_run and mfpr_lpm
		 * individually if pull != MFP_PULL_NONE
		 */
		tmp = MFPR_AF_SEL(af) | MFPR_DRIVE(drv);

		if (likely(pull == MFP_PULL_NONE)) {
			p->mfpr_run = tmp | mfpr_lpm[lpm] | mfpr_edge[edge];
			p->mfpr_lpm = p->mfpr_run;
		} else {
			p->mfpr_lpm = tmp | mfpr_lpm[lpm] | mfpr_edge[edge];
			p->mfpr_run = tmp | mfpr_pull[pull];
		}

		p->config = c; __mfp_config_run(p);
eric miao's avatar
eric miao committed
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
	}

	mfpr_sync();
	spin_unlock_irqrestore(&mfp_spin_lock, flags);
}

unsigned long pxa3xx_mfp_read(int mfp)
{
	unsigned long val, flags;

	BUG_ON(mfp >= MFP_PIN_MAX);

	spin_lock_irqsave(&mfp_spin_lock, flags);
	val = mfpr_readl(mfp_table[mfp].mfpr_off);
	spin_unlock_irqrestore(&mfp_spin_lock, flags);

	return val;
}

void pxa3xx_mfp_write(int mfp, unsigned long val)
{
	unsigned long flags;

	BUG_ON(mfp >= MFP_PIN_MAX);

	spin_lock_irqsave(&mfp_spin_lock, flags);
	mfpr_writel(mfp_table[mfp].mfpr_off, val);
	mfpr_sync();
	spin_unlock_irqrestore(&mfp_spin_lock, flags);
}

void __init pxa3xx_mfp_init_addr(struct pxa3xx_mfp_addr_map *map)
{
	struct pxa3xx_mfp_addr_map *p;
	unsigned long offset, flags;
	int i;

	spin_lock_irqsave(&mfp_spin_lock, flags);

	for (p = map; p->start != MFP_PIN_INVALID; p++) {
		offset = p->offset;
		i = p->start;

		do {
			mfp_table[i].mfpr_off = offset;
176 177
			mfp_table[i].mfpr_run = 0;
			mfp_table[i].mfpr_lpm = 0;
eric miao's avatar
eric miao committed
178 179 180 181 182 183 184 185 186
			offset += 4; i++;
		} while ((i <= p->end) && (p->end != -1));
	}

	spin_unlock_irqrestore(&mfp_spin_lock, flags);
}

void __init pxa3xx_init_mfp(void)
{
187 188 189 190
	int i;

	for (i = 0; i < ARRAY_SIZE(mfp_table); i++)
		mfp_table[i].config = -1;
eric miao's avatar
eric miao committed
191
}