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