Pin control fixes for v3.10:
- Six patches fixing up the suspend/resume and wakeup handling of the Samsung and Exynos drivers. - Errorpath fixes for four different drivers. All on the probe() errorpath. - Make the debugfs code for pin config take the right mutex. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.10 (GNU/Linux) iQIcBAABAgAGBQJRpiV9AAoJEEEQszewGV1zMoEP/i/XS0p/KoyHvD3k4L1vw5nc kxDjjYXYZRrNzLtqMdr+DrEITO9yArnuJQeeUlx92Aq0GCC3B/Rh1YvhkIKvqhx4 tbySQqpDaBbWt7UsixtZBBt8CxVGiD26lLbkpAECunAFgh+XA4x6dyuSbWdem+1Y XBX56BpukU+pfa62sM9P2Hs4Cj/QQ/ABDkibzoD1tFnORdGPkcFwddXdthn37MZL WLF14522xL1GCrwzDVDbDTHsbgooFZRI8Zv0cExnbGc+BrivcnSAnVCioACHY+Pg +iHk1ls+rJEnZgqafEzq9ViHRx3ctyiscUyrdYS5OMoHZ4PcqcSqtgmi0YUTynwf jNa3OXVQ4SITuj8Q1vvxwOejUD1L2GdWSij8gBIYZTKShqntdsrYj9zX6SljHd6P x/93UpXLL9N1nbbTA/XsD1HRSYCmtHS20GH3N2hsDJa8nIQNwBD1ydj+Mzt0ROLf pKvT7jPVSYC8lYYMrigFhNuUVir0mCKiHYPrz3H6oWTVX+YFxj7420b3mid0u5fw mi8zfpxhLOMPnDGQnB3U2xUva4Nfshn9RLBfdBjC08H3OJnGTgjBwtfiqx6vbLqi ZkOl/gElq9AKUOqVNT51E0G/4Nvbe/jBQqQppLDBsxQ4x8LLBIrFmInOs/IiH3wV Q4TVoXut9HMB6YDBl2BA =J5kT -----END PGP SIGNATURE----- Merge tag 'pinctrl-fixes-v3.10-3' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl Pull pin-control fixes from Linus Walleij: - Six patches fixing up the suspend/resume and wakeup handling of the Samsung and Exynos drivers. - Errorpath fixes for four different drivers. All on the probe() errorpath. - Make the debugfs code for pin config take the right mutex. * tag 'pinctrl-fixes-v3.10-3' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl: pinctrl: pinconf: take the right mutex pinctrl: sunxi: fix error return code in sunxi_pinctrl_probe() pinctrl: exynos: Handle suspend/resume of GPIO EINT registers pinctrl: samsung: Allow per-bank SoC-specific private data pinctrl: samsung: Add support for SoC-specific suspend/resume callbacks pinctrl: Don't override the error code in probe error handling ARM: EXYNOS: Fix EINT wake-up mask configuration when pinctrl is used pinctrl: exynos: Add support for set_irq_wake of wake-up EINTs pinctrl: samsung: fix suspend/resume functionality
This commit is contained in:
Коммит
7b55eab81e
|
@ -18,8 +18,15 @@
|
|||
#ifndef __ASM_ARCH_PM_CORE_H
|
||||
#define __ASM_ARCH_PM_CORE_H __FILE__
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <mach/regs-pmu.h>
|
||||
|
||||
#ifdef CONFIG_PINCTRL_EXYNOS
|
||||
extern u32 exynos_get_eint_wake_mask(void);
|
||||
#else
|
||||
static inline u32 exynos_get_eint_wake_mask(void) { return 0xffffffff; }
|
||||
#endif
|
||||
|
||||
static inline void s3c_pm_debug_init_uart(void)
|
||||
{
|
||||
/* nothing here yet */
|
||||
|
@ -27,7 +34,12 @@ static inline void s3c_pm_debug_init_uart(void)
|
|||
|
||||
static inline void s3c_pm_arch_prepare_irqs(void)
|
||||
{
|
||||
__raw_writel(s3c_irqwake_eintmask, S5P_EINT_WAKEUP_MASK);
|
||||
u32 eintmask = s3c_irqwake_eintmask;
|
||||
|
||||
if (of_have_populated_dt())
|
||||
eintmask = exynos_get_eint_wake_mask();
|
||||
|
||||
__raw_writel(eintmask, S5P_EINT_WAKEUP_MASK);
|
||||
__raw_writel(s3c_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK);
|
||||
}
|
||||
|
||||
|
|
|
@ -610,7 +610,7 @@ static int pinconf_dbg_config_print(struct seq_file *s, void *d)
|
|||
bool found = false;
|
||||
unsigned long config;
|
||||
|
||||
mutex_lock(&pctldev->mutex);
|
||||
mutex_lock(&pinctrl_maps_mutex);
|
||||
|
||||
/* Parse the pinctrl map and look for the elected pin/state */
|
||||
for_each_maps(maps_node, i, map) {
|
||||
|
@ -659,7 +659,7 @@ static int pinconf_dbg_config_print(struct seq_file *s, void *d)
|
|||
confops->pin_config_config_dbg_show(pctldev, s, config);
|
||||
|
||||
exit:
|
||||
mutex_unlock(&pctldev->mutex);
|
||||
mutex_unlock(&pinctrl_maps_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -830,7 +830,8 @@ static int __init u300_gpio_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
err_no_range:
|
||||
err = gpiochip_remove(&gpio->chip);
|
||||
if (gpiochip_remove(&gpio->chip))
|
||||
dev_err(&pdev->dev, "failed to remove gpio chip\n");
|
||||
err_no_chip:
|
||||
err_no_domain:
|
||||
err_no_port:
|
||||
|
|
|
@ -196,6 +196,12 @@ static irqreturn_t exynos_eint_gpio_irq(int irq, void *data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
struct exynos_eint_gpio_save {
|
||||
u32 eint_con;
|
||||
u32 eint_fltcon0;
|
||||
u32 eint_fltcon1;
|
||||
};
|
||||
|
||||
/*
|
||||
* exynos_eint_gpio_init() - setup handling of external gpio interrupts.
|
||||
* @d: driver data of samsung pinctrl driver.
|
||||
|
@ -204,8 +210,8 @@ static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d)
|
|||
{
|
||||
struct samsung_pin_bank *bank;
|
||||
struct device *dev = d->dev;
|
||||
unsigned int ret;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (!d->irq) {
|
||||
dev_err(dev, "irq number not available\n");
|
||||
|
@ -227,11 +233,29 @@ static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d)
|
|||
bank->nr_pins, &exynos_gpio_irqd_ops, bank);
|
||||
if (!bank->irq_domain) {
|
||||
dev_err(dev, "gpio irq domain add failed\n");
|
||||
return -ENXIO;
|
||||
ret = -ENXIO;
|
||||
goto err_domains;
|
||||
}
|
||||
|
||||
bank->soc_priv = devm_kzalloc(d->dev,
|
||||
sizeof(struct exynos_eint_gpio_save), GFP_KERNEL);
|
||||
if (!bank->soc_priv) {
|
||||
irq_domain_remove(bank->irq_domain);
|
||||
ret = -ENOMEM;
|
||||
goto err_domains;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_domains:
|
||||
for (--i, --bank; i >= 0; --i, --bank) {
|
||||
if (bank->eint_type != EINT_TYPE_GPIO)
|
||||
continue;
|
||||
irq_domain_remove(bank->irq_domain);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void exynos_wkup_irq_unmask(struct irq_data *irqd)
|
||||
|
@ -326,6 +350,28 @@ static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static u32 exynos_eint_wake_mask = 0xffffffff;
|
||||
|
||||
u32 exynos_get_eint_wake_mask(void)
|
||||
{
|
||||
return exynos_eint_wake_mask;
|
||||
}
|
||||
|
||||
static int exynos_wkup_irq_set_wake(struct irq_data *irqd, unsigned int on)
|
||||
{
|
||||
struct samsung_pin_bank *bank = irq_data_get_irq_chip_data(irqd);
|
||||
unsigned long bit = 1UL << (2 * bank->eint_offset + irqd->hwirq);
|
||||
|
||||
pr_info("wake %s for irq %d\n", on ? "enabled" : "disabled", irqd->irq);
|
||||
|
||||
if (!on)
|
||||
exynos_eint_wake_mask |= bit;
|
||||
else
|
||||
exynos_eint_wake_mask &= ~bit;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* irq_chip for wakeup interrupts
|
||||
*/
|
||||
|
@ -335,6 +381,7 @@ static struct irq_chip exynos_wkup_irq_chip = {
|
|||
.irq_mask = exynos_wkup_irq_mask,
|
||||
.irq_ack = exynos_wkup_irq_ack,
|
||||
.irq_set_type = exynos_wkup_irq_set_type,
|
||||
.irq_set_wake = exynos_wkup_irq_set_wake,
|
||||
};
|
||||
|
||||
/* interrupt handler for wakeup interrupts 0..15 */
|
||||
|
@ -505,6 +552,72 @@ static int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void exynos_pinctrl_suspend_bank(
|
||||
struct samsung_pinctrl_drv_data *drvdata,
|
||||
struct samsung_pin_bank *bank)
|
||||
{
|
||||
struct exynos_eint_gpio_save *save = bank->soc_priv;
|
||||
void __iomem *regs = drvdata->virt_base;
|
||||
|
||||
save->eint_con = readl(regs + EXYNOS_GPIO_ECON_OFFSET
|
||||
+ bank->eint_offset);
|
||||
save->eint_fltcon0 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
|
||||
+ 2 * bank->eint_offset);
|
||||
save->eint_fltcon1 = readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
|
||||
+ 2 * bank->eint_offset + 4);
|
||||
|
||||
pr_debug("%s: save con %#010x\n", bank->name, save->eint_con);
|
||||
pr_debug("%s: save fltcon0 %#010x\n", bank->name, save->eint_fltcon0);
|
||||
pr_debug("%s: save fltcon1 %#010x\n", bank->name, save->eint_fltcon1);
|
||||
}
|
||||
|
||||
static void exynos_pinctrl_suspend(struct samsung_pinctrl_drv_data *drvdata)
|
||||
{
|
||||
struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
|
||||
struct samsung_pin_bank *bank = ctrl->pin_banks;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ctrl->nr_banks; ++i, ++bank)
|
||||
if (bank->eint_type == EINT_TYPE_GPIO)
|
||||
exynos_pinctrl_suspend_bank(drvdata, bank);
|
||||
}
|
||||
|
||||
static void exynos_pinctrl_resume_bank(
|
||||
struct samsung_pinctrl_drv_data *drvdata,
|
||||
struct samsung_pin_bank *bank)
|
||||
{
|
||||
struct exynos_eint_gpio_save *save = bank->soc_priv;
|
||||
void __iomem *regs = drvdata->virt_base;
|
||||
|
||||
pr_debug("%s: con %#010x => %#010x\n", bank->name,
|
||||
readl(regs + EXYNOS_GPIO_ECON_OFFSET
|
||||
+ bank->eint_offset), save->eint_con);
|
||||
pr_debug("%s: fltcon0 %#010x => %#010x\n", bank->name,
|
||||
readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
|
||||
+ 2 * bank->eint_offset), save->eint_fltcon0);
|
||||
pr_debug("%s: fltcon1 %#010x => %#010x\n", bank->name,
|
||||
readl(regs + EXYNOS_GPIO_EFLTCON_OFFSET
|
||||
+ 2 * bank->eint_offset + 4), save->eint_fltcon1);
|
||||
|
||||
writel(save->eint_con, regs + EXYNOS_GPIO_ECON_OFFSET
|
||||
+ bank->eint_offset);
|
||||
writel(save->eint_fltcon0, regs + EXYNOS_GPIO_EFLTCON_OFFSET
|
||||
+ 2 * bank->eint_offset);
|
||||
writel(save->eint_fltcon1, regs + EXYNOS_GPIO_EFLTCON_OFFSET
|
||||
+ 2 * bank->eint_offset + 4);
|
||||
}
|
||||
|
||||
static void exynos_pinctrl_resume(struct samsung_pinctrl_drv_data *drvdata)
|
||||
{
|
||||
struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
|
||||
struct samsung_pin_bank *bank = ctrl->pin_banks;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ctrl->nr_banks; ++i, ++bank)
|
||||
if (bank->eint_type == EINT_TYPE_GPIO)
|
||||
exynos_pinctrl_resume_bank(drvdata, bank);
|
||||
}
|
||||
|
||||
/* pin banks of exynos4210 pin-controller 0 */
|
||||
static struct samsung_pin_bank exynos4210_pin_banks0[] = {
|
||||
EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0", 0x00),
|
||||
|
@ -568,6 +681,8 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = {
|
|||
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||
.svc = EXYNOS_SVC_OFFSET,
|
||||
.eint_gpio_init = exynos_eint_gpio_init,
|
||||
.suspend = exynos_pinctrl_suspend,
|
||||
.resume = exynos_pinctrl_resume,
|
||||
.label = "exynos4210-gpio-ctrl0",
|
||||
}, {
|
||||
/* pin-controller instance 1 data */
|
||||
|
@ -582,6 +697,8 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = {
|
|||
.svc = EXYNOS_SVC_OFFSET,
|
||||
.eint_gpio_init = exynos_eint_gpio_init,
|
||||
.eint_wkup_init = exynos_eint_wkup_init,
|
||||
.suspend = exynos_pinctrl_suspend,
|
||||
.resume = exynos_pinctrl_resume,
|
||||
.label = "exynos4210-gpio-ctrl1",
|
||||
}, {
|
||||
/* pin-controller instance 2 data */
|
||||
|
@ -663,6 +780,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = {
|
|||
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||
.svc = EXYNOS_SVC_OFFSET,
|
||||
.eint_gpio_init = exynos_eint_gpio_init,
|
||||
.suspend = exynos_pinctrl_suspend,
|
||||
.resume = exynos_pinctrl_resume,
|
||||
.label = "exynos4x12-gpio-ctrl0",
|
||||
}, {
|
||||
/* pin-controller instance 1 data */
|
||||
|
@ -677,6 +796,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = {
|
|||
.svc = EXYNOS_SVC_OFFSET,
|
||||
.eint_gpio_init = exynos_eint_gpio_init,
|
||||
.eint_wkup_init = exynos_eint_wkup_init,
|
||||
.suspend = exynos_pinctrl_suspend,
|
||||
.resume = exynos_pinctrl_resume,
|
||||
.label = "exynos4x12-gpio-ctrl1",
|
||||
}, {
|
||||
/* pin-controller instance 2 data */
|
||||
|
@ -687,6 +808,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = {
|
|||
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||
.svc = EXYNOS_SVC_OFFSET,
|
||||
.eint_gpio_init = exynos_eint_gpio_init,
|
||||
.suspend = exynos_pinctrl_suspend,
|
||||
.resume = exynos_pinctrl_resume,
|
||||
.label = "exynos4x12-gpio-ctrl2",
|
||||
}, {
|
||||
/* pin-controller instance 3 data */
|
||||
|
@ -697,6 +820,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = {
|
|||
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||
.svc = EXYNOS_SVC_OFFSET,
|
||||
.eint_gpio_init = exynos_eint_gpio_init,
|
||||
.suspend = exynos_pinctrl_suspend,
|
||||
.resume = exynos_pinctrl_resume,
|
||||
.label = "exynos4x12-gpio-ctrl3",
|
||||
},
|
||||
};
|
||||
|
@ -775,6 +900,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = {
|
|||
.svc = EXYNOS_SVC_OFFSET,
|
||||
.eint_gpio_init = exynos_eint_gpio_init,
|
||||
.eint_wkup_init = exynos_eint_wkup_init,
|
||||
.suspend = exynos_pinctrl_suspend,
|
||||
.resume = exynos_pinctrl_resume,
|
||||
.label = "exynos5250-gpio-ctrl0",
|
||||
}, {
|
||||
/* pin-controller instance 1 data */
|
||||
|
@ -785,6 +912,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = {
|
|||
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||
.svc = EXYNOS_SVC_OFFSET,
|
||||
.eint_gpio_init = exynos_eint_gpio_init,
|
||||
.suspend = exynos_pinctrl_suspend,
|
||||
.resume = exynos_pinctrl_resume,
|
||||
.label = "exynos5250-gpio-ctrl1",
|
||||
}, {
|
||||
/* pin-controller instance 2 data */
|
||||
|
@ -795,6 +924,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = {
|
|||
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||
.svc = EXYNOS_SVC_OFFSET,
|
||||
.eint_gpio_init = exynos_eint_gpio_init,
|
||||
.suspend = exynos_pinctrl_suspend,
|
||||
.resume = exynos_pinctrl_resume,
|
||||
.label = "exynos5250-gpio-ctrl2",
|
||||
}, {
|
||||
/* pin-controller instance 3 data */
|
||||
|
@ -805,6 +936,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = {
|
|||
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||
.svc = EXYNOS_SVC_OFFSET,
|
||||
.eint_gpio_init = exynos_eint_gpio_init,
|
||||
.suspend = exynos_pinctrl_suspend,
|
||||
.resume = exynos_pinctrl_resume,
|
||||
.label = "exynos5250-gpio-ctrl3",
|
||||
},
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
/* External GPIO and wakeup interrupt related definitions */
|
||||
#define EXYNOS_GPIO_ECON_OFFSET 0x700
|
||||
#define EXYNOS_GPIO_EFLTCON_OFFSET 0x800
|
||||
#define EXYNOS_GPIO_EMASK_OFFSET 0x900
|
||||
#define EXYNOS_GPIO_EPEND_OFFSET 0xA00
|
||||
#define EXYNOS_WKUP_ECON_OFFSET 0xE00
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/gpio.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "pinctrl-samsung.h"
|
||||
|
@ -48,6 +49,9 @@ static struct pin_config {
|
|||
{ "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN },
|
||||
};
|
||||
|
||||
/* Global list of devices (struct samsung_pinctrl_drv_data) */
|
||||
LIST_HEAD(drvdata_list);
|
||||
|
||||
static unsigned int pin_base;
|
||||
|
||||
static inline struct samsung_pin_bank *gc_to_pin_bank(struct gpio_chip *gc)
|
||||
|
@ -956,9 +960,151 @@ static int samsung_pinctrl_probe(struct platform_device *pdev)
|
|||
ctrl->eint_wkup_init(drvdata);
|
||||
|
||||
platform_set_drvdata(pdev, drvdata);
|
||||
|
||||
/* Add to the global list */
|
||||
list_add_tail(&drvdata->node, &drvdata_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
|
||||
/**
|
||||
* samsung_pinctrl_suspend_dev - save pinctrl state for suspend for a device
|
||||
*
|
||||
* Save data for all banks handled by this device.
|
||||
*/
|
||||
static void samsung_pinctrl_suspend_dev(
|
||||
struct samsung_pinctrl_drv_data *drvdata)
|
||||
{
|
||||
struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
|
||||
void __iomem *virt_base = drvdata->virt_base;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ctrl->nr_banks; i++) {
|
||||
struct samsung_pin_bank *bank = &ctrl->pin_banks[i];
|
||||
void __iomem *reg = virt_base + bank->pctl_offset;
|
||||
|
||||
u8 *offs = bank->type->reg_offset;
|
||||
u8 *widths = bank->type->fld_width;
|
||||
enum pincfg_type type;
|
||||
|
||||
/* Registers without a powerdown config aren't lost */
|
||||
if (!widths[PINCFG_TYPE_CON_PDN])
|
||||
continue;
|
||||
|
||||
for (type = 0; type < PINCFG_TYPE_NUM; type++)
|
||||
if (widths[type])
|
||||
bank->pm_save[type] = readl(reg + offs[type]);
|
||||
|
||||
if (widths[PINCFG_TYPE_FUNC] * bank->nr_pins > 32) {
|
||||
/* Some banks have two config registers */
|
||||
bank->pm_save[PINCFG_TYPE_NUM] =
|
||||
readl(reg + offs[PINCFG_TYPE_FUNC] + 4);
|
||||
pr_debug("Save %s @ %p (con %#010x %08x)\n",
|
||||
bank->name, reg,
|
||||
bank->pm_save[PINCFG_TYPE_FUNC],
|
||||
bank->pm_save[PINCFG_TYPE_NUM]);
|
||||
} else {
|
||||
pr_debug("Save %s @ %p (con %#010x)\n", bank->name,
|
||||
reg, bank->pm_save[PINCFG_TYPE_FUNC]);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctrl->suspend)
|
||||
ctrl->suspend(drvdata);
|
||||
}
|
||||
|
||||
/**
|
||||
* samsung_pinctrl_resume_dev - restore pinctrl state from suspend for a device
|
||||
*
|
||||
* Restore one of the banks that was saved during suspend.
|
||||
*
|
||||
* We don't bother doing anything complicated to avoid glitching lines since
|
||||
* we're called before pad retention is turned off.
|
||||
*/
|
||||
static void samsung_pinctrl_resume_dev(struct samsung_pinctrl_drv_data *drvdata)
|
||||
{
|
||||
struct samsung_pin_ctrl *ctrl = drvdata->ctrl;
|
||||
void __iomem *virt_base = drvdata->virt_base;
|
||||
int i;
|
||||
|
||||
if (ctrl->resume)
|
||||
ctrl->resume(drvdata);
|
||||
|
||||
for (i = 0; i < ctrl->nr_banks; i++) {
|
||||
struct samsung_pin_bank *bank = &ctrl->pin_banks[i];
|
||||
void __iomem *reg = virt_base + bank->pctl_offset;
|
||||
|
||||
u8 *offs = bank->type->reg_offset;
|
||||
u8 *widths = bank->type->fld_width;
|
||||
enum pincfg_type type;
|
||||
|
||||
/* Registers without a powerdown config aren't lost */
|
||||
if (!widths[PINCFG_TYPE_CON_PDN])
|
||||
continue;
|
||||
|
||||
if (widths[PINCFG_TYPE_FUNC] * bank->nr_pins > 32) {
|
||||
/* Some banks have two config registers */
|
||||
pr_debug("%s @ %p (con %#010x %08x => %#010x %08x)\n",
|
||||
bank->name, reg,
|
||||
readl(reg + offs[PINCFG_TYPE_FUNC]),
|
||||
readl(reg + offs[PINCFG_TYPE_FUNC] + 4),
|
||||
bank->pm_save[PINCFG_TYPE_FUNC],
|
||||
bank->pm_save[PINCFG_TYPE_NUM]);
|
||||
writel(bank->pm_save[PINCFG_TYPE_NUM],
|
||||
reg + offs[PINCFG_TYPE_FUNC] + 4);
|
||||
} else {
|
||||
pr_debug("%s @ %p (con %#010x => %#010x)\n", bank->name,
|
||||
reg, readl(reg + offs[PINCFG_TYPE_FUNC]),
|
||||
bank->pm_save[PINCFG_TYPE_FUNC]);
|
||||
}
|
||||
for (type = 0; type < PINCFG_TYPE_NUM; type++)
|
||||
if (widths[type])
|
||||
writel(bank->pm_save[type], reg + offs[type]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* samsung_pinctrl_suspend - save pinctrl state for suspend
|
||||
*
|
||||
* Save data for all banks across all devices.
|
||||
*/
|
||||
static int samsung_pinctrl_suspend(void)
|
||||
{
|
||||
struct samsung_pinctrl_drv_data *drvdata;
|
||||
|
||||
list_for_each_entry(drvdata, &drvdata_list, node) {
|
||||
samsung_pinctrl_suspend_dev(drvdata);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* samsung_pinctrl_resume - restore pinctrl state for suspend
|
||||
*
|
||||
* Restore data for all banks across all devices.
|
||||
*/
|
||||
static void samsung_pinctrl_resume(void)
|
||||
{
|
||||
struct samsung_pinctrl_drv_data *drvdata;
|
||||
|
||||
list_for_each_entry_reverse(drvdata, &drvdata_list, node) {
|
||||
samsung_pinctrl_resume_dev(drvdata);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
#define samsung_pinctrl_suspend NULL
|
||||
#define samsung_pinctrl_resume NULL
|
||||
#endif
|
||||
|
||||
static struct syscore_ops samsung_pinctrl_syscore_ops = {
|
||||
.suspend = samsung_pinctrl_suspend,
|
||||
.resume = samsung_pinctrl_resume,
|
||||
};
|
||||
|
||||
static const struct of_device_id samsung_pinctrl_dt_match[] = {
|
||||
#ifdef CONFIG_PINCTRL_EXYNOS
|
||||
{ .compatible = "samsung,exynos4210-pinctrl",
|
||||
|
@ -987,6 +1133,14 @@ static struct platform_driver samsung_pinctrl_driver = {
|
|||
|
||||
static int __init samsung_pinctrl_drv_register(void)
|
||||
{
|
||||
/*
|
||||
* Register syscore ops for save/restore of registers across suspend.
|
||||
* It's important to ensure that this driver is running at an earlier
|
||||
* initcall level than any arch-specific init calls that install syscore
|
||||
* ops that turn off pad retention (like exynos_pm_resume).
|
||||
*/
|
||||
register_syscore_ops(&samsung_pinctrl_syscore_ops);
|
||||
|
||||
return platform_driver_register(&samsung_pinctrl_driver);
|
||||
}
|
||||
postcore_initcall(samsung_pinctrl_drv_register);
|
||||
|
|
|
@ -127,6 +127,7 @@ struct samsung_pin_bank_type {
|
|||
* @gpio_chip: GPIO chip of the bank.
|
||||
* @grange: linux gpio pin range supported by this bank.
|
||||
* @slock: spinlock protecting bank registers
|
||||
* @pm_save: saved register values during suspend
|
||||
*/
|
||||
struct samsung_pin_bank {
|
||||
struct samsung_pin_bank_type *type;
|
||||
|
@ -138,12 +139,15 @@ struct samsung_pin_bank {
|
|||
u32 eint_mask;
|
||||
u32 eint_offset;
|
||||
char *name;
|
||||
void *soc_priv;
|
||||
struct device_node *of_node;
|
||||
struct samsung_pinctrl_drv_data *drvdata;
|
||||
struct irq_domain *irq_domain;
|
||||
struct gpio_chip gpio_chip;
|
||||
struct pinctrl_gpio_range grange;
|
||||
spinlock_t slock;
|
||||
|
||||
u32 pm_save[PINCFG_TYPE_NUM + 1]; /* +1 to handle double CON registers*/
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -184,11 +188,15 @@ struct samsung_pin_ctrl {
|
|||
|
||||
int (*eint_gpio_init)(struct samsung_pinctrl_drv_data *);
|
||||
int (*eint_wkup_init)(struct samsung_pinctrl_drv_data *);
|
||||
void (*suspend)(struct samsung_pinctrl_drv_data *);
|
||||
void (*resume)(struct samsung_pinctrl_drv_data *);
|
||||
|
||||
char *label;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct samsung_pinctrl_drv_data: wrapper for holding driver data together.
|
||||
* @node: global list node
|
||||
* @virt_base: register base address of the controller.
|
||||
* @dev: device instance representing the controller.
|
||||
* @irq: interrpt number used by the controller to notify gpio interrupts.
|
||||
|
@ -201,6 +209,7 @@ struct samsung_pin_ctrl {
|
|||
* @nr_function: number of such pin functions.
|
||||
*/
|
||||
struct samsung_pinctrl_drv_data {
|
||||
struct list_head node;
|
||||
void __iomem *virt_base;
|
||||
struct device *dev;
|
||||
int irq;
|
||||
|
|
|
@ -1990,8 +1990,10 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(clk))
|
||||
if (IS_ERR(clk)) {
|
||||
ret = PTR_ERR(clk);
|
||||
goto gpiochip_error;
|
||||
}
|
||||
|
||||
clk_prepare_enable(clk);
|
||||
|
||||
|
@ -2000,7 +2002,8 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
gpiochip_error:
|
||||
ret = gpiochip_remove(pctl->chip);
|
||||
if (gpiochip_remove(pctl->chip))
|
||||
dev_err(&pdev->dev, "failed to remove gpio chip\n");
|
||||
pinctrl_error:
|
||||
pinctrl_unregister(pctl->pctl_dev);
|
||||
return ret;
|
||||
|
|
|
@ -609,8 +609,7 @@ int wmt_pinctrl_probe(struct platform_device *pdev,
|
|||
return 0;
|
||||
|
||||
fail_range:
|
||||
err = gpiochip_remove(&data->gpio_chip);
|
||||
if (err)
|
||||
if (gpiochip_remove(&data->gpio_chip))
|
||||
dev_err(&pdev->dev, "failed to remove gpio chip\n");
|
||||
fail_gpio:
|
||||
pinctrl_unregister(data->pctl_dev);
|
||||
|
|
Загрузка…
Ссылка в новой задаче