aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl/intel
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2014-12-04 05:32:50 -0500
committerLinus Walleij <linus.walleij@linaro.org>2015-01-10 16:47:22 -0500
commit9eb457b547cc731bc2fc251bd79891a60c64fc3e (patch)
tree172cfa5afbf64eef973fb6151d2882dd601cfa25 /drivers/pinctrl/intel
parentb7392d2247cfe6771f95d256374f1a8e6a6f48d6 (diff)
pinctrl: cherryview: Save and restore pin configs over system sleep
Before resuming from system sleep BIOS restores its view of pin configuration. If we have configured some pins differently from that, for instance some driver requested a pin as a GPIO but it was not in GPIO mode originally, our view of the pin configuration will not match the hardware state anymore. This patch saves the pin configuration and interrupt mask registers on suspend and restores them on exit. This should make sure that the previously configured state is still in effect. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
Diffstat (limited to 'drivers/pinctrl/intel')
-rw-r--r--drivers/pinctrl/intel/pinctrl-cherryview.c104
1 files changed, 104 insertions, 0 deletions
diff --git a/drivers/pinctrl/intel/pinctrl-cherryview.c b/drivers/pinctrl/intel/pinctrl-cherryview.c
index e9f8b39d1a9f..dde67d425e9c 100644
--- a/drivers/pinctrl/intel/pinctrl-cherryview.c
+++ b/drivers/pinctrl/intel/pinctrl-cherryview.c
@@ -148,6 +148,11 @@ struct chv_community {
148 size_t ngpios; 148 size_t ngpios;
149}; 149};
150 150
151struct chv_pin_context {
152 u32 padctrl0;
153 u32 padctrl1;
154};
155
151/** 156/**
152 * struct chv_pinctrl - CHV pinctrl private structure 157 * struct chv_pinctrl - CHV pinctrl private structure
153 * @dev: Pointer to the parent device 158 * @dev: Pointer to the parent device
@@ -172,6 +177,8 @@ struct chv_pinctrl {
172 spinlock_t lock; 177 spinlock_t lock;
173 unsigned intr_lines[16]; 178 unsigned intr_lines[16];
174 const struct chv_community *community; 179 const struct chv_community *community;
180 u32 saved_intmask;
181 struct chv_pin_context *saved_pin_context;
175}; 182};
176 183
177#define gpiochip_to_pinctrl(c) container_of(c, struct chv_pinctrl, chip) 184#define gpiochip_to_pinctrl(c) container_of(c, struct chv_pinctrl, chip)
@@ -1443,6 +1450,14 @@ static int chv_pinctrl_probe(struct platform_device *pdev)
1443 spin_lock_init(&pctrl->lock); 1450 spin_lock_init(&pctrl->lock);
1444 pctrl->dev = &pdev->dev; 1451 pctrl->dev = &pdev->dev;
1445 1452
1453#ifdef CONFIG_PM_SLEEP
1454 pctrl->saved_pin_context = devm_kcalloc(pctrl->dev,
1455 pctrl->community->npins, sizeof(*pctrl->saved_pin_context),
1456 GFP_KERNEL);
1457 if (!pctrl->saved_pin_context)
1458 return -ENOMEM;
1459#endif
1460
1446 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1461 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1447 pctrl->regs = devm_ioremap_resource(&pdev->dev, res); 1462 pctrl->regs = devm_ioremap_resource(&pdev->dev, res);
1448 if (IS_ERR(pctrl->regs)) 1463 if (IS_ERR(pctrl->regs))
@@ -1486,6 +1501,94 @@ static int chv_pinctrl_remove(struct platform_device *pdev)
1486 return 0; 1501 return 0;
1487} 1502}
1488 1503
1504#ifdef CONFIG_PM_SLEEP
1505static int chv_pinctrl_suspend(struct device *dev)
1506{
1507 struct platform_device *pdev = to_platform_device(dev);
1508 struct chv_pinctrl *pctrl = platform_get_drvdata(pdev);
1509 int i;
1510
1511 pctrl->saved_intmask = readl(pctrl->regs + CHV_INTMASK);
1512
1513 for (i = 0; i < pctrl->community->npins; i++) {
1514 const struct pinctrl_pin_desc *desc;
1515 struct chv_pin_context *ctx;
1516 void __iomem *reg;
1517
1518 desc = &pctrl->community->pins[i];
1519 if (chv_pad_locked(pctrl, desc->number))
1520 continue;
1521
1522 ctx = &pctrl->saved_pin_context[i];
1523
1524 reg = chv_padreg(pctrl, desc->number, CHV_PADCTRL0);
1525 ctx->padctrl0 = readl(reg) & ~CHV_PADCTRL0_GPIORXSTATE;
1526
1527 reg = chv_padreg(pctrl, desc->number, CHV_PADCTRL1);
1528 ctx->padctrl1 = readl(reg);
1529 }
1530
1531 return 0;
1532}
1533
1534static int chv_pinctrl_resume(struct device *dev)
1535{
1536 struct platform_device *pdev = to_platform_device(dev);
1537 struct chv_pinctrl *pctrl = platform_get_drvdata(pdev);
1538 int i;
1539
1540 /*
1541 * Mask all interrupts before restoring per-pin configuration
1542 * registers because we don't know in which state BIOS left them
1543 * upon exiting suspend.
1544 */
1545 chv_writel(0, pctrl->regs + CHV_INTMASK);
1546
1547 for (i = 0; i < pctrl->community->npins; i++) {
1548 const struct pinctrl_pin_desc *desc;
1549 const struct chv_pin_context *ctx;
1550 void __iomem *reg;
1551 u32 val;
1552
1553 desc = &pctrl->community->pins[i];
1554 if (chv_pad_locked(pctrl, desc->number))
1555 continue;
1556
1557 ctx = &pctrl->saved_pin_context[i];
1558
1559 /* Only restore if our saved state differs from the current */
1560 reg = chv_padreg(pctrl, desc->number, CHV_PADCTRL0);
1561 val = readl(reg) & ~CHV_PADCTRL0_GPIORXSTATE;
1562 if (ctx->padctrl0 != val) {
1563 chv_writel(ctx->padctrl0, reg);
1564 dev_dbg(pctrl->dev, "restored pin %2u ctrl0 0x%08x\n",
1565 desc->number, readl(reg));
1566 }
1567
1568 reg = chv_padreg(pctrl, desc->number, CHV_PADCTRL1);
1569 val = readl(reg);
1570 if (ctx->padctrl1 != val) {
1571 chv_writel(ctx->padctrl1, reg);
1572 dev_dbg(pctrl->dev, "restored pin %2u ctrl1 0x%08x\n",
1573 desc->number, readl(reg));
1574 }
1575 }
1576
1577 /*
1578 * Now that all pins are restored to known state, we can restore
1579 * the interrupt mask register as well.
1580 */
1581 chv_writel(0xffff, pctrl->regs + CHV_INTSTAT);
1582 chv_writel(pctrl->saved_intmask, pctrl->regs + CHV_INTMASK);
1583
1584 return 0;
1585}
1586#endif
1587
1588static const struct dev_pm_ops chv_pinctrl_pm_ops = {
1589 SET_LATE_SYSTEM_SLEEP_PM_OPS(chv_pinctrl_suspend, chv_pinctrl_resume)
1590};
1591
1489static const struct acpi_device_id chv_pinctrl_acpi_match[] = { 1592static const struct acpi_device_id chv_pinctrl_acpi_match[] = {
1490 { "INT33FF" }, 1593 { "INT33FF" },
1491 { } 1594 { }
@@ -1498,6 +1601,7 @@ static struct platform_driver chv_pinctrl_driver = {
1498 .driver = { 1601 .driver = {
1499 .name = "cherryview-pinctrl", 1602 .name = "cherryview-pinctrl",
1500 .owner = THIS_MODULE, 1603 .owner = THIS_MODULE,
1604 .pm = &chv_pinctrl_pm_ops,
1501 .acpi_match_table = chv_pinctrl_acpi_match, 1605 .acpi_match_table = chv_pinctrl_acpi_match,
1502 }, 1606 },
1503}; 1607};