18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/arch/arm/plat-pxa/mfp.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *   Multi-Function Pin Support
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 2007 Marvell Internation Ltd.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * 2007-08-21: eric miao <eric.miao@marvell.com>
108c2ecf20Sopenharmony_ci *             initial version
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/init.h>
168c2ecf20Sopenharmony_ci#include <linux/io.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <plat/mfp.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define MFPR_SIZE	(PAGE_SIZE)
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* MFPR register bit definitions */
238c2ecf20Sopenharmony_ci#define MFPR_PULL_SEL		(0x1 << 15)
248c2ecf20Sopenharmony_ci#define MFPR_PULLUP_EN		(0x1 << 14)
258c2ecf20Sopenharmony_ci#define MFPR_PULLDOWN_EN	(0x1 << 13)
268c2ecf20Sopenharmony_ci#define MFPR_SLEEP_SEL		(0x1 << 9)
278c2ecf20Sopenharmony_ci#define MFPR_SLEEP_OE_N		(0x1 << 7)
288c2ecf20Sopenharmony_ci#define MFPR_EDGE_CLEAR		(0x1 << 6)
298c2ecf20Sopenharmony_ci#define MFPR_EDGE_FALL_EN	(0x1 << 5)
308c2ecf20Sopenharmony_ci#define MFPR_EDGE_RISE_EN	(0x1 << 4)
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define MFPR_SLEEP_DATA(x)	((x) << 8)
338c2ecf20Sopenharmony_ci#define MFPR_DRIVE(x)		(((x) & 0x7) << 10)
348c2ecf20Sopenharmony_ci#define MFPR_AF_SEL(x)		(((x) & 0x7) << 0)
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define MFPR_EDGE_NONE		(0)
378c2ecf20Sopenharmony_ci#define MFPR_EDGE_RISE		(MFPR_EDGE_RISE_EN)
388c2ecf20Sopenharmony_ci#define MFPR_EDGE_FALL		(MFPR_EDGE_FALL_EN)
398c2ecf20Sopenharmony_ci#define MFPR_EDGE_BOTH		(MFPR_EDGE_RISE | MFPR_EDGE_FALL)
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/*
428c2ecf20Sopenharmony_ci * Table that determines the low power modes outputs, with actual settings
438c2ecf20Sopenharmony_ci * used in parentheses for don't-care values. Except for the float output,
448c2ecf20Sopenharmony_ci * the configured driven and pulled levels match, so if there is a need for
458c2ecf20Sopenharmony_ci * non-LPM pulled output, the same configuration could probably be used.
468c2ecf20Sopenharmony_ci *
478c2ecf20Sopenharmony_ci * Output value  sleep_oe_n  sleep_data  pullup_en  pulldown_en  pull_sel
488c2ecf20Sopenharmony_ci *                 (bit 7)    (bit 8)    (bit 14)     (bit 13)   (bit 15)
498c2ecf20Sopenharmony_ci *
508c2ecf20Sopenharmony_ci * Input            0          X(0)        X(0)        X(0)       0
518c2ecf20Sopenharmony_ci * Drive 0          0          0           0           X(1)       0
528c2ecf20Sopenharmony_ci * Drive 1          0          1           X(1)        0	  0
538c2ecf20Sopenharmony_ci * Pull hi (1)      1          X(1)        1           0	  0
548c2ecf20Sopenharmony_ci * Pull lo (0)      1          X(0)        0           1	  0
558c2ecf20Sopenharmony_ci * Z (float)        1          X(0)        0           0	  0
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_ci#define MFPR_LPM_INPUT		(0)
588c2ecf20Sopenharmony_ci#define MFPR_LPM_DRIVE_LOW	(MFPR_SLEEP_DATA(0) | MFPR_PULLDOWN_EN)
598c2ecf20Sopenharmony_ci#define MFPR_LPM_DRIVE_HIGH    	(MFPR_SLEEP_DATA(1) | MFPR_PULLUP_EN)
608c2ecf20Sopenharmony_ci#define MFPR_LPM_PULL_LOW      	(MFPR_LPM_DRIVE_LOW  | MFPR_SLEEP_OE_N)
618c2ecf20Sopenharmony_ci#define MFPR_LPM_PULL_HIGH     	(MFPR_LPM_DRIVE_HIGH | MFPR_SLEEP_OE_N)
628c2ecf20Sopenharmony_ci#define MFPR_LPM_FLOAT         	(MFPR_SLEEP_OE_N)
638c2ecf20Sopenharmony_ci#define MFPR_LPM_MASK		(0xe080)
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/*
668c2ecf20Sopenharmony_ci * The pullup and pulldown state of the MFP pin at run mode is by default
678c2ecf20Sopenharmony_ci * determined by the selected alternate function. In case that some buggy
688c2ecf20Sopenharmony_ci * devices need to override this default behavior,  the definitions below
698c2ecf20Sopenharmony_ci * indicates the setting of corresponding MFPR bits
708c2ecf20Sopenharmony_ci *
718c2ecf20Sopenharmony_ci * Definition       pull_sel  pullup_en  pulldown_en
728c2ecf20Sopenharmony_ci * MFPR_PULL_NONE       0         0        0
738c2ecf20Sopenharmony_ci * MFPR_PULL_LOW        1         0        1
748c2ecf20Sopenharmony_ci * MFPR_PULL_HIGH       1         1        0
758c2ecf20Sopenharmony_ci * MFPR_PULL_BOTH       1         1        1
768c2ecf20Sopenharmony_ci * MFPR_PULL_FLOAT	1         0        0
778c2ecf20Sopenharmony_ci */
788c2ecf20Sopenharmony_ci#define MFPR_PULL_NONE		(0)
798c2ecf20Sopenharmony_ci#define MFPR_PULL_LOW		(MFPR_PULL_SEL | MFPR_PULLDOWN_EN)
808c2ecf20Sopenharmony_ci#define MFPR_PULL_BOTH		(MFPR_PULL_LOW | MFPR_PULLUP_EN)
818c2ecf20Sopenharmony_ci#define MFPR_PULL_HIGH		(MFPR_PULL_SEL | MFPR_PULLUP_EN)
828c2ecf20Sopenharmony_ci#define MFPR_PULL_FLOAT		(MFPR_PULL_SEL)
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* mfp_spin_lock is used to ensure that MFP register configuration
858c2ecf20Sopenharmony_ci * (most likely a read-modify-write operation) is atomic, and that
868c2ecf20Sopenharmony_ci * mfp_table[] is consistent
878c2ecf20Sopenharmony_ci */
888c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(mfp_spin_lock);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_cistatic void __iomem *mfpr_mmio_base;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistruct mfp_pin {
938c2ecf20Sopenharmony_ci	unsigned long	config;		/* -1 for not configured */
948c2ecf20Sopenharmony_ci	unsigned long	mfpr_off;	/* MFPRxx Register offset */
958c2ecf20Sopenharmony_ci	unsigned long	mfpr_run;	/* Run-Mode Register Value */
968c2ecf20Sopenharmony_ci	unsigned long	mfpr_lpm;	/* Low Power Mode Register Value */
978c2ecf20Sopenharmony_ci};
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_cistatic struct mfp_pin mfp_table[MFP_PIN_MAX];
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/* mapping of MFP_LPM_* definitions to MFPR_LPM_* register bits */
1028c2ecf20Sopenharmony_cistatic const unsigned long mfpr_lpm[] = {
1038c2ecf20Sopenharmony_ci	MFPR_LPM_INPUT,
1048c2ecf20Sopenharmony_ci	MFPR_LPM_DRIVE_LOW,
1058c2ecf20Sopenharmony_ci	MFPR_LPM_DRIVE_HIGH,
1068c2ecf20Sopenharmony_ci	MFPR_LPM_PULL_LOW,
1078c2ecf20Sopenharmony_ci	MFPR_LPM_PULL_HIGH,
1088c2ecf20Sopenharmony_ci	MFPR_LPM_FLOAT,
1098c2ecf20Sopenharmony_ci	MFPR_LPM_INPUT,
1108c2ecf20Sopenharmony_ci};
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/* mapping of MFP_PULL_* definitions to MFPR_PULL_* register bits */
1138c2ecf20Sopenharmony_cistatic const unsigned long mfpr_pull[] = {
1148c2ecf20Sopenharmony_ci	MFPR_PULL_NONE,
1158c2ecf20Sopenharmony_ci	MFPR_PULL_LOW,
1168c2ecf20Sopenharmony_ci	MFPR_PULL_HIGH,
1178c2ecf20Sopenharmony_ci	MFPR_PULL_BOTH,
1188c2ecf20Sopenharmony_ci	MFPR_PULL_FLOAT,
1198c2ecf20Sopenharmony_ci};
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci/* mapping of MFP_LPM_EDGE_* definitions to MFPR_EDGE_* register bits */
1228c2ecf20Sopenharmony_cistatic const unsigned long mfpr_edge[] = {
1238c2ecf20Sopenharmony_ci	MFPR_EDGE_NONE,
1248c2ecf20Sopenharmony_ci	MFPR_EDGE_RISE,
1258c2ecf20Sopenharmony_ci	MFPR_EDGE_FALL,
1268c2ecf20Sopenharmony_ci	MFPR_EDGE_BOTH,
1278c2ecf20Sopenharmony_ci};
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci#define mfpr_readl(off)			\
1308c2ecf20Sopenharmony_ci	__raw_readl(mfpr_mmio_base + (off))
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci#define mfpr_writel(off, val)		\
1338c2ecf20Sopenharmony_ci	__raw_writel(val, mfpr_mmio_base + (off))
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci#define mfp_configured(p)	((p)->config != -1)
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci/*
1388c2ecf20Sopenharmony_ci * perform a read-back of any valid MFPR register to make sure the
1398c2ecf20Sopenharmony_ci * previous writings are finished
1408c2ecf20Sopenharmony_ci */
1418c2ecf20Sopenharmony_cistatic unsigned long mfpr_off_readback;
1428c2ecf20Sopenharmony_ci#define mfpr_sync()	(void)__raw_readl(mfpr_mmio_base + mfpr_off_readback)
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_cistatic inline void __mfp_config_run(struct mfp_pin *p)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	if (mfp_configured(p))
1478c2ecf20Sopenharmony_ci		mfpr_writel(p->mfpr_off, p->mfpr_run);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_cistatic inline void __mfp_config_lpm(struct mfp_pin *p)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	if (mfp_configured(p)) {
1538c2ecf20Sopenharmony_ci		unsigned long mfpr_clr = (p->mfpr_run & ~MFPR_EDGE_BOTH) | MFPR_EDGE_CLEAR;
1548c2ecf20Sopenharmony_ci		if (mfpr_clr != p->mfpr_run)
1558c2ecf20Sopenharmony_ci			mfpr_writel(p->mfpr_off, mfpr_clr);
1568c2ecf20Sopenharmony_ci		if (p->mfpr_lpm != mfpr_clr)
1578c2ecf20Sopenharmony_ci			mfpr_writel(p->mfpr_off, p->mfpr_lpm);
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_civoid mfp_config(unsigned long *mfp_cfgs, int num)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	unsigned long flags;
1648c2ecf20Sopenharmony_ci	int i;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mfp_spin_lock, flags);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	for (i = 0; i < num; i++, mfp_cfgs++) {
1698c2ecf20Sopenharmony_ci		unsigned long tmp, c = *mfp_cfgs;
1708c2ecf20Sopenharmony_ci		struct mfp_pin *p;
1718c2ecf20Sopenharmony_ci		int pin, af, drv, lpm, edge, pull;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci		pin = MFP_PIN(c);
1748c2ecf20Sopenharmony_ci		BUG_ON(pin >= MFP_PIN_MAX);
1758c2ecf20Sopenharmony_ci		p = &mfp_table[pin];
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		af  = MFP_AF(c);
1788c2ecf20Sopenharmony_ci		drv = MFP_DS(c);
1798c2ecf20Sopenharmony_ci		lpm = MFP_LPM_STATE(c);
1808c2ecf20Sopenharmony_ci		edge = MFP_LPM_EDGE(c);
1818c2ecf20Sopenharmony_ci		pull = MFP_PULL(c);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci		/* run-mode pull settings will conflict with MFPR bits of
1848c2ecf20Sopenharmony_ci		 * low power mode state,  calculate mfpr_run and mfpr_lpm
1858c2ecf20Sopenharmony_ci		 * individually if pull != MFP_PULL_NONE
1868c2ecf20Sopenharmony_ci		 */
1878c2ecf20Sopenharmony_ci		tmp = MFPR_AF_SEL(af) | MFPR_DRIVE(drv);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci		if (likely(pull == MFP_PULL_NONE)) {
1908c2ecf20Sopenharmony_ci			p->mfpr_run = tmp | mfpr_lpm[lpm] | mfpr_edge[edge];
1918c2ecf20Sopenharmony_ci			p->mfpr_lpm = p->mfpr_run;
1928c2ecf20Sopenharmony_ci		} else {
1938c2ecf20Sopenharmony_ci			p->mfpr_lpm = tmp | mfpr_lpm[lpm] | mfpr_edge[edge];
1948c2ecf20Sopenharmony_ci			p->mfpr_run = tmp | mfpr_pull[pull];
1958c2ecf20Sopenharmony_ci		}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci		p->config = c; __mfp_config_run(p);
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	mfpr_sync();
2018c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mfp_spin_lock, flags);
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ciunsigned long mfp_read(int mfp)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	unsigned long val, flags;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	BUG_ON(mfp < 0 || mfp >= MFP_PIN_MAX);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mfp_spin_lock, flags);
2118c2ecf20Sopenharmony_ci	val = mfpr_readl(mfp_table[mfp].mfpr_off);
2128c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mfp_spin_lock, flags);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return val;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_civoid mfp_write(int mfp, unsigned long val)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	unsigned long flags;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	BUG_ON(mfp < 0 || mfp >= MFP_PIN_MAX);
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mfp_spin_lock, flags);
2248c2ecf20Sopenharmony_ci	mfpr_writel(mfp_table[mfp].mfpr_off, val);
2258c2ecf20Sopenharmony_ci	mfpr_sync();
2268c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mfp_spin_lock, flags);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_civoid __init mfp_init_base(void __iomem *mfpr_base)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	int i;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* initialize the table with default - unconfigured */
2348c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(mfp_table); i++)
2358c2ecf20Sopenharmony_ci		mfp_table[i].config = -1;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	mfpr_mmio_base = mfpr_base;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_civoid __init mfp_init_addr(struct mfp_addr_map *map)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct mfp_addr_map *p;
2438c2ecf20Sopenharmony_ci	unsigned long offset, flags;
2448c2ecf20Sopenharmony_ci	int i;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mfp_spin_lock, flags);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	/* mfp offset for readback */
2498c2ecf20Sopenharmony_ci	mfpr_off_readback = map[0].offset;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	for (p = map; p->start != MFP_PIN_INVALID; p++) {
2528c2ecf20Sopenharmony_ci		offset = p->offset;
2538c2ecf20Sopenharmony_ci		i = p->start;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci		do {
2568c2ecf20Sopenharmony_ci			mfp_table[i].mfpr_off = offset;
2578c2ecf20Sopenharmony_ci			mfp_table[i].mfpr_run = 0;
2588c2ecf20Sopenharmony_ci			mfp_table[i].mfpr_lpm = 0;
2598c2ecf20Sopenharmony_ci			offset += 4; i++;
2608c2ecf20Sopenharmony_ci		} while ((i <= p->end) && (p->end != -1));
2618c2ecf20Sopenharmony_ci	}
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mfp_spin_lock, flags);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_civoid mfp_config_lpm(void)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct mfp_pin *p = &mfp_table[0];
2698c2ecf20Sopenharmony_ci	int pin;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	for (pin = 0; pin < ARRAY_SIZE(mfp_table); pin++, p++)
2728c2ecf20Sopenharmony_ci		__mfp_config_lpm(p);
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_civoid mfp_config_run(void)
2768c2ecf20Sopenharmony_ci{
2778c2ecf20Sopenharmony_ci	struct mfp_pin *p = &mfp_table[0];
2788c2ecf20Sopenharmony_ci	int pin;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	for (pin = 0; pin < ARRAY_SIZE(mfp_table); pin++, p++)
2818c2ecf20Sopenharmony_ci		__mfp_config_run(p);
2828c2ecf20Sopenharmony_ci}
283