/* * Copyright (c) 2013, Sony Mobile Communications AB. * Copyright (c) 2013, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * author: mbahr@avm.de * * Ultra-Low-level functions for GPIO's by AVM * (reduced to the min - means max necessary usage) */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_AVM_ENHANCED) #include #include "pinctrl-msm.h" /*--- #define DBG_TRC(args...) printk(KERN_INFO args) ---*/ #define DBG_TRC(args...) struct _msm_avm_gpio_priv { const struct msm_pingroup *group; unsigned int ngpios; void __iomem *iobase; spinlock_t *plock; } msm_avm_gpio; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void msm_avm_gpio_init(const struct msm_pingroup *msm_group, unsigned int ngpios, void __iomem *iobase, spinlock_t *plock){ struct _msm_avm_gpio_priv *pmsm_priv = &msm_avm_gpio; pmsm_priv->iobase = iobase; pmsm_priv->ngpios = ngpios; pmsm_priv->group = msm_group; pmsm_priv->plock = plock; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void msm_avm_gpio_exit(void) { struct _msm_avm_gpio_priv *pmsm_priv = &msm_avm_gpio; pmsm_priv->ngpios = 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ int msm_avm_gpio_ctrl(unsigned int gpio_pin, enum _hw_gpio_function pin_mode, enum _hw_gpio_direction pin_dir){ struct _msm_avm_gpio_priv *pmsm_priv = &msm_avm_gpio; const struct msm_pingroup *g; unsigned int val; unsigned long flags; if(gpio_pin >= pmsm_priv->ngpios) { return -EINVAL; } g = &pmsm_priv->group[gpio_pin]; spin_lock_irqsave(pmsm_priv->plock, flags); val = readl(pmsm_priv->iobase + g->ctl_reg); val &= ~(0x7 << g->mux_bit); val |= pin_mode << g->mux_bit; writel(val, pmsm_priv->iobase + g->ctl_reg); if(pin_dir == GPIO_OUTPUT_PIN) { val = readl(pmsm_priv->iobase + g->ctl_reg); val |= BIT(g->oe_bit); writel(val, pmsm_priv->iobase + g->ctl_reg); } else { val = readl(pmsm_priv->iobase + g->ctl_reg); val &= ~BIT(g->oe_bit); writel(val, pmsm_priv->iobase + g->ctl_reg); } DBG_TRC("%s: gpio=%2d ctlreg=%04x %s FUN%d %s drive=%2d mA\n", __func__, gpio_pin, val, (val & 0x3) == MSM_NO_PULL ? "NO_PULL " : (val & 0x3) == MSM_PULL_DOWN ? "PULL_DOWN" : (val & 0x3) == MSM_PULL_UP ? "PULL_UP " : "KEEPER ", ((val >> 2) & 0xF), val & (1 << 9) ? "OE" : " ", (((val >> 6) & 0x7) + 1) * 2 ); spin_unlock_irqrestore(pmsm_priv->plock, flags); return 0; } EXPORT_SYMBOL(msm_avm_gpio_ctrl); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ int msm_avm_gpio_config(unsigned int gpio_pin, unsigned int bias, unsigned int drive) { struct _msm_avm_gpio_priv *pmsm_priv = &msm_avm_gpio; const struct msm_pingroup *g; unsigned int val, mask_b = 0, mask_d = 0, pin_mode; unsigned long flags; if(gpio_pin >= pmsm_priv->ngpios) { return -EINVAL; } g = &pmsm_priv->group[gpio_pin]; switch (bias) { case PIN_CONFIG_BIAS_DISABLE: pin_mode = MSM_NO_PULL, mask_b = 3; break; case PIN_CONFIG_BIAS_PULL_DOWN: pin_mode = MSM_PULL_DOWN, mask_b = 3; break; case PIN_CONFIG_BIAS_PULL_UP: pin_mode = MSM_PULL_UP, mask_b = 3; break; case PIN_CONFIG_BIAS_BUS_HOLD: pin_mode = MSM_KEEPER, mask_b = 3; break; } if(drive) { mask_d = 0x7; drive = drive / 2 - 1; } spin_lock_irqsave(pmsm_priv->plock, flags); val = readl(pmsm_priv->iobase + g->ctl_reg); if(mask_b) { val &= ~(0x3 << g->pull_bit); val |= pin_mode << g->pull_bit; } if(mask_d) { val &= ~(0x7 << g->drv_bit); val |= drive << g->drv_bit; } writel(val, pmsm_priv->iobase + g->ctl_reg); DBG_TRC("%s: gpio=%2d ctlreg=%04x %s FUN%d %s drive=%2d mA\n", __func__, gpio_pin, val, (val & 0x3) == MSM_NO_PULL ? "NO_PULL " : (val & 0x3) == MSM_PULL_DOWN ? "PULL_DOWN" : (val & 0x3) == MSM_PULL_UP ? "PULL_UP " : "KEEPER ", ((val >> g->mux_bit) & 0xF), (val & (1 << g->oe_bit)) ? "OE" : " ", (((val >> g->drv_bit) & 0x7) + 1) * 2 ); spin_unlock_irqrestore(pmsm_priv->plock, flags); return 0; } EXPORT_SYMBOL(msm_avm_gpio_config); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ int msm_avm_gpio_out_bit(unsigned int gpio_pin, int value){ struct _msm_avm_gpio_priv *pmsm_priv = &msm_avm_gpio; const struct msm_pingroup *g; unsigned int val; unsigned long flags; if(gpio_pin >= pmsm_priv->ngpios) { return -EINVAL; } g = &pmsm_priv->group[gpio_pin]; spin_lock_irqsave(pmsm_priv->plock, flags); val = readl(pmsm_priv->iobase + g->io_reg); if (value) { val |= BIT(g->out_bit); } else { val &= ~BIT(g->out_bit); } writel(val, pmsm_priv->iobase + g->io_reg); spin_unlock_irqrestore(pmsm_priv->plock, flags); return 0; } EXPORT_SYMBOL(msm_avm_gpio_out_bit); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ int msm_avm_gpio_in_bit(unsigned int gpio_pin){ struct _msm_avm_gpio_priv *pmsm_priv = &msm_avm_gpio; const struct msm_pingroup *g; unsigned int val; if(gpio_pin >= pmsm_priv->ngpios) { return -EINVAL; } g = &pmsm_priv->group[gpio_pin]; val = readl(pmsm_priv->iobase + g->io_reg); return !!(val & BIT(g->in_bit)); } EXPORT_SYMBOL(msm_avm_gpio_in_bit); /*--------------------------------------------------------------------------------*\ * piglet-Treiber benoetigt exklusiven Zugriff um TDM-Clocks zu messen * (Standardfunktionen zu langsam) \*--------------------------------------------------------------------------------*/ unsigned char *msm_avm_gpio_get_membase_and_bit(unsigned int gpio_pin, unsigned int *bit){ struct _msm_avm_gpio_priv *pmsm_priv = &msm_avm_gpio; const struct msm_pingroup *g; if(gpio_pin >= pmsm_priv->ngpios) { return NULL; } g = &pmsm_priv->group[gpio_pin]; if(bit) *bit = g->in_bit; return pmsm_priv->iobase + g->io_reg; } EXPORT_SYMBOL(msm_avm_gpio_get_membase_and_bit); #endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/