162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * linux/arch/arm/plat-pxa/mfp.c 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Multi-Function Pin Support 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2007 Marvell Internation Ltd. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * 2007-08-21: eric miao <eric.miao@marvell.com> 1062306a36Sopenharmony_ci * initial version 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/kernel.h> 1562306a36Sopenharmony_ci#include <linux/init.h> 1662306a36Sopenharmony_ci#include <linux/io.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/soc/pxa/mfp.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define MFPR_SIZE (PAGE_SIZE) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* MFPR register bit definitions */ 2362306a36Sopenharmony_ci#define MFPR_PULL_SEL (0x1 << 15) 2462306a36Sopenharmony_ci#define MFPR_PULLUP_EN (0x1 << 14) 2562306a36Sopenharmony_ci#define MFPR_PULLDOWN_EN (0x1 << 13) 2662306a36Sopenharmony_ci#define MFPR_SLEEP_SEL (0x1 << 9) 2762306a36Sopenharmony_ci#define MFPR_SLEEP_OE_N (0x1 << 7) 2862306a36Sopenharmony_ci#define MFPR_EDGE_CLEAR (0x1 << 6) 2962306a36Sopenharmony_ci#define MFPR_EDGE_FALL_EN (0x1 << 5) 3062306a36Sopenharmony_ci#define MFPR_EDGE_RISE_EN (0x1 << 4) 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define MFPR_SLEEP_DATA(x) ((x) << 8) 3362306a36Sopenharmony_ci#define MFPR_DRIVE(x) (((x) & 0x7) << 10) 3462306a36Sopenharmony_ci#define MFPR_AF_SEL(x) (((x) & 0x7) << 0) 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define MFPR_EDGE_NONE (0) 3762306a36Sopenharmony_ci#define MFPR_EDGE_RISE (MFPR_EDGE_RISE_EN) 3862306a36Sopenharmony_ci#define MFPR_EDGE_FALL (MFPR_EDGE_FALL_EN) 3962306a36Sopenharmony_ci#define MFPR_EDGE_BOTH (MFPR_EDGE_RISE | MFPR_EDGE_FALL) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* 4262306a36Sopenharmony_ci * Table that determines the low power modes outputs, with actual settings 4362306a36Sopenharmony_ci * used in parentheses for don't-care values. Except for the float output, 4462306a36Sopenharmony_ci * the configured driven and pulled levels match, so if there is a need for 4562306a36Sopenharmony_ci * non-LPM pulled output, the same configuration could probably be used. 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * Output value sleep_oe_n sleep_data pullup_en pulldown_en pull_sel 4862306a36Sopenharmony_ci * (bit 7) (bit 8) (bit 14) (bit 13) (bit 15) 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * Input 0 X(0) X(0) X(0) 0 5162306a36Sopenharmony_ci * Drive 0 0 0 0 X(1) 0 5262306a36Sopenharmony_ci * Drive 1 0 1 X(1) 0 0 5362306a36Sopenharmony_ci * Pull hi (1) 1 X(1) 1 0 0 5462306a36Sopenharmony_ci * Pull lo (0) 1 X(0) 0 1 0 5562306a36Sopenharmony_ci * Z (float) 1 X(0) 0 0 0 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci#define MFPR_LPM_INPUT (0) 5862306a36Sopenharmony_ci#define MFPR_LPM_DRIVE_LOW (MFPR_SLEEP_DATA(0) | MFPR_PULLDOWN_EN) 5962306a36Sopenharmony_ci#define MFPR_LPM_DRIVE_HIGH (MFPR_SLEEP_DATA(1) | MFPR_PULLUP_EN) 6062306a36Sopenharmony_ci#define MFPR_LPM_PULL_LOW (MFPR_LPM_DRIVE_LOW | MFPR_SLEEP_OE_N) 6162306a36Sopenharmony_ci#define MFPR_LPM_PULL_HIGH (MFPR_LPM_DRIVE_HIGH | MFPR_SLEEP_OE_N) 6262306a36Sopenharmony_ci#define MFPR_LPM_FLOAT (MFPR_SLEEP_OE_N) 6362306a36Sopenharmony_ci#define MFPR_LPM_MASK (0xe080) 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/* 6662306a36Sopenharmony_ci * The pullup and pulldown state of the MFP pin at run mode is by default 6762306a36Sopenharmony_ci * determined by the selected alternate function. In case that some buggy 6862306a36Sopenharmony_ci * devices need to override this default behavior, the definitions below 6962306a36Sopenharmony_ci * indicates the setting of corresponding MFPR bits 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * Definition pull_sel pullup_en pulldown_en 7262306a36Sopenharmony_ci * MFPR_PULL_NONE 0 0 0 7362306a36Sopenharmony_ci * MFPR_PULL_LOW 1 0 1 7462306a36Sopenharmony_ci * MFPR_PULL_HIGH 1 1 0 7562306a36Sopenharmony_ci * MFPR_PULL_BOTH 1 1 1 7662306a36Sopenharmony_ci * MFPR_PULL_FLOAT 1 0 0 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_ci#define MFPR_PULL_NONE (0) 7962306a36Sopenharmony_ci#define MFPR_PULL_LOW (MFPR_PULL_SEL | MFPR_PULLDOWN_EN) 8062306a36Sopenharmony_ci#define MFPR_PULL_BOTH (MFPR_PULL_LOW | MFPR_PULLUP_EN) 8162306a36Sopenharmony_ci#define MFPR_PULL_HIGH (MFPR_PULL_SEL | MFPR_PULLUP_EN) 8262306a36Sopenharmony_ci#define MFPR_PULL_FLOAT (MFPR_PULL_SEL) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci/* mfp_spin_lock is used to ensure that MFP register configuration 8562306a36Sopenharmony_ci * (most likely a read-modify-write operation) is atomic, and that 8662306a36Sopenharmony_ci * mfp_table[] is consistent 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(mfp_spin_lock); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_cistatic void __iomem *mfpr_mmio_base; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistruct mfp_pin { 9362306a36Sopenharmony_ci unsigned long config; /* -1 for not configured */ 9462306a36Sopenharmony_ci unsigned long mfpr_off; /* MFPRxx Register offset */ 9562306a36Sopenharmony_ci unsigned long mfpr_run; /* Run-Mode Register Value */ 9662306a36Sopenharmony_ci unsigned long mfpr_lpm; /* Low Power Mode Register Value */ 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic struct mfp_pin mfp_table[MFP_PIN_MAX]; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* mapping of MFP_LPM_* definitions to MFPR_LPM_* register bits */ 10262306a36Sopenharmony_cistatic const unsigned long mfpr_lpm[] = { 10362306a36Sopenharmony_ci MFPR_LPM_INPUT, 10462306a36Sopenharmony_ci MFPR_LPM_DRIVE_LOW, 10562306a36Sopenharmony_ci MFPR_LPM_DRIVE_HIGH, 10662306a36Sopenharmony_ci MFPR_LPM_PULL_LOW, 10762306a36Sopenharmony_ci MFPR_LPM_PULL_HIGH, 10862306a36Sopenharmony_ci MFPR_LPM_FLOAT, 10962306a36Sopenharmony_ci MFPR_LPM_INPUT, 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* mapping of MFP_PULL_* definitions to MFPR_PULL_* register bits */ 11362306a36Sopenharmony_cistatic const unsigned long mfpr_pull[] = { 11462306a36Sopenharmony_ci MFPR_PULL_NONE, 11562306a36Sopenharmony_ci MFPR_PULL_LOW, 11662306a36Sopenharmony_ci MFPR_PULL_HIGH, 11762306a36Sopenharmony_ci MFPR_PULL_BOTH, 11862306a36Sopenharmony_ci MFPR_PULL_FLOAT, 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci/* mapping of MFP_LPM_EDGE_* definitions to MFPR_EDGE_* register bits */ 12262306a36Sopenharmony_cistatic const unsigned long mfpr_edge[] = { 12362306a36Sopenharmony_ci MFPR_EDGE_NONE, 12462306a36Sopenharmony_ci MFPR_EDGE_RISE, 12562306a36Sopenharmony_ci MFPR_EDGE_FALL, 12662306a36Sopenharmony_ci MFPR_EDGE_BOTH, 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#define mfpr_readl(off) \ 13062306a36Sopenharmony_ci __raw_readl(mfpr_mmio_base + (off)) 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define mfpr_writel(off, val) \ 13362306a36Sopenharmony_ci __raw_writel(val, mfpr_mmio_base + (off)) 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci#define mfp_configured(p) ((p)->config != -1) 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* 13862306a36Sopenharmony_ci * perform a read-back of any valid MFPR register to make sure the 13962306a36Sopenharmony_ci * previous writings are finished 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_cistatic unsigned long mfpr_off_readback; 14262306a36Sopenharmony_ci#define mfpr_sync() (void)__raw_readl(mfpr_mmio_base + mfpr_off_readback) 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic inline void __mfp_config_run(struct mfp_pin *p) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci if (mfp_configured(p)) 14762306a36Sopenharmony_ci mfpr_writel(p->mfpr_off, p->mfpr_run); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic inline void __mfp_config_lpm(struct mfp_pin *p) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci if (mfp_configured(p)) { 15362306a36Sopenharmony_ci unsigned long mfpr_clr = (p->mfpr_run & ~MFPR_EDGE_BOTH) | MFPR_EDGE_CLEAR; 15462306a36Sopenharmony_ci if (mfpr_clr != p->mfpr_run) 15562306a36Sopenharmony_ci mfpr_writel(p->mfpr_off, mfpr_clr); 15662306a36Sopenharmony_ci if (p->mfpr_lpm != mfpr_clr) 15762306a36Sopenharmony_ci mfpr_writel(p->mfpr_off, p->mfpr_lpm); 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_civoid mfp_config(unsigned long *mfp_cfgs, int num) 16262306a36Sopenharmony_ci{ 16362306a36Sopenharmony_ci unsigned long flags; 16462306a36Sopenharmony_ci int i; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci spin_lock_irqsave(&mfp_spin_lock, flags); 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci for (i = 0; i < num; i++, mfp_cfgs++) { 16962306a36Sopenharmony_ci unsigned long tmp, c = *mfp_cfgs; 17062306a36Sopenharmony_ci struct mfp_pin *p; 17162306a36Sopenharmony_ci int pin, af, drv, lpm, edge, pull; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci pin = MFP_PIN(c); 17462306a36Sopenharmony_ci BUG_ON(pin >= MFP_PIN_MAX); 17562306a36Sopenharmony_ci p = &mfp_table[pin]; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci af = MFP_AF(c); 17862306a36Sopenharmony_ci drv = MFP_DS(c); 17962306a36Sopenharmony_ci lpm = MFP_LPM_STATE(c); 18062306a36Sopenharmony_ci edge = MFP_LPM_EDGE(c); 18162306a36Sopenharmony_ci pull = MFP_PULL(c); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* run-mode pull settings will conflict with MFPR bits of 18462306a36Sopenharmony_ci * low power mode state, calculate mfpr_run and mfpr_lpm 18562306a36Sopenharmony_ci * individually if pull != MFP_PULL_NONE 18662306a36Sopenharmony_ci */ 18762306a36Sopenharmony_ci tmp = MFPR_AF_SEL(af) | MFPR_DRIVE(drv); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (likely(pull == MFP_PULL_NONE)) { 19062306a36Sopenharmony_ci p->mfpr_run = tmp | mfpr_lpm[lpm] | mfpr_edge[edge]; 19162306a36Sopenharmony_ci p->mfpr_lpm = p->mfpr_run; 19262306a36Sopenharmony_ci } else { 19362306a36Sopenharmony_ci p->mfpr_lpm = tmp | mfpr_lpm[lpm] | mfpr_edge[edge]; 19462306a36Sopenharmony_ci p->mfpr_run = tmp | mfpr_pull[pull]; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci p->config = c; __mfp_config_run(p); 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci mfpr_sync(); 20162306a36Sopenharmony_ci spin_unlock_irqrestore(&mfp_spin_lock, flags); 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ciunsigned long mfp_read(int mfp) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci unsigned long val, flags; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci BUG_ON(mfp < 0 || mfp >= MFP_PIN_MAX); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci spin_lock_irqsave(&mfp_spin_lock, flags); 21162306a36Sopenharmony_ci val = mfpr_readl(mfp_table[mfp].mfpr_off); 21262306a36Sopenharmony_ci spin_unlock_irqrestore(&mfp_spin_lock, flags); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci return val; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_civoid mfp_write(int mfp, unsigned long val) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci unsigned long flags; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci BUG_ON(mfp < 0 || mfp >= MFP_PIN_MAX); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci spin_lock_irqsave(&mfp_spin_lock, flags); 22462306a36Sopenharmony_ci mfpr_writel(mfp_table[mfp].mfpr_off, val); 22562306a36Sopenharmony_ci mfpr_sync(); 22662306a36Sopenharmony_ci spin_unlock_irqrestore(&mfp_spin_lock, flags); 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_civoid __init mfp_init_base(void __iomem *mfpr_base) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci int i; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* initialize the table with default - unconfigured */ 23462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(mfp_table); i++) 23562306a36Sopenharmony_ci mfp_table[i].config = -1; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci mfpr_mmio_base = mfpr_base; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_civoid __init mfp_init_addr(struct mfp_addr_map *map) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci struct mfp_addr_map *p; 24362306a36Sopenharmony_ci unsigned long offset, flags; 24462306a36Sopenharmony_ci int i; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci spin_lock_irqsave(&mfp_spin_lock, flags); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* mfp offset for readback */ 24962306a36Sopenharmony_ci mfpr_off_readback = map[0].offset; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci for (p = map; p->start != MFP_PIN_INVALID; p++) { 25262306a36Sopenharmony_ci offset = p->offset; 25362306a36Sopenharmony_ci i = p->start; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci do { 25662306a36Sopenharmony_ci mfp_table[i].mfpr_off = offset; 25762306a36Sopenharmony_ci mfp_table[i].mfpr_run = 0; 25862306a36Sopenharmony_ci mfp_table[i].mfpr_lpm = 0; 25962306a36Sopenharmony_ci offset += 4; i++; 26062306a36Sopenharmony_ci } while ((i <= p->end) && (p->end != -1)); 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci spin_unlock_irqrestore(&mfp_spin_lock, flags); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_civoid mfp_config_lpm(void) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct mfp_pin *p = &mfp_table[0]; 26962306a36Sopenharmony_ci int pin; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci for (pin = 0; pin < ARRAY_SIZE(mfp_table); pin++, p++) 27262306a36Sopenharmony_ci __mfp_config_lpm(p); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_civoid mfp_config_run(void) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci struct mfp_pin *p = &mfp_table[0]; 27862306a36Sopenharmony_ci int pin; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci for (pin = 0; pin < ARRAY_SIZE(mfp_table); pin++, p++) 28162306a36Sopenharmony_ci __mfp_config_run(p); 28262306a36Sopenharmony_ci} 283