ib-drm-gpio-pdx86-rtc-wdt for v5.12-1
First part of Intel MID outdated platforms removal. The following is an automated git shortlog grouped by driver: drm/gma500: - Get rid of duplicate NULL checks - Convert to use new SCU IPC API gpio: - msic: Remove driver for deprecated platform - intel-mid: Remove driver for deprecated platform intel_mid_powerbtn: - Remove driver for deprecated platform intel_mid_thermal: - Remove driver for deprecated platform intel_scu_wdt: - Get rid of custom x86 model comparison - Drop SCU notification - Move driver from arch/x86 rtc: - mrst: Remove driver for deprecated platform watchdog: - intel-mid_wdt: Postpone IRQ handler registration till SCU is ready - intel_scu_watchdog: Remove driver for deprecated platform -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEqaflIX74DDDzMJJtb7wzTHR8rCgFAmAPDAIACgkQb7wzTHR8 rChmgw//bTsRde7Yq89gP89zRLml6L7W5gDL44Q3WD3QXox6C8scZ+i960fjD8GD bQGPfIXRTi2jXF6k5Tm7zl7dmAQGeOacbwWtbVJ36DIMOtOkOEUAN9kjOFYYBGsU oBwIpmrGV3LLZLAnXa7RhNhiUXPP/OwB71IJYu5fvYgB9mPqv3WCoiP7cA6Ok000 e99FFgvCXo2dHXQsorYkeXebER3jXcK6t7NLnq/aNVqoVM84eg0+KweErGAbZ+PQ RKHJglBvktkOYmD6I5KPEGFgBY/WtwOTL9OsED92aWQQQ4o/Ol0Wc3txXtVnJWTq jeDCAOxTcdDspAmiY7pD+qw5t1zYQrbJDbcXrMlQnfW28hLLAouxlyi3qJgKMJFx ymPMuomxvYAMoZtfPlQ/kdNJjRGgcLJ3aJVo+4Zm5qRoxqoi6SqSJDB5FlBIXOcj ZkeYLZsIKYqesRendq9tiaH6H2AWe66WkL4syp11L5BIk8dW0t3qF0jGfziYpyRl +kMaY7FPxNcPgvbB2ut9yEy5A/8gO6olR2BqLaO2NgPIFMc4udb5e2lEqzDHfFge tknJkzMFVpMHS6z5WuHqcbxgVn5ejyJikyMfTvFAXlteI4eFXWDfxMbT2Mq0DFmK qT0oST0x6iij/tro+W1alCCZiDgnOh2a4WayEiPTTZLNWDSk8kQ= =ePKv -----END PGP SIGNATURE----- Merge tag 'ib-drm-gpio-pdx86-rtc-wdt-v5.12-1' into for-next ib-drm-gpio-pdx86-rtc-wdt for v5.12-1 First part of Intel MID outdated platforms removal. The following is an automated git shortlog grouped by driver: drm/gma500: - Get rid of duplicate NULL checks - Convert to use new SCU IPC API gpio: - msic: Remove driver for deprecated platform - intel-mid: Remove driver for deprecated platform intel_mid_powerbtn: - Remove driver for deprecated platform intel_mid_thermal: - Remove driver for deprecated platform intel_scu_wdt: - Get rid of custom x86 model comparison - Drop SCU notification - Move driver from arch/x86 rtc: - mrst: Remove driver for deprecated platform watchdog: - intel-mid_wdt: Postpone IRQ handler registration till SCU is ready - intel_scu_watchdog: Remove driver for deprecated platform
This commit is contained in:
Коммит
a40f530e77
|
@ -8940,7 +8940,6 @@ L: linux-gpio@vger.kernel.org
|
|||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-gpio-intel.git
|
||||
F: drivers/gpio/gpio-ich.c
|
||||
F: drivers/gpio/gpio-intel-mid.c
|
||||
F: drivers/gpio/gpio-merrifield.c
|
||||
F: drivers/gpio/gpio-ml-ioh.c
|
||||
F: drivers/gpio/gpio-pch.c
|
||||
|
@ -9099,7 +9098,6 @@ M: Andy Shevchenko <andy@kernel.org>
|
|||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-gpio-intel.git
|
||||
F: drivers/gpio/gpio-*cove.c
|
||||
F: drivers/gpio/gpio-msic.c
|
||||
|
||||
INTEL PMIC MULTIFUNCTION DEVICE DRIVERS
|
||||
M: Andy Shevchenko <andy@kernel.org>
|
||||
|
|
|
@ -30,4 +30,3 @@ obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o
|
|||
obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o
|
||||
obj-$(subst m,y,$(CONFIG_INTEL_MID_POWER_BUTTON)) += platform_mrfld_power_btn.o
|
||||
obj-$(subst m,y,$(CONFIG_RTC_DRV_CMOS)) += platform_mrfld_rtc.o
|
||||
obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_mrfld_wdt.o
|
||||
|
|
|
@ -1249,13 +1249,6 @@ config GPIO_MAX77650
|
|||
GPIO driver for MAX77650/77651 PMIC from Maxim Semiconductor.
|
||||
These chips have a single pin that can be configured as GPIO.
|
||||
|
||||
config GPIO_MSIC
|
||||
bool "Intel MSIC mixed signal gpio support"
|
||||
depends on (X86 || COMPILE_TEST) && MFD_INTEL_MSIC
|
||||
help
|
||||
Enable support for GPIO on intel MSIC controllers found in
|
||||
intel MID devices
|
||||
|
||||
config GPIO_PALMAS
|
||||
bool "TI PALMAS series PMICs GPIO"
|
||||
depends on MFD_PALMAS
|
||||
|
@ -1451,13 +1444,6 @@ config GPIO_BT8XX
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config GPIO_INTEL_MID
|
||||
bool "Intel MID GPIO support"
|
||||
depends on X86_INTEL_MID
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say Y here to support Intel MID GPIO.
|
||||
|
||||
config GPIO_MERRIFIELD
|
||||
tristate "Intel Merrifield GPIO support"
|
||||
depends on X86_INTEL_MID
|
||||
|
|
|
@ -67,7 +67,6 @@ obj-$(CONFIG_GPIO_HISI) += gpio-hisi.o
|
|||
obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o
|
||||
obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o
|
||||
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
|
||||
obj-$(CONFIG_GPIO_INTEL_MID) += gpio-intel-mid.o
|
||||
obj-$(CONFIG_GPIO_IOP) += gpio-iop.o
|
||||
obj-$(CONFIG_GPIO_IT87) += gpio-it87.o
|
||||
obj-$(CONFIG_GPIO_IXP4XX) += gpio-ixp4xx.o
|
||||
|
|
|
@ -101,7 +101,7 @@ for a few GPIOs. Those should stay where they are.
|
|||
|
||||
At the same time it makes sense to get rid of code duplication in existing or
|
||||
new coming drivers. For example, gpio-ml-ioh should be incorporated into
|
||||
gpio-pch. In similar way gpio-intel-mid into gpio-pxa.
|
||||
gpio-pch.
|
||||
|
||||
|
||||
Generic MMIO GPIO
|
||||
|
|
|
@ -1,414 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Intel MID GPIO driver
|
||||
*
|
||||
* Copyright (c) 2008-2014,2016 Intel Corporation.
|
||||
*/
|
||||
|
||||
/* Supports:
|
||||
* Moorestown platform Langwell chip.
|
||||
* Medfield platform Penwell chip.
|
||||
* Clovertrail platform Cloverview chip.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/stddef.h>
|
||||
|
||||
#define INTEL_MID_IRQ_TYPE_EDGE (1 << 0)
|
||||
#define INTEL_MID_IRQ_TYPE_LEVEL (1 << 1)
|
||||
|
||||
/*
|
||||
* Langwell chip has 64 pins and thus there are 2 32bit registers to control
|
||||
* each feature, while Penwell chip has 96 pins for each block, and need 3 32bit
|
||||
* registers to control them, so we only define the order here instead of a
|
||||
* structure, to get a bit offset for a pin (use GPDR as an example):
|
||||
*
|
||||
* nreg = ngpio / 32;
|
||||
* reg = offset / 32;
|
||||
* bit = offset % 32;
|
||||
* reg_addr = reg_base + GPDR * nreg * 4 + reg * 4;
|
||||
*
|
||||
* so the bit of reg_addr is to control pin offset's GPDR feature
|
||||
*/
|
||||
|
||||
enum GPIO_REG {
|
||||
GPLR = 0, /* pin level read-only */
|
||||
GPDR, /* pin direction */
|
||||
GPSR, /* pin set */
|
||||
GPCR, /* pin clear */
|
||||
GRER, /* rising edge detect */
|
||||
GFER, /* falling edge detect */
|
||||
GEDR, /* edge detect result */
|
||||
GAFR, /* alt function */
|
||||
};
|
||||
|
||||
/* intel_mid gpio driver data */
|
||||
struct intel_mid_gpio_ddata {
|
||||
u16 ngpio; /* number of gpio pins */
|
||||
u32 chip_irq_type; /* chip interrupt type */
|
||||
};
|
||||
|
||||
struct intel_mid_gpio {
|
||||
struct gpio_chip chip;
|
||||
void __iomem *reg_base;
|
||||
spinlock_t lock;
|
||||
struct pci_dev *pdev;
|
||||
};
|
||||
|
||||
static void __iomem *gpio_reg(struct gpio_chip *chip, unsigned offset,
|
||||
enum GPIO_REG reg_type)
|
||||
{
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(chip);
|
||||
unsigned nreg = chip->ngpio / 32;
|
||||
u8 reg = offset / 32;
|
||||
|
||||
return priv->reg_base + reg_type * nreg * 4 + reg * 4;
|
||||
}
|
||||
|
||||
static void __iomem *gpio_reg_2bit(struct gpio_chip *chip, unsigned offset,
|
||||
enum GPIO_REG reg_type)
|
||||
{
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(chip);
|
||||
unsigned nreg = chip->ngpio / 32;
|
||||
u8 reg = offset / 16;
|
||||
|
||||
return priv->reg_base + reg_type * nreg * 4 + reg * 4;
|
||||
}
|
||||
|
||||
static int intel_gpio_request(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
void __iomem *gafr = gpio_reg_2bit(chip, offset, GAFR);
|
||||
u32 value = readl(gafr);
|
||||
int shift = (offset % 16) << 1, af = (value >> shift) & 3;
|
||||
|
||||
if (af) {
|
||||
value &= ~(3 << shift);
|
||||
writel(value, gafr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
void __iomem *gplr = gpio_reg(chip, offset, GPLR);
|
||||
|
||||
return !!(readl(gplr) & BIT(offset % 32));
|
||||
}
|
||||
|
||||
static void intel_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
void __iomem *gpsr, *gpcr;
|
||||
|
||||
if (value) {
|
||||
gpsr = gpio_reg(chip, offset, GPSR);
|
||||
writel(BIT(offset % 32), gpsr);
|
||||
} else {
|
||||
gpcr = gpio_reg(chip, offset, GPCR);
|
||||
writel(BIT(offset % 32), gpcr);
|
||||
}
|
||||
}
|
||||
|
||||
static int intel_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(chip);
|
||||
void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
|
||||
u32 value;
|
||||
unsigned long flags;
|
||||
|
||||
if (priv->pdev)
|
||||
pm_runtime_get(&priv->pdev->dev);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
value = readl(gpdr);
|
||||
value &= ~BIT(offset % 32);
|
||||
writel(value, gpdr);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
if (priv->pdev)
|
||||
pm_runtime_put(&priv->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(chip);
|
||||
void __iomem *gpdr = gpio_reg(chip, offset, GPDR);
|
||||
unsigned long flags;
|
||||
|
||||
intel_gpio_set(chip, offset, value);
|
||||
|
||||
if (priv->pdev)
|
||||
pm_runtime_get(&priv->pdev->dev);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
value = readl(gpdr);
|
||||
value |= BIT(offset % 32);
|
||||
writel(value, gpdr);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
if (priv->pdev)
|
||||
pm_runtime_put(&priv->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_mid_irq_type(struct irq_data *d, unsigned type)
|
||||
{
|
||||
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(gc);
|
||||
u32 gpio = irqd_to_hwirq(d);
|
||||
unsigned long flags;
|
||||
u32 value;
|
||||
void __iomem *grer = gpio_reg(&priv->chip, gpio, GRER);
|
||||
void __iomem *gfer = gpio_reg(&priv->chip, gpio, GFER);
|
||||
|
||||
if (gpio >= priv->chip.ngpio)
|
||||
return -EINVAL;
|
||||
|
||||
if (priv->pdev)
|
||||
pm_runtime_get(&priv->pdev->dev);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
if (type & IRQ_TYPE_EDGE_RISING)
|
||||
value = readl(grer) | BIT(gpio % 32);
|
||||
else
|
||||
value = readl(grer) & (~BIT(gpio % 32));
|
||||
writel(value, grer);
|
||||
|
||||
if (type & IRQ_TYPE_EDGE_FALLING)
|
||||
value = readl(gfer) | BIT(gpio % 32);
|
||||
else
|
||||
value = readl(gfer) & (~BIT(gpio % 32));
|
||||
writel(value, gfer);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
if (priv->pdev)
|
||||
pm_runtime_put(&priv->pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void intel_mid_irq_unmask(struct irq_data *d)
|
||||
{
|
||||
}
|
||||
|
||||
static void intel_mid_irq_mask(struct irq_data *d)
|
||||
{
|
||||
}
|
||||
|
||||
static struct irq_chip intel_mid_irqchip = {
|
||||
.name = "INTEL_MID-GPIO",
|
||||
.irq_mask = intel_mid_irq_mask,
|
||||
.irq_unmask = intel_mid_irq_unmask,
|
||||
.irq_set_type = intel_mid_irq_type,
|
||||
};
|
||||
|
||||
static const struct intel_mid_gpio_ddata gpio_lincroft = {
|
||||
.ngpio = 64,
|
||||
};
|
||||
|
||||
static const struct intel_mid_gpio_ddata gpio_penwell_aon = {
|
||||
.ngpio = 96,
|
||||
.chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
|
||||
};
|
||||
|
||||
static const struct intel_mid_gpio_ddata gpio_penwell_core = {
|
||||
.ngpio = 96,
|
||||
.chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
|
||||
};
|
||||
|
||||
static const struct intel_mid_gpio_ddata gpio_cloverview_aon = {
|
||||
.ngpio = 96,
|
||||
.chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE | INTEL_MID_IRQ_TYPE_LEVEL,
|
||||
};
|
||||
|
||||
static const struct intel_mid_gpio_ddata gpio_cloverview_core = {
|
||||
.ngpio = 96,
|
||||
.chip_irq_type = INTEL_MID_IRQ_TYPE_EDGE,
|
||||
};
|
||||
|
||||
static const struct pci_device_id intel_gpio_ids[] = {
|
||||
{
|
||||
/* Lincroft */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080f),
|
||||
.driver_data = (kernel_ulong_t)&gpio_lincroft,
|
||||
},
|
||||
{
|
||||
/* Penwell AON */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081f),
|
||||
.driver_data = (kernel_ulong_t)&gpio_penwell_aon,
|
||||
},
|
||||
{
|
||||
/* Penwell Core */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x081a),
|
||||
.driver_data = (kernel_ulong_t)&gpio_penwell_core,
|
||||
},
|
||||
{
|
||||
/* Cloverview Aon */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08eb),
|
||||
.driver_data = (kernel_ulong_t)&gpio_cloverview_aon,
|
||||
},
|
||||
{
|
||||
/* Cloverview Core */
|
||||
PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x08f7),
|
||||
.driver_data = (kernel_ulong_t)&gpio_cloverview_core,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
static void intel_mid_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct gpio_chip *gc = irq_desc_get_handler_data(desc);
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(gc);
|
||||
struct irq_data *data = irq_desc_get_irq_data(desc);
|
||||
struct irq_chip *chip = irq_data_get_irq_chip(data);
|
||||
u32 base, gpio, mask;
|
||||
unsigned long pending;
|
||||
void __iomem *gedr;
|
||||
|
||||
/* check GPIO controller to check which pin triggered the interrupt */
|
||||
for (base = 0; base < priv->chip.ngpio; base += 32) {
|
||||
gedr = gpio_reg(&priv->chip, base, GEDR);
|
||||
while ((pending = readl(gedr))) {
|
||||
gpio = __ffs(pending);
|
||||
mask = BIT(gpio);
|
||||
/* Clear before handling so we can't lose an edge */
|
||||
writel(mask, gedr);
|
||||
generic_handle_irq(irq_find_mapping(gc->irq.domain,
|
||||
base + gpio));
|
||||
}
|
||||
}
|
||||
|
||||
chip->irq_eoi(data);
|
||||
}
|
||||
|
||||
static int intel_mid_irq_init_hw(struct gpio_chip *chip)
|
||||
{
|
||||
struct intel_mid_gpio *priv = gpiochip_get_data(chip);
|
||||
void __iomem *reg;
|
||||
unsigned base;
|
||||
|
||||
for (base = 0; base < priv->chip.ngpio; base += 32) {
|
||||
/* Clear the rising-edge detect register */
|
||||
reg = gpio_reg(&priv->chip, base, GRER);
|
||||
writel(0, reg);
|
||||
/* Clear the falling-edge detect register */
|
||||
reg = gpio_reg(&priv->chip, base, GFER);
|
||||
writel(0, reg);
|
||||
/* Clear the edge detect status register */
|
||||
reg = gpio_reg(&priv->chip, base, GEDR);
|
||||
writel(~0, reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused intel_gpio_runtime_idle(struct device *dev)
|
||||
{
|
||||
int err = pm_schedule_suspend(dev, 500);
|
||||
return err ?: -EBUSY;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops intel_gpio_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(NULL, NULL, intel_gpio_runtime_idle)
|
||||
};
|
||||
|
||||
static int intel_gpio_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct intel_mid_gpio *priv;
|
||||
u32 gpio_base;
|
||||
u32 irq_base;
|
||||
int retval;
|
||||
struct gpio_irq_chip *girq;
|
||||
struct intel_mid_gpio_ddata *ddata =
|
||||
(struct intel_mid_gpio_ddata *)id->driver_data;
|
||||
|
||||
retval = pcim_enable_device(pdev);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = pcim_iomap_regions(pdev, 1 << 0 | 1 << 1, pci_name(pdev));
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "I/O memory mapping error\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
base = pcim_iomap_table(pdev)[1];
|
||||
|
||||
irq_base = readl(base);
|
||||
gpio_base = readl(sizeof(u32) + base);
|
||||
|
||||
/* release the IO mapping, since we already get the info from bar1 */
|
||||
pcim_iounmap_regions(pdev, 1 << 1);
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->reg_base = pcim_iomap_table(pdev)[0];
|
||||
priv->chip.label = dev_name(&pdev->dev);
|
||||
priv->chip.parent = &pdev->dev;
|
||||
priv->chip.request = intel_gpio_request;
|
||||
priv->chip.direction_input = intel_gpio_direction_input;
|
||||
priv->chip.direction_output = intel_gpio_direction_output;
|
||||
priv->chip.get = intel_gpio_get;
|
||||
priv->chip.set = intel_gpio_set;
|
||||
priv->chip.base = gpio_base;
|
||||
priv->chip.ngpio = ddata->ngpio;
|
||||
priv->chip.can_sleep = false;
|
||||
priv->pdev = pdev;
|
||||
|
||||
spin_lock_init(&priv->lock);
|
||||
|
||||
girq = &priv->chip.irq;
|
||||
girq->chip = &intel_mid_irqchip;
|
||||
girq->init_hw = intel_mid_irq_init_hw;
|
||||
girq->parent_handler = intel_mid_irq_handler;
|
||||
girq->num_parents = 1;
|
||||
girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents,
|
||||
sizeof(*girq->parents),
|
||||
GFP_KERNEL);
|
||||
if (!girq->parents)
|
||||
return -ENOMEM;
|
||||
girq->parents[0] = pdev->irq;
|
||||
girq->first = irq_base;
|
||||
girq->default_type = IRQ_TYPE_NONE;
|
||||
girq->handler = handle_simple_irq;
|
||||
|
||||
pci_set_drvdata(pdev, priv);
|
||||
|
||||
retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "gpiochip_add error %d\n", retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct pci_driver intel_gpio_driver = {
|
||||
.name = "intel_mid_gpio",
|
||||
.id_table = intel_gpio_ids,
|
||||
.probe = intel_gpio_probe,
|
||||
.driver = {
|
||||
.pm = &intel_gpio_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
builtin_pci_driver(intel_gpio_driver);
|
|
@ -1,314 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Intel Medfield MSIC GPIO driver>
|
||||
* Copyright (c) 2011, Intel Corporation.
|
||||
*
|
||||
* Author: Mathias Nyman <mathias.nyman@linux.intel.com>
|
||||
* Based on intel_pmic_gpio.c
|
||||
*/
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/intel_msic.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/* the offset for the mapping of global gpio pin to irq */
|
||||
#define MSIC_GPIO_IRQ_OFFSET 0x100
|
||||
|
||||
#define MSIC_GPIO_DIR_IN 0
|
||||
#define MSIC_GPIO_DIR_OUT BIT(5)
|
||||
#define MSIC_GPIO_TRIG_FALL BIT(1)
|
||||
#define MSIC_GPIO_TRIG_RISE BIT(2)
|
||||
|
||||
/* masks for msic gpio output GPIOxxxxCTLO registers */
|
||||
#define MSIC_GPIO_DIR_MASK BIT(5)
|
||||
#define MSIC_GPIO_DRV_MASK BIT(4)
|
||||
#define MSIC_GPIO_REN_MASK BIT(3)
|
||||
#define MSIC_GPIO_RVAL_MASK (BIT(2) | BIT(1))
|
||||
#define MSIC_GPIO_DOUT_MASK BIT(0)
|
||||
|
||||
/* masks for msic gpio input GPIOxxxxCTLI registers */
|
||||
#define MSIC_GPIO_GLBYP_MASK BIT(5)
|
||||
#define MSIC_GPIO_DBNC_MASK (BIT(4) | BIT(3))
|
||||
#define MSIC_GPIO_INTCNT_MASK (BIT(2) | BIT(1))
|
||||
#define MSIC_GPIO_DIN_MASK BIT(0)
|
||||
|
||||
#define MSIC_NUM_GPIO 24
|
||||
|
||||
struct msic_gpio {
|
||||
struct platform_device *pdev;
|
||||
struct mutex buslock;
|
||||
struct gpio_chip chip;
|
||||
int irq;
|
||||
unsigned irq_base;
|
||||
unsigned long trig_change_mask;
|
||||
unsigned trig_type;
|
||||
};
|
||||
|
||||
/*
|
||||
* MSIC has 24 gpios, 16 low voltage (1.2-1.8v) and 8 high voltage (3v).
|
||||
* Both the high and low voltage gpios are divided in two banks.
|
||||
* GPIOs are numbered with GPIO0LV0 as gpio_base in the following order:
|
||||
* GPIO0LV0..GPIO0LV7: low voltage, bank 0, gpio_base
|
||||
* GPIO1LV0..GPIO1LV7: low voltage, bank 1, gpio_base + 8
|
||||
* GPIO0HV0..GPIO0HV3: high voltage, bank 0, gpio_base + 16
|
||||
* GPIO1HV0..GPIO1HV3: high voltage, bank 1, gpio_base + 20
|
||||
*/
|
||||
|
||||
static int msic_gpio_to_ireg(unsigned offset)
|
||||
{
|
||||
if (offset >= MSIC_NUM_GPIO)
|
||||
return -EINVAL;
|
||||
|
||||
if (offset < 8)
|
||||
return INTEL_MSIC_GPIO0LV0CTLI - offset;
|
||||
if (offset < 16)
|
||||
return INTEL_MSIC_GPIO1LV0CTLI - offset + 8;
|
||||
if (offset < 20)
|
||||
return INTEL_MSIC_GPIO0HV0CTLI - offset + 16;
|
||||
|
||||
return INTEL_MSIC_GPIO1HV0CTLI - offset + 20;
|
||||
}
|
||||
|
||||
static int msic_gpio_to_oreg(unsigned offset)
|
||||
{
|
||||
if (offset >= MSIC_NUM_GPIO)
|
||||
return -EINVAL;
|
||||
|
||||
if (offset < 8)
|
||||
return INTEL_MSIC_GPIO0LV0CTLO - offset;
|
||||
if (offset < 16)
|
||||
return INTEL_MSIC_GPIO1LV0CTLO - offset + 8;
|
||||
if (offset < 20)
|
||||
return INTEL_MSIC_GPIO0HV0CTLO - offset + 16;
|
||||
|
||||
return INTEL_MSIC_GPIO1HV0CTLO - offset + 20;
|
||||
}
|
||||
|
||||
static int msic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
int reg;
|
||||
|
||||
reg = msic_gpio_to_oreg(offset);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
return intel_msic_reg_update(reg, MSIC_GPIO_DIR_IN, MSIC_GPIO_DIR_MASK);
|
||||
}
|
||||
|
||||
static int msic_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned offset, int value)
|
||||
{
|
||||
int reg;
|
||||
unsigned mask;
|
||||
|
||||
value = (!!value) | MSIC_GPIO_DIR_OUT;
|
||||
mask = MSIC_GPIO_DIR_MASK | MSIC_GPIO_DOUT_MASK;
|
||||
|
||||
reg = msic_gpio_to_oreg(offset);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
return intel_msic_reg_update(reg, value, mask);
|
||||
}
|
||||
|
||||
static int msic_gpio_get(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
u8 r;
|
||||
int ret;
|
||||
int reg;
|
||||
|
||||
reg = msic_gpio_to_ireg(offset);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
ret = intel_msic_reg_read(reg, &r);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !!(r & MSIC_GPIO_DIN_MASK);
|
||||
}
|
||||
|
||||
static void msic_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
int reg;
|
||||
|
||||
reg = msic_gpio_to_oreg(offset);
|
||||
if (reg < 0)
|
||||
return;
|
||||
|
||||
intel_msic_reg_update(reg, !!value , MSIC_GPIO_DOUT_MASK);
|
||||
}
|
||||
|
||||
/*
|
||||
* This is called from genirq with mg->buslock locked and
|
||||
* irq_desc->lock held. We can not access the scu bus here, so we
|
||||
* store the change and update in the bus_sync_unlock() function below
|
||||
*/
|
||||
static int msic_irq_type(struct irq_data *data, unsigned type)
|
||||
{
|
||||
struct msic_gpio *mg = irq_data_get_irq_chip_data(data);
|
||||
u32 gpio = data->irq - mg->irq_base;
|
||||
|
||||
if (gpio >= mg->chip.ngpio)
|
||||
return -EINVAL;
|
||||
|
||||
/* mark for which gpio the trigger changed, protected by buslock */
|
||||
mg->trig_change_mask |= (1 << gpio);
|
||||
mg->trig_type = type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msic_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
|
||||
{
|
||||
struct msic_gpio *mg = gpiochip_get_data(chip);
|
||||
return mg->irq_base + offset;
|
||||
}
|
||||
|
||||
static void msic_bus_lock(struct irq_data *data)
|
||||
{
|
||||
struct msic_gpio *mg = irq_data_get_irq_chip_data(data);
|
||||
mutex_lock(&mg->buslock);
|
||||
}
|
||||
|
||||
static void msic_bus_sync_unlock(struct irq_data *data)
|
||||
{
|
||||
struct msic_gpio *mg = irq_data_get_irq_chip_data(data);
|
||||
int offset;
|
||||
int reg;
|
||||
u8 trig = 0;
|
||||
|
||||
/* We can only get one change at a time as the buslock covers the
|
||||
entire transaction. The irq_desc->lock is dropped before we are
|
||||
called but that is fine */
|
||||
if (mg->trig_change_mask) {
|
||||
offset = __ffs(mg->trig_change_mask);
|
||||
|
||||
reg = msic_gpio_to_ireg(offset);
|
||||
if (reg < 0)
|
||||
goto out;
|
||||
|
||||
if (mg->trig_type & IRQ_TYPE_EDGE_RISING)
|
||||
trig |= MSIC_GPIO_TRIG_RISE;
|
||||
if (mg->trig_type & IRQ_TYPE_EDGE_FALLING)
|
||||
trig |= MSIC_GPIO_TRIG_FALL;
|
||||
|
||||
intel_msic_reg_update(reg, trig, MSIC_GPIO_INTCNT_MASK);
|
||||
mg->trig_change_mask = 0;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&mg->buslock);
|
||||
}
|
||||
|
||||
/* Firmware does all the masking and unmasking for us, no masking here. */
|
||||
static void msic_irq_unmask(struct irq_data *data) { }
|
||||
|
||||
static void msic_irq_mask(struct irq_data *data) { }
|
||||
|
||||
static struct irq_chip msic_irqchip = {
|
||||
.name = "MSIC-GPIO",
|
||||
.irq_mask = msic_irq_mask,
|
||||
.irq_unmask = msic_irq_unmask,
|
||||
.irq_set_type = msic_irq_type,
|
||||
.irq_bus_lock = msic_bus_lock,
|
||||
.irq_bus_sync_unlock = msic_bus_sync_unlock,
|
||||
};
|
||||
|
||||
static void msic_gpio_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_data *data = irq_desc_get_irq_data(desc);
|
||||
struct msic_gpio *mg = irq_data_get_irq_handler_data(data);
|
||||
struct irq_chip *chip = irq_data_get_irq_chip(data);
|
||||
struct intel_msic *msic = pdev_to_intel_msic(mg->pdev);
|
||||
unsigned long pending;
|
||||
int i;
|
||||
int bitnr;
|
||||
u8 pin;
|
||||
|
||||
for (i = 0; i < (mg->chip.ngpio / BITS_PER_BYTE); i++) {
|
||||
intel_msic_irq_read(msic, INTEL_MSIC_GPIO0LVIRQ + i, &pin);
|
||||
pending = pin;
|
||||
|
||||
for_each_set_bit(bitnr, &pending, BITS_PER_BYTE)
|
||||
generic_handle_irq(mg->irq_base + i * BITS_PER_BYTE + bitnr);
|
||||
}
|
||||
chip->irq_eoi(data);
|
||||
}
|
||||
|
||||
static int platform_msic_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct intel_msic_gpio_pdata *pdata = dev_get_platdata(dev);
|
||||
struct msic_gpio *mg;
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
int retval;
|
||||
int i;
|
||||
|
||||
if (irq < 0) {
|
||||
dev_err(dev, "no IRQ line: %d\n", irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
if (!pdata || !pdata->gpio_base) {
|
||||
dev_err(dev, "incorrect or missing platform data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mg = kzalloc(sizeof(*mg), GFP_KERNEL);
|
||||
if (!mg)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(dev, mg);
|
||||
|
||||
mg->pdev = pdev;
|
||||
mg->irq = irq;
|
||||
mg->irq_base = pdata->gpio_base + MSIC_GPIO_IRQ_OFFSET;
|
||||
mg->chip.label = "msic_gpio";
|
||||
mg->chip.direction_input = msic_gpio_direction_input;
|
||||
mg->chip.direction_output = msic_gpio_direction_output;
|
||||
mg->chip.get = msic_gpio_get;
|
||||
mg->chip.set = msic_gpio_set;
|
||||
mg->chip.to_irq = msic_gpio_to_irq;
|
||||
mg->chip.base = pdata->gpio_base;
|
||||
mg->chip.ngpio = MSIC_NUM_GPIO;
|
||||
mg->chip.can_sleep = true;
|
||||
mg->chip.parent = dev;
|
||||
|
||||
mutex_init(&mg->buslock);
|
||||
|
||||
retval = gpiochip_add_data(&mg->chip, mg);
|
||||
if (retval) {
|
||||
dev_err(dev, "Adding MSIC gpio chip failed\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; i < mg->chip.ngpio; i++) {
|
||||
irq_set_chip_data(i + mg->irq_base, mg);
|
||||
irq_set_chip_and_handler(i + mg->irq_base,
|
||||
&msic_irqchip,
|
||||
handle_simple_irq);
|
||||
}
|
||||
irq_set_chained_handler_and_data(mg->irq, msic_gpio_irq_handler, mg);
|
||||
|
||||
return 0;
|
||||
err:
|
||||
kfree(mg);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static struct platform_driver platform_msic_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "msic_gpio",
|
||||
},
|
||||
.probe = platform_msic_gpio_probe,
|
||||
};
|
||||
|
||||
static int __init platform_msic_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&platform_msic_gpio_driver);
|
||||
}
|
||||
subsys_initcall(platform_msic_gpio_init);
|
|
@ -30,6 +30,7 @@ config DRM_GMA3600
|
|||
config DRM_MEDFIELD
|
||||
bool "Intel Medfield support (Experimental)"
|
||||
depends on DRM_GMA500 && X86_INTEL_MID
|
||||
select INTEL_SCU_IPC
|
||||
help
|
||||
Say yes to include support for the Intel Medfield platform.
|
||||
|
||||
|
|
|
@ -8,8 +8,6 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/gpio/machine.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
#include "mdfld_dsi_output.h"
|
||||
#include "mdfld_output.h"
|
||||
#include "mid_bios.h"
|
||||
|
|
|
@ -30,8 +30,6 @@
|
|||
#include <linux/pm_runtime.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
#include "mdfld_dsi_dpi.h"
|
||||
#include "mdfld_dsi_output.h"
|
||||
#include "mdfld_dsi_pkg_sender.h"
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
* Scott Rowe <scott.m.rowe@intel.com>
|
||||
*/
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
#include "mdfld_output.h"
|
||||
#include "mdfld_dsi_dpi.h"
|
||||
#include "mdfld_dsi_output.h"
|
||||
|
@ -58,11 +60,14 @@ static void mdfld_init_panel(struct drm_device *dev, int mipi_pipe,
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
int mdfld_output_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
dev_priv->scu = devm_intel_scu_ipc_dev_get(&dev->pdev->dev);
|
||||
if (!dev_priv->scu)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
/* FIXME: hardcoded for now */
|
||||
dev_priv->mdfld_panel_id = TC35876X;
|
||||
/* MIPI panel 1 */
|
||||
|
@ -71,4 +76,3 @@ int mdfld_output_init(struct drm_device *dev)
|
|||
mdfld_init_panel(dev, 1, HDMI);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,9 +10,6 @@
|
|||
#include <linux/dmi.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include <asm/intel-mid.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
#include <drm/drm.h>
|
||||
|
||||
#include "intel_bios.h"
|
||||
|
|
|
@ -428,6 +428,8 @@ struct psb_ops;
|
|||
|
||||
#define PSB_NUM_PIPE 3
|
||||
|
||||
struct intel_scu_ipc_dev;
|
||||
|
||||
struct drm_psb_private {
|
||||
struct drm_device *dev;
|
||||
struct pci_dev *aux_pdev; /* Currently only used by mrst */
|
||||
|
@ -567,6 +569,7 @@ struct drm_psb_private {
|
|||
* Used for modifying backlight from
|
||||
* xrandr -- consider removing and using HAL instead
|
||||
*/
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
struct backlight_device *backlight_device;
|
||||
struct drm_property *backlight_property;
|
||||
bool backlight_enabled;
|
||||
|
|
|
@ -444,6 +444,7 @@ static inline u16 calc_clkdiv(unsigned long baseclk, unsigned int f)
|
|||
|
||||
static void tc35876x_brightness_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
u8 pwmctrl;
|
||||
u16 clkdiv;
|
||||
|
@ -451,23 +452,23 @@ static void tc35876x_brightness_init(struct drm_device *dev)
|
|||
/* Make sure the PWM reference is the 19.2 MHz system clock. Read first
|
||||
* instead of setting directly to catch potential conflicts between PWM
|
||||
* users. */
|
||||
ret = intel_scu_ipc_ioread8(GPIOPWMCTRL, &pwmctrl);
|
||||
ret = intel_scu_ipc_dev_ioread8(dev_priv->scu, GPIOPWMCTRL, &pwmctrl);
|
||||
if (ret || pwmctrl != 0x01) {
|
||||
if (ret)
|
||||
dev_err(&dev->pdev->dev, "GPIOPWMCTRL read failed\n");
|
||||
else
|
||||
dev_warn(&dev->pdev->dev, "GPIOPWMCTRL was not set to system clock (pwmctrl = 0x%02x)\n", pwmctrl);
|
||||
|
||||
ret = intel_scu_ipc_iowrite8(GPIOPWMCTRL, 0x01);
|
||||
ret = intel_scu_ipc_dev_iowrite8(dev_priv->scu, GPIOPWMCTRL, 0x01);
|
||||
if (ret)
|
||||
dev_err(&dev->pdev->dev, "GPIOPWMCTRL set failed\n");
|
||||
}
|
||||
|
||||
clkdiv = calc_clkdiv(SYSTEMCLK, PWM_FREQUENCY);
|
||||
|
||||
ret = intel_scu_ipc_iowrite8(PWM0CLKDIV1, (clkdiv >> 8) & 0xff);
|
||||
ret = intel_scu_ipc_dev_iowrite8(dev_priv->scu, PWM0CLKDIV1, (clkdiv >> 8) & 0xff);
|
||||
if (!ret)
|
||||
ret = intel_scu_ipc_iowrite8(PWM0CLKDIV0, clkdiv & 0xff);
|
||||
ret = intel_scu_ipc_dev_iowrite8(dev_priv->scu, PWM0CLKDIV0, clkdiv & 0xff);
|
||||
|
||||
if (ret)
|
||||
dev_err(&dev->pdev->dev, "PWM0CLKDIV set failed\n");
|
||||
|
@ -480,6 +481,7 @@ static void tc35876x_brightness_init(struct drm_device *dev)
|
|||
|
||||
void tc35876x_brightness_control(struct drm_device *dev, int level)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
u8 duty_val;
|
||||
u8 panel_duty_val;
|
||||
|
@ -495,7 +497,7 @@ void tc35876x_brightness_control(struct drm_device *dev, int level)
|
|||
panel_duty_val = (2 * level - 100) * 0xA9 /
|
||||
MDFLD_DSI_BRIGHTNESS_MAX_LEVEL + 0x56;
|
||||
|
||||
ret = intel_scu_ipc_iowrite8(PWM0DUTYCYCLE, duty_val);
|
||||
ret = intel_scu_ipc_dev_iowrite8(dev_priv->scu, PWM0DUTYCYCLE, duty_val);
|
||||
if (ret)
|
||||
dev_err(&tc35876x_client->dev, "%s: ipc write fail\n",
|
||||
__func__);
|
||||
|
@ -516,11 +518,9 @@ void tc35876x_toshiba_bridge_panel_off(struct drm_device *dev)
|
|||
|
||||
dev_dbg(&tc35876x_client->dev, "%s\n", __func__);
|
||||
|
||||
if (bridge_bl_enable)
|
||||
gpiod_set_value_cansleep(bridge_bl_enable, 0);
|
||||
gpiod_set_value_cansleep(bridge_bl_enable, 0);
|
||||
|
||||
if (backlight_voltage)
|
||||
gpiod_set_value_cansleep(backlight_voltage, 0);
|
||||
gpiod_set_value_cansleep(backlight_voltage, 0);
|
||||
}
|
||||
|
||||
void tc35876x_toshiba_bridge_panel_on(struct drm_device *dev)
|
||||
|
@ -565,8 +565,7 @@ void tc35876x_toshiba_bridge_panel_on(struct drm_device *dev)
|
|||
"i2c write failed (%d)\n", ret);
|
||||
}
|
||||
|
||||
if (bridge_bl_enable)
|
||||
gpiod_set_value_cansleep(bridge_bl_enable, 1);
|
||||
gpiod_set_value_cansleep(bridge_bl_enable, 1);
|
||||
|
||||
tc35876x_brightness_control(dev, dev_priv->brightness_adjusted);
|
||||
}
|
||||
|
@ -640,20 +639,17 @@ static int tc35876x_bridge_probe(struct i2c_client *client,
|
|||
bridge_reset = devm_gpiod_get_optional(&client->dev, "bridge-reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(bridge_reset))
|
||||
return PTR_ERR(bridge_reset);
|
||||
if (bridge_reset)
|
||||
gpiod_set_consumer_name(bridge_reset, "tc35876x bridge reset");
|
||||
gpiod_set_consumer_name(bridge_reset, "tc35876x bridge reset");
|
||||
|
||||
bridge_bl_enable = devm_gpiod_get_optional(&client->dev, "bl-en", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(bridge_bl_enable))
|
||||
return PTR_ERR(bridge_bl_enable);
|
||||
if (bridge_bl_enable)
|
||||
gpiod_set_consumer_name(bridge_bl_enable, "tc35876x panel bl en");
|
||||
gpiod_set_consumer_name(bridge_bl_enable, "tc35876x panel bl en");
|
||||
|
||||
backlight_voltage = devm_gpiod_get_optional(&client->dev, "vadd", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(backlight_voltage))
|
||||
return PTR_ERR(backlight_voltage);
|
||||
if (backlight_voltage)
|
||||
gpiod_set_consumer_name(backlight_voltage, "tc35876x panel vadd");
|
||||
gpiod_set_consumer_name(backlight_voltage, "tc35876x panel vadd");
|
||||
|
||||
tc35876x_client = client;
|
||||
|
||||
|
|
|
@ -1425,6 +1425,14 @@ config INTEL_SCU_PLATFORM
|
|||
and SCU (sometimes called PMC as well). The driver currently
|
||||
supports Intel Elkhart Lake and compatible platforms.
|
||||
|
||||
config INTEL_SCU_WDT
|
||||
bool
|
||||
default INTEL_SCU_PCI
|
||||
depends on INTEL_MID_WATCHDOG
|
||||
help
|
||||
This is a specific platform code to instantiate watchdog device
|
||||
on ACPI-based Intel MID platforms.
|
||||
|
||||
config INTEL_SCU_IPC_UTIL
|
||||
tristate "Intel SCU IPC utility driver"
|
||||
depends on INTEL_SCU
|
||||
|
|
|
@ -146,6 +146,7 @@ obj-$(CONFIG_INTEL_PUNIT_IPC) += intel_punit_ipc.o
|
|||
obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o
|
||||
obj-$(CONFIG_INTEL_SCU_PCI) += intel_scu_pcidrv.o
|
||||
obj-$(CONFIG_INTEL_SCU_PLATFORM) += intel_scu_pltdrv.o
|
||||
obj-$(CONFIG_INTEL_SCU_WDT) += intel_scu_wdt.o
|
||||
obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o
|
||||
obj-$(CONFIG_INTEL_TELEMETRY) += intel_telemetry_core.o \
|
||||
intel_telemetry_pltdrv.o \
|
||||
|
|
|
@ -11,8 +11,9 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/intel-mid_wdt.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/intel-mid.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include <asm/io_apic.h>
|
||||
#include <asm/hw_irq.h>
|
||||
|
||||
|
@ -49,34 +50,26 @@ static struct intel_mid_wdt_pdata tangier_pdata = {
|
|||
.probe = tangier_probe,
|
||||
};
|
||||
|
||||
static int wdt_scu_status_change(struct notifier_block *nb,
|
||||
unsigned long code, void *data)
|
||||
{
|
||||
if (code == SCU_DOWN) {
|
||||
platform_device_unregister(&wdt_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return platform_device_register(&wdt_dev);
|
||||
}
|
||||
|
||||
static struct notifier_block wdt_scu_notifier = {
|
||||
.notifier_call = wdt_scu_status_change,
|
||||
static const struct x86_cpu_id intel_mid_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT_MID, &tangier_pdata),
|
||||
{}
|
||||
};
|
||||
|
||||
static int __init register_mid_wdt(void)
|
||||
{
|
||||
if (intel_mid_identify_cpu() != INTEL_MID_CPU_CHIP_TANGIER)
|
||||
const struct x86_cpu_id *id;
|
||||
|
||||
id = x86_match_cpu(intel_mid_cpu_ids);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
wdt_dev.dev.platform_data = &tangier_pdata;
|
||||
|
||||
/*
|
||||
* We need to be sure that the SCU IPC is ready before watchdog device
|
||||
* can be registered:
|
||||
*/
|
||||
intel_scu_notifier_add(&wdt_scu_notifier);
|
||||
|
||||
return 0;
|
||||
wdt_dev.dev.platform_data = (const struct intel_mid_wdt_pdata *)id->driver_data;
|
||||
return platform_device_register(&wdt_dev);
|
||||
}
|
||||
arch_initcall(register_mid_wdt);
|
||||
|
||||
static void __exit unregister_mid_wdt(void)
|
||||
{
|
||||
platform_device_unregister(&wdt_dev);
|
||||
}
|
||||
__exitcall(unregister_mid_wdt);
|
|
@ -973,18 +973,6 @@ config RTC_DRV_ALPHA
|
|||
Direct support for the real-time clock found on every Alpha
|
||||
system, specifically MC146818 compatibles. If in doubt, say Y.
|
||||
|
||||
config RTC_DRV_VRTC
|
||||
tristate "Virtual RTC for Intel MID platforms"
|
||||
depends on X86_INTEL_MID
|
||||
default y if X86_INTEL_MID
|
||||
|
||||
help
|
||||
Say "yes" here to get direct support for the real time clock
|
||||
found on Moorestown platforms. The VRTC is a emulated RTC that
|
||||
derives its clock source from a real RTC in the PMIC. The MC146818
|
||||
style programming interface is mostly conserved, but any
|
||||
updates are done via IPC calls to the system controller FW.
|
||||
|
||||
config RTC_DRV_DS1216
|
||||
tristate "Dallas DS1216"
|
||||
depends on SNI_RM
|
||||
|
|
|
@ -174,7 +174,6 @@ obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
|
|||
obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o
|
||||
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
|
||||
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
|
||||
obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o
|
||||
obj-$(CONFIG_RTC_DRV_VT8500) += rtc-vt8500.o
|
||||
obj-$(CONFIG_RTC_DRV_WILCO_EC) += rtc-wilco-ec.o
|
||||
obj-$(CONFIG_RTC_DRV_WM831X) += rtc-wm831x.o
|
||||
|
|
|
@ -1,521 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* rtc-mrst.c: Driver for Moorestown virtual RTC
|
||||
*
|
||||
* (C) Copyright 2009 Intel Corporation
|
||||
* Author: Jacob Pan (jacob.jun.pan@intel.com)
|
||||
* Feng Tang (feng.tang@intel.com)
|
||||
*
|
||||
* Note:
|
||||
* VRTC is emulated by system controller firmware, the real HW
|
||||
* RTC is located in the PMIC device. SCU FW shadows PMIC RTC
|
||||
* in a memory mapped IO space that is visible to the host IA
|
||||
* processor.
|
||||
*
|
||||
* This driver is based upon drivers/rtc/rtc-cmos.c
|
||||
*/
|
||||
|
||||
/*
|
||||
* Note:
|
||||
* * vRTC only supports binary mode and 24H mode
|
||||
* * vRTC only support PIE and AIE, no UIE, and its PIE only happens
|
||||
* at 23:59:59pm everyday, no support for adjustable frequency
|
||||
* * Alarm function is also limited to hr/min/sec.
|
||||
*/
|
||||
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mc146818rtc.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sfi.h>
|
||||
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include <asm/intel-mid.h>
|
||||
#include <asm/intel_mid_vrtc.h>
|
||||
|
||||
struct mrst_rtc {
|
||||
struct rtc_device *rtc;
|
||||
struct device *dev;
|
||||
int irq;
|
||||
|
||||
u8 enabled_wake;
|
||||
u8 suspend_ctrl;
|
||||
};
|
||||
|
||||
static const char driver_name[] = "rtc_mrst";
|
||||
|
||||
#define RTC_IRQMASK (RTC_PF | RTC_AF)
|
||||
|
||||
static inline int is_intr(u8 rtc_intr)
|
||||
{
|
||||
if (!(rtc_intr & RTC_IRQF))
|
||||
return 0;
|
||||
return rtc_intr & RTC_IRQMASK;
|
||||
}
|
||||
|
||||
static inline unsigned char vrtc_is_updating(void)
|
||||
{
|
||||
unsigned char uip;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rtc_lock, flags);
|
||||
uip = (vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP);
|
||||
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||
return uip;
|
||||
}
|
||||
|
||||
/*
|
||||
* rtc_time's year contains the increment over 1900, but vRTC's YEAR
|
||||
* register can't be programmed to value larger than 0x64, so vRTC
|
||||
* driver chose to use 1972 (1970 is UNIX time start point) as the base,
|
||||
* and does the translation at read/write time.
|
||||
*
|
||||
* Why not just use 1970 as the offset? it's because using 1972 will
|
||||
* make it consistent in leap year setting for both vrtc and low-level
|
||||
* physical rtc devices. Then why not use 1960 as the offset? If we use
|
||||
* 1960, for a device's first use, its YEAR register is 0 and the system
|
||||
* year will be parsed as 1960 which is not a valid UNIX time and will
|
||||
* cause many applications to fail mysteriously.
|
||||
*/
|
||||
static int mrst_read_time(struct device *dev, struct rtc_time *time)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (vrtc_is_updating())
|
||||
msleep(20);
|
||||
|
||||
spin_lock_irqsave(&rtc_lock, flags);
|
||||
time->tm_sec = vrtc_cmos_read(RTC_SECONDS);
|
||||
time->tm_min = vrtc_cmos_read(RTC_MINUTES);
|
||||
time->tm_hour = vrtc_cmos_read(RTC_HOURS);
|
||||
time->tm_mday = vrtc_cmos_read(RTC_DAY_OF_MONTH);
|
||||
time->tm_mon = vrtc_cmos_read(RTC_MONTH);
|
||||
time->tm_year = vrtc_cmos_read(RTC_YEAR);
|
||||
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||
|
||||
/* Adjust for the 1972/1900 */
|
||||
time->tm_year += 72;
|
||||
time->tm_mon--;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mrst_set_time(struct device *dev, struct rtc_time *time)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
unsigned char mon, day, hrs, min, sec;
|
||||
unsigned int yrs;
|
||||
|
||||
yrs = time->tm_year;
|
||||
mon = time->tm_mon + 1; /* tm_mon starts at zero */
|
||||
day = time->tm_mday;
|
||||
hrs = time->tm_hour;
|
||||
min = time->tm_min;
|
||||
sec = time->tm_sec;
|
||||
|
||||
if (yrs < 72 || yrs > 172)
|
||||
return -EINVAL;
|
||||
yrs -= 72;
|
||||
|
||||
spin_lock_irqsave(&rtc_lock, flags);
|
||||
|
||||
vrtc_cmos_write(yrs, RTC_YEAR);
|
||||
vrtc_cmos_write(mon, RTC_MONTH);
|
||||
vrtc_cmos_write(day, RTC_DAY_OF_MONTH);
|
||||
vrtc_cmos_write(hrs, RTC_HOURS);
|
||||
vrtc_cmos_write(min, RTC_MINUTES);
|
||||
vrtc_cmos_write(sec, RTC_SECONDS);
|
||||
|
||||
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||
|
||||
ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETTIME);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mrst_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
{
|
||||
struct mrst_rtc *mrst = dev_get_drvdata(dev);
|
||||
unsigned char rtc_control;
|
||||
|
||||
if (mrst->irq <= 0)
|
||||
return -EIO;
|
||||
|
||||
/* vRTC only supports binary mode */
|
||||
spin_lock_irq(&rtc_lock);
|
||||
t->time.tm_sec = vrtc_cmos_read(RTC_SECONDS_ALARM);
|
||||
t->time.tm_min = vrtc_cmos_read(RTC_MINUTES_ALARM);
|
||||
t->time.tm_hour = vrtc_cmos_read(RTC_HOURS_ALARM);
|
||||
|
||||
rtc_control = vrtc_cmos_read(RTC_CONTROL);
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
t->enabled = !!(rtc_control & RTC_AIE);
|
||||
t->pending = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mrst_checkintr(struct mrst_rtc *mrst, unsigned char rtc_control)
|
||||
{
|
||||
unsigned char rtc_intr;
|
||||
|
||||
/*
|
||||
* NOTE after changing RTC_xIE bits we always read INTR_FLAGS;
|
||||
* allegedly some older rtcs need that to handle irqs properly
|
||||
*/
|
||||
rtc_intr = vrtc_cmos_read(RTC_INTR_FLAGS);
|
||||
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
|
||||
if (is_intr(rtc_intr))
|
||||
rtc_update_irq(mrst->rtc, 1, rtc_intr);
|
||||
}
|
||||
|
||||
static void mrst_irq_enable(struct mrst_rtc *mrst, unsigned char mask)
|
||||
{
|
||||
unsigned char rtc_control;
|
||||
|
||||
/*
|
||||
* Flush any pending IRQ status, notably for update irqs,
|
||||
* before we enable new IRQs
|
||||
*/
|
||||
rtc_control = vrtc_cmos_read(RTC_CONTROL);
|
||||
mrst_checkintr(mrst, rtc_control);
|
||||
|
||||
rtc_control |= mask;
|
||||
vrtc_cmos_write(rtc_control, RTC_CONTROL);
|
||||
|
||||
mrst_checkintr(mrst, rtc_control);
|
||||
}
|
||||
|
||||
static void mrst_irq_disable(struct mrst_rtc *mrst, unsigned char mask)
|
||||
{
|
||||
unsigned char rtc_control;
|
||||
|
||||
rtc_control = vrtc_cmos_read(RTC_CONTROL);
|
||||
rtc_control &= ~mask;
|
||||
vrtc_cmos_write(rtc_control, RTC_CONTROL);
|
||||
mrst_checkintr(mrst, rtc_control);
|
||||
}
|
||||
|
||||
static int mrst_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
{
|
||||
struct mrst_rtc *mrst = dev_get_drvdata(dev);
|
||||
unsigned char hrs, min, sec;
|
||||
int ret = 0;
|
||||
|
||||
if (!mrst->irq)
|
||||
return -EIO;
|
||||
|
||||
hrs = t->time.tm_hour;
|
||||
min = t->time.tm_min;
|
||||
sec = t->time.tm_sec;
|
||||
|
||||
spin_lock_irq(&rtc_lock);
|
||||
/* Next rtc irq must not be from previous alarm setting */
|
||||
mrst_irq_disable(mrst, RTC_AIE);
|
||||
|
||||
/* Update alarm */
|
||||
vrtc_cmos_write(hrs, RTC_HOURS_ALARM);
|
||||
vrtc_cmos_write(min, RTC_MINUTES_ALARM);
|
||||
vrtc_cmos_write(sec, RTC_SECONDS_ALARM);
|
||||
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
ret = intel_scu_ipc_simple_command(IPCMSG_VRTC, IPC_CMD_VRTC_SETALARM);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
spin_lock_irq(&rtc_lock);
|
||||
if (t->enabled)
|
||||
mrst_irq_enable(mrst, RTC_AIE);
|
||||
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Currently, the vRTC doesn't support UIE ON/OFF */
|
||||
static int mrst_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct mrst_rtc *mrst = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rtc_lock, flags);
|
||||
if (enabled)
|
||||
mrst_irq_enable(mrst, RTC_AIE);
|
||||
else
|
||||
mrst_irq_disable(mrst, RTC_AIE);
|
||||
spin_unlock_irqrestore(&rtc_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#if IS_ENABLED(CONFIG_RTC_INTF_PROC)
|
||||
|
||||
static int mrst_procfs(struct device *dev, struct seq_file *seq)
|
||||
{
|
||||
unsigned char rtc_control;
|
||||
|
||||
spin_lock_irq(&rtc_lock);
|
||||
rtc_control = vrtc_cmos_read(RTC_CONTROL);
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
seq_printf(seq,
|
||||
"periodic_IRQ\t: %s\n"
|
||||
"alarm\t\t: %s\n"
|
||||
"BCD\t\t: no\n"
|
||||
"periodic_freq\t: daily (not adjustable)\n",
|
||||
(rtc_control & RTC_PIE) ? "on" : "off",
|
||||
(rtc_control & RTC_AIE) ? "on" : "off");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define mrst_procfs NULL
|
||||
#endif
|
||||
|
||||
static const struct rtc_class_ops mrst_rtc_ops = {
|
||||
.read_time = mrst_read_time,
|
||||
.set_time = mrst_set_time,
|
||||
.read_alarm = mrst_read_alarm,
|
||||
.set_alarm = mrst_set_alarm,
|
||||
.proc = mrst_procfs,
|
||||
.alarm_irq_enable = mrst_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static struct mrst_rtc mrst_rtc;
|
||||
|
||||
/*
|
||||
* When vRTC IRQ is captured by SCU FW, FW will clear the AIE bit in
|
||||
* Reg B, so no need for this driver to clear it
|
||||
*/
|
||||
static irqreturn_t mrst_rtc_irq(int irq, void *p)
|
||||
{
|
||||
u8 irqstat;
|
||||
|
||||
spin_lock(&rtc_lock);
|
||||
/* This read will clear all IRQ flags inside Reg C */
|
||||
irqstat = vrtc_cmos_read(RTC_INTR_FLAGS);
|
||||
spin_unlock(&rtc_lock);
|
||||
|
||||
irqstat &= RTC_IRQMASK | RTC_IRQF;
|
||||
if (is_intr(irqstat)) {
|
||||
rtc_update_irq(p, 1, irqstat);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int vrtc_mrst_do_probe(struct device *dev, struct resource *iomem,
|
||||
int rtc_irq)
|
||||
{
|
||||
int retval = 0;
|
||||
unsigned char rtc_control;
|
||||
|
||||
/* There can be only one ... */
|
||||
if (mrst_rtc.dev)
|
||||
return -EBUSY;
|
||||
|
||||
if (!iomem)
|
||||
return -ENODEV;
|
||||
|
||||
iomem = devm_request_mem_region(dev, iomem->start, resource_size(iomem),
|
||||
driver_name);
|
||||
if (!iomem) {
|
||||
dev_dbg(dev, "i/o mem already in use.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
mrst_rtc.irq = rtc_irq;
|
||||
mrst_rtc.dev = dev;
|
||||
dev_set_drvdata(dev, &mrst_rtc);
|
||||
|
||||
mrst_rtc.rtc = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(mrst_rtc.rtc))
|
||||
return PTR_ERR(mrst_rtc.rtc);
|
||||
|
||||
mrst_rtc.rtc->ops = &mrst_rtc_ops;
|
||||
|
||||
rename_region(iomem, dev_name(&mrst_rtc.rtc->dev));
|
||||
|
||||
spin_lock_irq(&rtc_lock);
|
||||
mrst_irq_disable(&mrst_rtc, RTC_PIE | RTC_AIE);
|
||||
rtc_control = vrtc_cmos_read(RTC_CONTROL);
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY)))
|
||||
dev_dbg(dev, "TODO: support more than 24-hr BCD mode\n");
|
||||
|
||||
if (rtc_irq) {
|
||||
retval = devm_request_irq(dev, rtc_irq, mrst_rtc_irq,
|
||||
0, dev_name(&mrst_rtc.rtc->dev),
|
||||
mrst_rtc.rtc);
|
||||
if (retval < 0) {
|
||||
dev_dbg(dev, "IRQ %d is already in use, err %d\n",
|
||||
rtc_irq, retval);
|
||||
goto cleanup0;
|
||||
}
|
||||
}
|
||||
|
||||
retval = devm_rtc_register_device(mrst_rtc.rtc);
|
||||
if (retval)
|
||||
goto cleanup0;
|
||||
|
||||
dev_dbg(dev, "initialised\n");
|
||||
return 0;
|
||||
|
||||
cleanup0:
|
||||
mrst_rtc.dev = NULL;
|
||||
dev_err(dev, "rtc-mrst: unable to initialise\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void rtc_mrst_do_shutdown(void)
|
||||
{
|
||||
spin_lock_irq(&rtc_lock);
|
||||
mrst_irq_disable(&mrst_rtc, RTC_IRQMASK);
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
}
|
||||
|
||||
static void rtc_mrst_do_remove(struct device *dev)
|
||||
{
|
||||
struct mrst_rtc *mrst = dev_get_drvdata(dev);
|
||||
|
||||
rtc_mrst_do_shutdown();
|
||||
|
||||
mrst->rtc = NULL;
|
||||
mrst->dev = NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mrst_suspend(struct device *dev)
|
||||
{
|
||||
struct mrst_rtc *mrst = dev_get_drvdata(dev);
|
||||
unsigned char tmp;
|
||||
|
||||
/* Only the alarm might be a wakeup event source */
|
||||
spin_lock_irq(&rtc_lock);
|
||||
mrst->suspend_ctrl = tmp = vrtc_cmos_read(RTC_CONTROL);
|
||||
if (tmp & (RTC_PIE | RTC_AIE)) {
|
||||
unsigned char mask;
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
mask = RTC_IRQMASK & ~RTC_AIE;
|
||||
else
|
||||
mask = RTC_IRQMASK;
|
||||
tmp &= ~mask;
|
||||
vrtc_cmos_write(tmp, RTC_CONTROL);
|
||||
|
||||
mrst_checkintr(mrst, tmp);
|
||||
}
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
|
||||
if (tmp & RTC_AIE) {
|
||||
mrst->enabled_wake = 1;
|
||||
enable_irq_wake(mrst->irq);
|
||||
}
|
||||
|
||||
dev_dbg(&mrst_rtc.rtc->dev, "suspend%s, ctrl %02x\n",
|
||||
(tmp & RTC_AIE) ? ", alarm may wake" : "",
|
||||
tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We want RTC alarms to wake us from the deep power saving state
|
||||
*/
|
||||
static inline int mrst_poweroff(struct device *dev)
|
||||
{
|
||||
return mrst_suspend(dev);
|
||||
}
|
||||
|
||||
static int mrst_resume(struct device *dev)
|
||||
{
|
||||
struct mrst_rtc *mrst = dev_get_drvdata(dev);
|
||||
unsigned char tmp = mrst->suspend_ctrl;
|
||||
|
||||
/* Re-enable any irqs previously active */
|
||||
if (tmp & RTC_IRQMASK) {
|
||||
unsigned char mask;
|
||||
|
||||
if (mrst->enabled_wake) {
|
||||
disable_irq_wake(mrst->irq);
|
||||
mrst->enabled_wake = 0;
|
||||
}
|
||||
|
||||
spin_lock_irq(&rtc_lock);
|
||||
do {
|
||||
vrtc_cmos_write(tmp, RTC_CONTROL);
|
||||
|
||||
mask = vrtc_cmos_read(RTC_INTR_FLAGS);
|
||||
mask &= (tmp & RTC_IRQMASK) | RTC_IRQF;
|
||||
if (!is_intr(mask))
|
||||
break;
|
||||
|
||||
rtc_update_irq(mrst->rtc, 1, mask);
|
||||
tmp &= ~RTC_AIE;
|
||||
} while (mask & RTC_AIE);
|
||||
spin_unlock_irq(&rtc_lock);
|
||||
}
|
||||
|
||||
dev_dbg(&mrst_rtc.rtc->dev, "resume, ctrl %02x\n", tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mrst_pm_ops, mrst_suspend, mrst_resume);
|
||||
#define MRST_PM_OPS (&mrst_pm_ops)
|
||||
|
||||
#else
|
||||
#define MRST_PM_OPS NULL
|
||||
|
||||
static inline int mrst_poweroff(struct device *dev)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int vrtc_mrst_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
return vrtc_mrst_do_probe(&pdev->dev,
|
||||
platform_get_resource(pdev, IORESOURCE_MEM, 0),
|
||||
platform_get_irq(pdev, 0));
|
||||
}
|
||||
|
||||
static int vrtc_mrst_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
rtc_mrst_do_remove(&pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void vrtc_mrst_platform_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
if (system_state == SYSTEM_POWER_OFF && !mrst_poweroff(&pdev->dev))
|
||||
return;
|
||||
|
||||
rtc_mrst_do_shutdown();
|
||||
}
|
||||
|
||||
MODULE_ALIAS("platform:vrtc_mrst");
|
||||
|
||||
static struct platform_driver vrtc_mrst_platform_driver = {
|
||||
.probe = vrtc_mrst_platform_probe,
|
||||
.remove = vrtc_mrst_platform_remove,
|
||||
.shutdown = vrtc_mrst_platform_shutdown,
|
||||
.driver = {
|
||||
.name = driver_name,
|
||||
.pm = MRST_PM_OPS,
|
||||
}
|
||||
};
|
||||
|
||||
module_platform_driver(vrtc_mrst_platform_driver);
|
||||
|
||||
MODULE_AUTHOR("Jacob Pan; Feng Tang");
|
||||
MODULE_DESCRIPTION("Driver for Moorestown virtual RTC");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1219,15 +1219,6 @@ config IE6XX_WDT
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ie6xx_wdt.
|
||||
|
||||
config INTEL_SCU_WATCHDOG
|
||||
bool "Intel SCU Watchdog for Mobile Platforms"
|
||||
depends on X86_INTEL_MID
|
||||
help
|
||||
Hardware driver for the watchdog time built into the Intel SCU
|
||||
for Intel Mobile Platforms.
|
||||
|
||||
To compile this driver as a module, choose M here.
|
||||
|
||||
config INTEL_MID_WATCHDOG
|
||||
tristate "Intel MID Watchdog Timer"
|
||||
depends on X86_INTEL_MID
|
||||
|
|
|
@ -140,7 +140,6 @@ obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
|
|||
obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
|
||||
obj-$(CONFIG_MACHZ_WDT) += machzwd.o
|
||||
obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
|
||||
obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o
|
||||
obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o
|
||||
obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o
|
||||
obj-$(CONFIG_NI903X_WDT) += ni903x_wdt.o
|
||||
|
|
|
@ -154,6 +154,10 @@ static int mid_wdt_probe(struct platform_device *pdev)
|
|||
watchdog_set_nowayout(wdt_dev, WATCHDOG_NOWAYOUT);
|
||||
watchdog_set_drvdata(wdt_dev, mid);
|
||||
|
||||
mid->scu = devm_intel_scu_ipc_dev_get(dev);
|
||||
if (!mid->scu)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
ret = devm_request_irq(dev, pdata->irq, mid_wdt_irq,
|
||||
IRQF_SHARED | IRQF_NO_SUSPEND, "watchdog",
|
||||
wdt_dev);
|
||||
|
@ -162,10 +166,6 @@ static int mid_wdt_probe(struct platform_device *pdev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
mid->scu = devm_intel_scu_ipc_dev_get(dev);
|
||||
if (!mid->scu)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
/*
|
||||
* The firmware followed by U-Boot leaves the watchdog running
|
||||
* with the default threshold which may vary. When we get here
|
||||
|
|
|
@ -1,533 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Intel_SCU 0.2: An Intel SCU IOH Based Watchdog Device
|
||||
* for Intel part #(s):
|
||||
* - AF82MP20 PCH
|
||||
*
|
||||
* Copyright (C) 2009-2010 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/sfi.h>
|
||||
#include <asm/irq.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include <asm/apb_timer.h>
|
||||
#include <asm/intel-mid.h>
|
||||
|
||||
#include "intel_scu_watchdog.h"
|
||||
|
||||
/* Bounds number of times we will retry loading time count */
|
||||
/* This retry is a work around for a silicon bug. */
|
||||
#define MAX_RETRY 16
|
||||
|
||||
#define IPC_SET_WATCHDOG_TIMER 0xF8
|
||||
|
||||
static int timer_margin = DEFAULT_SOFT_TO_HARD_MARGIN;
|
||||
module_param(timer_margin, int, 0);
|
||||
MODULE_PARM_DESC(timer_margin,
|
||||
"Watchdog timer margin"
|
||||
"Time between interrupt and resetting the system"
|
||||
"The range is from 1 to 160"
|
||||
"This is the time for all keep alives to arrive");
|
||||
|
||||
static int timer_set = DEFAULT_TIME;
|
||||
module_param(timer_set, int, 0);
|
||||
MODULE_PARM_DESC(timer_set,
|
||||
"Default Watchdog timer setting"
|
||||
"Complete cycle time"
|
||||
"The range is from 1 to 170"
|
||||
"This is the time for all keep alives to arrive");
|
||||
|
||||
/* After watchdog device is closed, check force_boot. If:
|
||||
* force_boot == 0, then force boot on next watchdog interrupt after close,
|
||||
* force_boot == 1, then force boot immediately when device is closed.
|
||||
*/
|
||||
static int force_boot;
|
||||
module_param(force_boot, int, 0);
|
||||
MODULE_PARM_DESC(force_boot,
|
||||
"A value of 1 means that the driver will reboot"
|
||||
"the system immediately if the /dev/watchdog device is closed"
|
||||
"A value of 0 means that when /dev/watchdog device is closed"
|
||||
"the watchdog timer will be refreshed for one more interval"
|
||||
"of length: timer_set. At the end of this interval, the"
|
||||
"watchdog timer will reset the system."
|
||||
);
|
||||
|
||||
/* there is only one device in the system now; this can be made into
|
||||
* an array in the future if we have more than one device */
|
||||
|
||||
static struct intel_scu_watchdog_dev watchdog_device;
|
||||
|
||||
/* Forces restart, if force_reboot is set */
|
||||
static void watchdog_fire(void)
|
||||
{
|
||||
if (force_boot) {
|
||||
pr_crit("Initiating system reboot\n");
|
||||
emergency_restart();
|
||||
pr_crit("Reboot didn't ?????\n");
|
||||
}
|
||||
|
||||
else {
|
||||
pr_crit("Immediate Reboot Disabled\n");
|
||||
pr_crit("System will reset when watchdog timer times out!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int check_timer_margin(int new_margin)
|
||||
{
|
||||
if ((new_margin < MIN_TIME_CYCLE) ||
|
||||
(new_margin > MAX_TIME - timer_set)) {
|
||||
pr_debug("value of new_margin %d is out of the range %d to %d\n",
|
||||
new_margin, MIN_TIME_CYCLE, MAX_TIME - timer_set);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* IPC operations
|
||||
*/
|
||||
static int watchdog_set_ipc(int soft_threshold, int threshold)
|
||||
{
|
||||
u32 *ipc_wbuf;
|
||||
u8 cbuf[16] = { '\0' };
|
||||
int ipc_ret = 0;
|
||||
|
||||
ipc_wbuf = (u32 *)&cbuf;
|
||||
ipc_wbuf[0] = soft_threshold;
|
||||
ipc_wbuf[1] = threshold;
|
||||
|
||||
ipc_ret = intel_scu_ipc_command(
|
||||
IPC_SET_WATCHDOG_TIMER,
|
||||
0,
|
||||
ipc_wbuf,
|
||||
2,
|
||||
NULL,
|
||||
0);
|
||||
|
||||
if (ipc_ret != 0)
|
||||
pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret);
|
||||
|
||||
return ipc_ret;
|
||||
};
|
||||
|
||||
/*
|
||||
* Intel_SCU operations
|
||||
*/
|
||||
|
||||
/* timer interrupt handler */
|
||||
static irqreturn_t watchdog_timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
int int_status;
|
||||
int_status = ioread32(watchdog_device.timer_interrupt_status_addr);
|
||||
|
||||
pr_debug("irq, int_status: %x\n", int_status);
|
||||
|
||||
if (int_status != 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* has the timer been started? If not, then this is spurious */
|
||||
if (watchdog_device.timer_started == 0) {
|
||||
pr_debug("spurious interrupt received\n");
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* temporarily disable the timer */
|
||||
iowrite32(0x00000002, watchdog_device.timer_control_addr);
|
||||
|
||||
/* set the timer to the threshold */
|
||||
iowrite32(watchdog_device.threshold,
|
||||
watchdog_device.timer_load_count_addr);
|
||||
|
||||
/* allow the timer to run */
|
||||
iowrite32(0x00000003, watchdog_device.timer_control_addr);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int intel_scu_keepalive(void)
|
||||
{
|
||||
|
||||
/* read eoi register - clears interrupt */
|
||||
ioread32(watchdog_device.timer_clear_interrupt_addr);
|
||||
|
||||
/* temporarily disable the timer */
|
||||
iowrite32(0x00000002, watchdog_device.timer_control_addr);
|
||||
|
||||
/* set the timer to the soft_threshold */
|
||||
iowrite32(watchdog_device.soft_threshold,
|
||||
watchdog_device.timer_load_count_addr);
|
||||
|
||||
/* allow the timer to run */
|
||||
iowrite32(0x00000003, watchdog_device.timer_control_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_scu_stop(void)
|
||||
{
|
||||
iowrite32(0, watchdog_device.timer_control_addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_scu_set_heartbeat(u32 t)
|
||||
{
|
||||
int ipc_ret;
|
||||
int retry_count;
|
||||
u32 soft_value;
|
||||
u32 hw_value;
|
||||
|
||||
watchdog_device.timer_set = t;
|
||||
watchdog_device.threshold =
|
||||
timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
|
||||
watchdog_device.soft_threshold =
|
||||
(watchdog_device.timer_set - timer_margin)
|
||||
* watchdog_device.timer_tbl_ptr->freq_hz;
|
||||
|
||||
pr_debug("set_heartbeat: timer freq is %d\n",
|
||||
watchdog_device.timer_tbl_ptr->freq_hz);
|
||||
pr_debug("set_heartbeat: timer_set is %x (hex)\n",
|
||||
watchdog_device.timer_set);
|
||||
pr_debug("set_heartbeat: timer_margin is %x (hex)\n", timer_margin);
|
||||
pr_debug("set_heartbeat: threshold is %x (hex)\n",
|
||||
watchdog_device.threshold);
|
||||
pr_debug("set_heartbeat: soft_threshold is %x (hex)\n",
|
||||
watchdog_device.soft_threshold);
|
||||
|
||||
/* Adjust thresholds by FREQ_ADJUSTMENT factor, to make the */
|
||||
/* watchdog timing come out right. */
|
||||
watchdog_device.threshold =
|
||||
watchdog_device.threshold / FREQ_ADJUSTMENT;
|
||||
watchdog_device.soft_threshold =
|
||||
watchdog_device.soft_threshold / FREQ_ADJUSTMENT;
|
||||
|
||||
/* temporarily disable the timer */
|
||||
iowrite32(0x00000002, watchdog_device.timer_control_addr);
|
||||
|
||||
/* send the threshold and soft_threshold via IPC to the processor */
|
||||
ipc_ret = watchdog_set_ipc(watchdog_device.soft_threshold,
|
||||
watchdog_device.threshold);
|
||||
|
||||
if (ipc_ret != 0) {
|
||||
/* Make sure the watchdog timer is stopped */
|
||||
intel_scu_stop();
|
||||
return ipc_ret;
|
||||
}
|
||||
|
||||
/* Soft Threshold set loop. Early versions of silicon did */
|
||||
/* not always set this count correctly. This loop checks */
|
||||
/* the value and retries if it was not set correctly. */
|
||||
|
||||
retry_count = 0;
|
||||
soft_value = watchdog_device.soft_threshold & 0xFFFF0000;
|
||||
do {
|
||||
|
||||
/* Make sure timer is stopped */
|
||||
intel_scu_stop();
|
||||
|
||||
if (MAX_RETRY < retry_count++) {
|
||||
/* Unable to set timer value */
|
||||
pr_err("Unable to set timer\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* set the timer to the soft threshold */
|
||||
iowrite32(watchdog_device.soft_threshold,
|
||||
watchdog_device.timer_load_count_addr);
|
||||
|
||||
/* read count value before starting timer */
|
||||
ioread32(watchdog_device.timer_load_count_addr);
|
||||
|
||||
/* Start the timer */
|
||||
iowrite32(0x00000003, watchdog_device.timer_control_addr);
|
||||
|
||||
/* read the value the time loaded into its count reg */
|
||||
hw_value = ioread32(watchdog_device.timer_load_count_addr);
|
||||
hw_value = hw_value & 0xFFFF0000;
|
||||
|
||||
|
||||
} while (soft_value != hw_value);
|
||||
|
||||
watchdog_device.timer_started = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* /dev/watchdog handling
|
||||
*/
|
||||
|
||||
static int intel_scu_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
|
||||
/* Set flag to indicate that watchdog device is open */
|
||||
if (test_and_set_bit(0, &watchdog_device.driver_open))
|
||||
return -EBUSY;
|
||||
|
||||
/* Check for reopen of driver. Reopens are not allowed */
|
||||
if (watchdog_device.driver_closed)
|
||||
return -EPERM;
|
||||
|
||||
return stream_open(inode, file);
|
||||
}
|
||||
|
||||
static int intel_scu_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
/*
|
||||
* This watchdog should not be closed, after the timer
|
||||
* is started with the WDIPC_SETTIMEOUT ioctl
|
||||
* If force_boot is set watchdog_fire() will cause an
|
||||
* immediate reset. If force_boot is not set, the watchdog
|
||||
* timer is refreshed for one more interval. At the end
|
||||
* of that interval, the watchdog timer will reset the system.
|
||||
*/
|
||||
|
||||
if (!test_and_clear_bit(0, &watchdog_device.driver_open)) {
|
||||
pr_debug("intel_scu_release, without open\n");
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
if (!watchdog_device.timer_started) {
|
||||
/* Just close, since timer has not been started */
|
||||
pr_debug("closed, without starting timer\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_crit("Unexpected close of /dev/watchdog!\n");
|
||||
|
||||
/* Since the timer was started, prevent future reopens */
|
||||
watchdog_device.driver_closed = 1;
|
||||
|
||||
/* Refresh the timer for one more interval */
|
||||
intel_scu_keepalive();
|
||||
|
||||
/* Reboot system (if force_boot is set) */
|
||||
watchdog_fire();
|
||||
|
||||
/* We should only reach this point if force_boot is not set */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t intel_scu_write(struct file *file,
|
||||
char const *data,
|
||||
size_t len,
|
||||
loff_t *ppos)
|
||||
{
|
||||
|
||||
if (watchdog_device.timer_started)
|
||||
/* Watchdog already started, keep it alive */
|
||||
intel_scu_keepalive();
|
||||
else
|
||||
/* Start watchdog with timer value set by init */
|
||||
intel_scu_set_heartbeat(watchdog_device.timer_set);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static long intel_scu_ioctl(struct file *file,
|
||||
unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
u32 __user *p = argp;
|
||||
u32 new_margin;
|
||||
|
||||
|
||||
static const struct watchdog_info ident = {
|
||||
.options = WDIOF_SETTIMEOUT
|
||||
| WDIOF_KEEPALIVEPING,
|
||||
.firmware_version = 0, /* @todo Get from SCU via
|
||||
ipc_get_scu_fw_version()? */
|
||||
.identity = "Intel_SCU IOH Watchdog" /* len < 32 */
|
||||
};
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
return copy_to_user(argp,
|
||||
&ident,
|
||||
sizeof(ident)) ? -EFAULT : 0;
|
||||
case WDIOC_GETSTATUS:
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
return put_user(0, p);
|
||||
case WDIOC_KEEPALIVE:
|
||||
intel_scu_keepalive();
|
||||
|
||||
return 0;
|
||||
case WDIOC_SETTIMEOUT:
|
||||
if (get_user(new_margin, p))
|
||||
return -EFAULT;
|
||||
|
||||
if (check_timer_margin(new_margin))
|
||||
return -EINVAL;
|
||||
|
||||
if (intel_scu_set_heartbeat(new_margin))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
case WDIOC_GETTIMEOUT:
|
||||
return put_user(watchdog_device.soft_threshold, p);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Notifier for system down
|
||||
*/
|
||||
static int intel_scu_notify_sys(struct notifier_block *this,
|
||||
unsigned long code,
|
||||
void *another_unused)
|
||||
{
|
||||
if (code == SYS_DOWN || code == SYS_HALT)
|
||||
/* Turn off the watchdog timer. */
|
||||
intel_scu_stop();
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Kernel Interfaces
|
||||
*/
|
||||
static const struct file_operations intel_scu_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.write = intel_scu_write,
|
||||
.unlocked_ioctl = intel_scu_ioctl,
|
||||
.compat_ioctl = compat_ptr_ioctl,
|
||||
.open = intel_scu_open,
|
||||
.release = intel_scu_release,
|
||||
};
|
||||
|
||||
static int __init intel_scu_watchdog_init(void)
|
||||
{
|
||||
int ret;
|
||||
u32 __iomem *tmp_addr;
|
||||
|
||||
/*
|
||||
* We don't really need to check this as the SFI timer get will fail
|
||||
* but if we do so we can exit with a clearer reason and no noise.
|
||||
*
|
||||
* If it isn't an intel MID device then it doesn't have this watchdog
|
||||
*/
|
||||
if (!intel_mid_identify_cpu())
|
||||
return -ENODEV;
|
||||
|
||||
/* Check boot parameters to verify that their initial values */
|
||||
/* are in range. */
|
||||
/* Check value of timer_set boot parameter */
|
||||
if ((timer_set < MIN_TIME_CYCLE) ||
|
||||
(timer_set > MAX_TIME - MIN_TIME_CYCLE)) {
|
||||
pr_err("value of timer_set %x (hex) is out of range from %x to %x (hex)\n",
|
||||
timer_set, MIN_TIME_CYCLE, MAX_TIME - MIN_TIME_CYCLE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check value of timer_margin boot parameter */
|
||||
if (check_timer_margin(timer_margin))
|
||||
return -EINVAL;
|
||||
|
||||
watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1);
|
||||
|
||||
if (watchdog_device.timer_tbl_ptr == NULL) {
|
||||
pr_debug("timer is not available\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
/* make sure the timer exists */
|
||||
if (watchdog_device.timer_tbl_ptr->phys_addr == 0) {
|
||||
pr_debug("timer %d does not have valid physical memory\n",
|
||||
sfi_mtimer_num);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (watchdog_device.timer_tbl_ptr->irq == 0) {
|
||||
pr_debug("timer %d invalid irq\n", sfi_mtimer_num);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
tmp_addr = ioremap(watchdog_device.timer_tbl_ptr->phys_addr,
|
||||
20);
|
||||
|
||||
if (tmp_addr == NULL) {
|
||||
pr_debug("timer unable to ioremap\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
watchdog_device.timer_load_count_addr = tmp_addr++;
|
||||
watchdog_device.timer_current_value_addr = tmp_addr++;
|
||||
watchdog_device.timer_control_addr = tmp_addr++;
|
||||
watchdog_device.timer_clear_interrupt_addr = tmp_addr++;
|
||||
watchdog_device.timer_interrupt_status_addr = tmp_addr++;
|
||||
|
||||
/* Set the default time values in device structure */
|
||||
|
||||
watchdog_device.timer_set = timer_set;
|
||||
watchdog_device.threshold =
|
||||
timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
|
||||
watchdog_device.soft_threshold =
|
||||
(watchdog_device.timer_set - timer_margin)
|
||||
* watchdog_device.timer_tbl_ptr->freq_hz;
|
||||
|
||||
|
||||
watchdog_device.intel_scu_notifier.notifier_call =
|
||||
intel_scu_notify_sys;
|
||||
|
||||
ret = register_reboot_notifier(&watchdog_device.intel_scu_notifier);
|
||||
if (ret) {
|
||||
pr_err("cannot register notifier %d)\n", ret);
|
||||
goto register_reboot_error;
|
||||
}
|
||||
|
||||
watchdog_device.miscdev.minor = WATCHDOG_MINOR;
|
||||
watchdog_device.miscdev.name = "watchdog";
|
||||
watchdog_device.miscdev.fops = &intel_scu_fops;
|
||||
|
||||
ret = misc_register(&watchdog_device.miscdev);
|
||||
if (ret) {
|
||||
pr_err("cannot register miscdev %d err =%d\n",
|
||||
WATCHDOG_MINOR, ret);
|
||||
goto misc_register_error;
|
||||
}
|
||||
|
||||
ret = request_irq((unsigned int)watchdog_device.timer_tbl_ptr->irq,
|
||||
watchdog_timer_interrupt,
|
||||
IRQF_SHARED, "watchdog",
|
||||
&watchdog_device.timer_load_count_addr);
|
||||
if (ret) {
|
||||
pr_err("error requesting irq %d\n", ret);
|
||||
goto request_irq_error;
|
||||
}
|
||||
/* Make sure timer is disabled before returning */
|
||||
intel_scu_stop();
|
||||
return 0;
|
||||
|
||||
/* error cleanup */
|
||||
|
||||
request_irq_error:
|
||||
misc_deregister(&watchdog_device.miscdev);
|
||||
misc_register_error:
|
||||
unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
|
||||
register_reboot_error:
|
||||
intel_scu_stop();
|
||||
iounmap(watchdog_device.timer_load_count_addr);
|
||||
return ret;
|
||||
}
|
||||
late_initcall(intel_scu_watchdog_init);
|
|
@ -1,50 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Intel_SCU 0.2: An Intel SCU IOH Based Watchdog Device
|
||||
* for Intel part #(s):
|
||||
* - AF82MP20 PCH
|
||||
*
|
||||
* Copyright (C) 2009-2010 Intel Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __INTEL_SCU_WATCHDOG_H
|
||||
#define __INTEL_SCU_WATCHDOG_H
|
||||
|
||||
#define WDT_VER "0.3"
|
||||
|
||||
/* minimum time between interrupts */
|
||||
#define MIN_TIME_CYCLE 1
|
||||
|
||||
/* Time from warning to reboot is 2 seconds */
|
||||
#define DEFAULT_SOFT_TO_HARD_MARGIN 2
|
||||
|
||||
#define MAX_TIME 170
|
||||
|
||||
#define DEFAULT_TIME 5
|
||||
|
||||
#define MAX_SOFT_TO_HARD_MARGIN (MAX_TIME-MIN_TIME_CYCLE)
|
||||
|
||||
/* Ajustment to clock tick frequency to make timing come out right */
|
||||
#define FREQ_ADJUSTMENT 8
|
||||
|
||||
struct intel_scu_watchdog_dev {
|
||||
ulong driver_open;
|
||||
ulong driver_closed;
|
||||
u32 timer_started;
|
||||
u32 timer_set;
|
||||
u32 threshold;
|
||||
u32 soft_threshold;
|
||||
u32 __iomem *timer_load_count_addr;
|
||||
u32 __iomem *timer_current_value_addr;
|
||||
u32 __iomem *timer_control_addr;
|
||||
u32 __iomem *timer_clear_interrupt_addr;
|
||||
u32 __iomem *timer_interrupt_status_addr;
|
||||
struct sfi_timer_table_entry *timer_tbl_ptr;
|
||||
struct notifier_block intel_scu_notifier;
|
||||
struct miscdevice miscdev;
|
||||
};
|
||||
|
||||
extern int sfi_mtimer_num;
|
||||
|
||||
/* extern struct sfi_timer_table_entry *sfi_get_mtmr(int hint); */
|
||||
#endif /* __INTEL_SCU_WATCHDOG_H */
|
Загрузка…
Ссылка в новой задаче