Commit a40f530e authored by Hans de Goede's avatar Hans de Goede

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
parents fa707a58 a507e5d9
......@@ -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
......
// 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);
// 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)
return -ENODEV;
const struct x86_cpu_id *id;
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);
id = x86_match_cpu(intel_mid_cpu_ids);
if (!id)
return -ENODEV;
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
......
// 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
......
// 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);
/* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment