mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-29 10:01:25 +00:00
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:
commit
7b55eab81e
9 changed files with 323 additions and 11 deletions
|
@ -18,8 +18,15 @@
|
||||||
#ifndef __ASM_ARCH_PM_CORE_H
|
#ifndef __ASM_ARCH_PM_CORE_H
|
||||||
#define __ASM_ARCH_PM_CORE_H __FILE__
|
#define __ASM_ARCH_PM_CORE_H __FILE__
|
||||||
|
|
||||||
|
#include <linux/of.h>
|
||||||
#include <mach/regs-pmu.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)
|
static inline void s3c_pm_debug_init_uart(void)
|
||||||
{
|
{
|
||||||
/* nothing here yet */
|
/* 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)
|
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);
|
__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;
|
bool found = false;
|
||||||
unsigned long config;
|
unsigned long config;
|
||||||
|
|
||||||
mutex_lock(&pctldev->mutex);
|
mutex_lock(&pinctrl_maps_mutex);
|
||||||
|
|
||||||
/* Parse the pinctrl map and look for the elected pin/state */
|
/* Parse the pinctrl map and look for the elected pin/state */
|
||||||
for_each_maps(maps_node, i, map) {
|
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);
|
confops->pin_config_config_dbg_show(pctldev, s, config);
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
mutex_unlock(&pctldev->mutex);
|
mutex_unlock(&pinctrl_maps_mutex);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -830,7 +830,8 @@ static int __init u300_gpio_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_no_range:
|
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_chip:
|
||||||
err_no_domain:
|
err_no_domain:
|
||||||
err_no_port:
|
err_no_port:
|
||||||
|
|
|
@ -196,6 +196,12 @@ static irqreturn_t exynos_eint_gpio_irq(int irq, void *data)
|
||||||
return IRQ_HANDLED;
|
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.
|
* exynos_eint_gpio_init() - setup handling of external gpio interrupts.
|
||||||
* @d: driver data of samsung pinctrl driver.
|
* @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 samsung_pin_bank *bank;
|
||||||
struct device *dev = d->dev;
|
struct device *dev = d->dev;
|
||||||
unsigned int ret;
|
int ret;
|
||||||
unsigned int i;
|
int i;
|
||||||
|
|
||||||
if (!d->irq) {
|
if (!d->irq) {
|
||||||
dev_err(dev, "irq number not available\n");
|
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);
|
bank->nr_pins, &exynos_gpio_irqd_ops, bank);
|
||||||
if (!bank->irq_domain) {
|
if (!bank->irq_domain) {
|
||||||
dev_err(dev, "gpio irq domain add failed\n");
|
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;
|
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)
|
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;
|
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
|
* irq_chip for wakeup interrupts
|
||||||
*/
|
*/
|
||||||
|
@ -335,6 +381,7 @@ static struct irq_chip exynos_wkup_irq_chip = {
|
||||||
.irq_mask = exynos_wkup_irq_mask,
|
.irq_mask = exynos_wkup_irq_mask,
|
||||||
.irq_ack = exynos_wkup_irq_ack,
|
.irq_ack = exynos_wkup_irq_ack,
|
||||||
.irq_set_type = exynos_wkup_irq_set_type,
|
.irq_set_type = exynos_wkup_irq_set_type,
|
||||||
|
.irq_set_wake = exynos_wkup_irq_set_wake,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* interrupt handler for wakeup interrupts 0..15 */
|
/* 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;
|
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 */
|
/* pin banks of exynos4210 pin-controller 0 */
|
||||||
static struct samsung_pin_bank exynos4210_pin_banks0[] = {
|
static struct samsung_pin_bank exynos4210_pin_banks0[] = {
|
||||||
EXYNOS_PIN_BANK_EINTG(8, 0x000, "gpa0", 0x00),
|
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,
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||||
.svc = EXYNOS_SVC_OFFSET,
|
.svc = EXYNOS_SVC_OFFSET,
|
||||||
.eint_gpio_init = exynos_eint_gpio_init,
|
.eint_gpio_init = exynos_eint_gpio_init,
|
||||||
|
.suspend = exynos_pinctrl_suspend,
|
||||||
|
.resume = exynos_pinctrl_resume,
|
||||||
.label = "exynos4210-gpio-ctrl0",
|
.label = "exynos4210-gpio-ctrl0",
|
||||||
}, {
|
}, {
|
||||||
/* pin-controller instance 1 data */
|
/* pin-controller instance 1 data */
|
||||||
|
@ -582,6 +697,8 @@ struct samsung_pin_ctrl exynos4210_pin_ctrl[] = {
|
||||||
.svc = EXYNOS_SVC_OFFSET,
|
.svc = EXYNOS_SVC_OFFSET,
|
||||||
.eint_gpio_init = exynos_eint_gpio_init,
|
.eint_gpio_init = exynos_eint_gpio_init,
|
||||||
.eint_wkup_init = exynos_eint_wkup_init,
|
.eint_wkup_init = exynos_eint_wkup_init,
|
||||||
|
.suspend = exynos_pinctrl_suspend,
|
||||||
|
.resume = exynos_pinctrl_resume,
|
||||||
.label = "exynos4210-gpio-ctrl1",
|
.label = "exynos4210-gpio-ctrl1",
|
||||||
}, {
|
}, {
|
||||||
/* pin-controller instance 2 data */
|
/* pin-controller instance 2 data */
|
||||||
|
@ -663,6 +780,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = {
|
||||||
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||||
.svc = EXYNOS_SVC_OFFSET,
|
.svc = EXYNOS_SVC_OFFSET,
|
||||||
.eint_gpio_init = exynos_eint_gpio_init,
|
.eint_gpio_init = exynos_eint_gpio_init,
|
||||||
|
.suspend = exynos_pinctrl_suspend,
|
||||||
|
.resume = exynos_pinctrl_resume,
|
||||||
.label = "exynos4x12-gpio-ctrl0",
|
.label = "exynos4x12-gpio-ctrl0",
|
||||||
}, {
|
}, {
|
||||||
/* pin-controller instance 1 data */
|
/* pin-controller instance 1 data */
|
||||||
|
@ -677,6 +796,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = {
|
||||||
.svc = EXYNOS_SVC_OFFSET,
|
.svc = EXYNOS_SVC_OFFSET,
|
||||||
.eint_gpio_init = exynos_eint_gpio_init,
|
.eint_gpio_init = exynos_eint_gpio_init,
|
||||||
.eint_wkup_init = exynos_eint_wkup_init,
|
.eint_wkup_init = exynos_eint_wkup_init,
|
||||||
|
.suspend = exynos_pinctrl_suspend,
|
||||||
|
.resume = exynos_pinctrl_resume,
|
||||||
.label = "exynos4x12-gpio-ctrl1",
|
.label = "exynos4x12-gpio-ctrl1",
|
||||||
}, {
|
}, {
|
||||||
/* pin-controller instance 2 data */
|
/* pin-controller instance 2 data */
|
||||||
|
@ -687,6 +808,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = {
|
||||||
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||||
.svc = EXYNOS_SVC_OFFSET,
|
.svc = EXYNOS_SVC_OFFSET,
|
||||||
.eint_gpio_init = exynos_eint_gpio_init,
|
.eint_gpio_init = exynos_eint_gpio_init,
|
||||||
|
.suspend = exynos_pinctrl_suspend,
|
||||||
|
.resume = exynos_pinctrl_resume,
|
||||||
.label = "exynos4x12-gpio-ctrl2",
|
.label = "exynos4x12-gpio-ctrl2",
|
||||||
}, {
|
}, {
|
||||||
/* pin-controller instance 3 data */
|
/* pin-controller instance 3 data */
|
||||||
|
@ -697,6 +820,8 @@ struct samsung_pin_ctrl exynos4x12_pin_ctrl[] = {
|
||||||
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||||
.svc = EXYNOS_SVC_OFFSET,
|
.svc = EXYNOS_SVC_OFFSET,
|
||||||
.eint_gpio_init = exynos_eint_gpio_init,
|
.eint_gpio_init = exynos_eint_gpio_init,
|
||||||
|
.suspend = exynos_pinctrl_suspend,
|
||||||
|
.resume = exynos_pinctrl_resume,
|
||||||
.label = "exynos4x12-gpio-ctrl3",
|
.label = "exynos4x12-gpio-ctrl3",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -775,6 +900,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = {
|
||||||
.svc = EXYNOS_SVC_OFFSET,
|
.svc = EXYNOS_SVC_OFFSET,
|
||||||
.eint_gpio_init = exynos_eint_gpio_init,
|
.eint_gpio_init = exynos_eint_gpio_init,
|
||||||
.eint_wkup_init = exynos_eint_wkup_init,
|
.eint_wkup_init = exynos_eint_wkup_init,
|
||||||
|
.suspend = exynos_pinctrl_suspend,
|
||||||
|
.resume = exynos_pinctrl_resume,
|
||||||
.label = "exynos5250-gpio-ctrl0",
|
.label = "exynos5250-gpio-ctrl0",
|
||||||
}, {
|
}, {
|
||||||
/* pin-controller instance 1 data */
|
/* pin-controller instance 1 data */
|
||||||
|
@ -785,6 +912,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = {
|
||||||
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||||
.svc = EXYNOS_SVC_OFFSET,
|
.svc = EXYNOS_SVC_OFFSET,
|
||||||
.eint_gpio_init = exynos_eint_gpio_init,
|
.eint_gpio_init = exynos_eint_gpio_init,
|
||||||
|
.suspend = exynos_pinctrl_suspend,
|
||||||
|
.resume = exynos_pinctrl_resume,
|
||||||
.label = "exynos5250-gpio-ctrl1",
|
.label = "exynos5250-gpio-ctrl1",
|
||||||
}, {
|
}, {
|
||||||
/* pin-controller instance 2 data */
|
/* pin-controller instance 2 data */
|
||||||
|
@ -795,6 +924,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = {
|
||||||
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||||
.svc = EXYNOS_SVC_OFFSET,
|
.svc = EXYNOS_SVC_OFFSET,
|
||||||
.eint_gpio_init = exynos_eint_gpio_init,
|
.eint_gpio_init = exynos_eint_gpio_init,
|
||||||
|
.suspend = exynos_pinctrl_suspend,
|
||||||
|
.resume = exynos_pinctrl_resume,
|
||||||
.label = "exynos5250-gpio-ctrl2",
|
.label = "exynos5250-gpio-ctrl2",
|
||||||
}, {
|
}, {
|
||||||
/* pin-controller instance 3 data */
|
/* pin-controller instance 3 data */
|
||||||
|
@ -805,6 +936,8 @@ struct samsung_pin_ctrl exynos5250_pin_ctrl[] = {
|
||||||
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
.geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
|
||||||
.svc = EXYNOS_SVC_OFFSET,
|
.svc = EXYNOS_SVC_OFFSET,
|
||||||
.eint_gpio_init = exynos_eint_gpio_init,
|
.eint_gpio_init = exynos_eint_gpio_init,
|
||||||
|
.suspend = exynos_pinctrl_suspend,
|
||||||
|
.resume = exynos_pinctrl_resume,
|
||||||
.label = "exynos5250-gpio-ctrl3",
|
.label = "exynos5250-gpio-ctrl3",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
/* External GPIO and wakeup interrupt related definitions */
|
/* External GPIO and wakeup interrupt related definitions */
|
||||||
#define EXYNOS_GPIO_ECON_OFFSET 0x700
|
#define EXYNOS_GPIO_ECON_OFFSET 0x700
|
||||||
|
#define EXYNOS_GPIO_EFLTCON_OFFSET 0x800
|
||||||
#define EXYNOS_GPIO_EMASK_OFFSET 0x900
|
#define EXYNOS_GPIO_EMASK_OFFSET 0x900
|
||||||
#define EXYNOS_GPIO_EPEND_OFFSET 0xA00
|
#define EXYNOS_GPIO_EPEND_OFFSET 0xA00
|
||||||
#define EXYNOS_WKUP_ECON_OFFSET 0xE00
|
#define EXYNOS_WKUP_ECON_OFFSET 0xE00
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/irqdomain.h>
|
#include <linux/irqdomain.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/syscore_ops.h>
|
||||||
|
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "pinctrl-samsung.h"
|
#include "pinctrl-samsung.h"
|
||||||
|
@ -48,6 +49,9 @@ static struct pin_config {
|
||||||
{ "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN },
|
{ "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 unsigned int pin_base;
|
||||||
|
|
||||||
static inline struct samsung_pin_bank *gc_to_pin_bank(struct gpio_chip *gc)
|
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);
|
ctrl->eint_wkup_init(drvdata);
|
||||||
|
|
||||||
platform_set_drvdata(pdev, drvdata);
|
platform_set_drvdata(pdev, drvdata);
|
||||||
|
|
||||||
|
/* Add to the global list */
|
||||||
|
list_add_tail(&drvdata->node, &drvdata_list);
|
||||||
|
|
||||||
return 0;
|
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[] = {
|
static const struct of_device_id samsung_pinctrl_dt_match[] = {
|
||||||
#ifdef CONFIG_PINCTRL_EXYNOS
|
#ifdef CONFIG_PINCTRL_EXYNOS
|
||||||
{ .compatible = "samsung,exynos4210-pinctrl",
|
{ .compatible = "samsung,exynos4210-pinctrl",
|
||||||
|
@ -987,6 +1133,14 @@ static struct platform_driver samsung_pinctrl_driver = {
|
||||||
|
|
||||||
static int __init samsung_pinctrl_drv_register(void)
|
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);
|
return platform_driver_register(&samsung_pinctrl_driver);
|
||||||
}
|
}
|
||||||
postcore_initcall(samsung_pinctrl_drv_register);
|
postcore_initcall(samsung_pinctrl_drv_register);
|
||||||
|
|
|
@ -127,6 +127,7 @@ struct samsung_pin_bank_type {
|
||||||
* @gpio_chip: GPIO chip of the bank.
|
* @gpio_chip: GPIO chip of the bank.
|
||||||
* @grange: linux gpio pin range supported by this bank.
|
* @grange: linux gpio pin range supported by this bank.
|
||||||
* @slock: spinlock protecting bank registers
|
* @slock: spinlock protecting bank registers
|
||||||
|
* @pm_save: saved register values during suspend
|
||||||
*/
|
*/
|
||||||
struct samsung_pin_bank {
|
struct samsung_pin_bank {
|
||||||
struct samsung_pin_bank_type *type;
|
struct samsung_pin_bank_type *type;
|
||||||
|
@ -138,12 +139,15 @@ struct samsung_pin_bank {
|
||||||
u32 eint_mask;
|
u32 eint_mask;
|
||||||
u32 eint_offset;
|
u32 eint_offset;
|
||||||
char *name;
|
char *name;
|
||||||
|
void *soc_priv;
|
||||||
struct device_node *of_node;
|
struct device_node *of_node;
|
||||||
struct samsung_pinctrl_drv_data *drvdata;
|
struct samsung_pinctrl_drv_data *drvdata;
|
||||||
struct irq_domain *irq_domain;
|
struct irq_domain *irq_domain;
|
||||||
struct gpio_chip gpio_chip;
|
struct gpio_chip gpio_chip;
|
||||||
struct pinctrl_gpio_range grange;
|
struct pinctrl_gpio_range grange;
|
||||||
spinlock_t slock;
|
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_gpio_init)(struct samsung_pinctrl_drv_data *);
|
||||||
int (*eint_wkup_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;
|
char *label;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct samsung_pinctrl_drv_data: wrapper for holding driver data together.
|
* struct samsung_pinctrl_drv_data: wrapper for holding driver data together.
|
||||||
|
* @node: global list node
|
||||||
* @virt_base: register base address of the controller.
|
* @virt_base: register base address of the controller.
|
||||||
* @dev: device instance representing the controller.
|
* @dev: device instance representing the controller.
|
||||||
* @irq: interrpt number used by the controller to notify gpio interrupts.
|
* @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.
|
* @nr_function: number of such pin functions.
|
||||||
*/
|
*/
|
||||||
struct samsung_pinctrl_drv_data {
|
struct samsung_pinctrl_drv_data {
|
||||||
|
struct list_head node;
|
||||||
void __iomem *virt_base;
|
void __iomem *virt_base;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
int irq;
|
int irq;
|
||||||
|
|
|
@ -1990,8 +1990,10 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
clk = devm_clk_get(&pdev->dev, NULL);
|
clk = devm_clk_get(&pdev->dev, NULL);
|
||||||
if (IS_ERR(clk))
|
if (IS_ERR(clk)) {
|
||||||
|
ret = PTR_ERR(clk);
|
||||||
goto gpiochip_error;
|
goto gpiochip_error;
|
||||||
|
}
|
||||||
|
|
||||||
clk_prepare_enable(clk);
|
clk_prepare_enable(clk);
|
||||||
|
|
||||||
|
@ -2000,7 +2002,8 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
gpiochip_error:
|
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_error:
|
||||||
pinctrl_unregister(pctl->pctl_dev);
|
pinctrl_unregister(pctl->pctl_dev);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -609,8 +609,7 @@ int wmt_pinctrl_probe(struct platform_device *pdev,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
fail_range:
|
fail_range:
|
||||||
err = gpiochip_remove(&data->gpio_chip);
|
if (gpiochip_remove(&data->gpio_chip))
|
||||||
if (err)
|
|
||||||
dev_err(&pdev->dev, "failed to remove gpio chip\n");
|
dev_err(&pdev->dev, "failed to remove gpio chip\n");
|
||||||
fail_gpio:
|
fail_gpio:
|
||||||
pinctrl_unregister(data->pctl_dev);
|
pinctrl_unregister(data->pctl_dev);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue