ib-drm-gpio-pdx86-rtc-wdt for v5.12-2
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: - Drop mistakenly added const - 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----- iQIzBAABCgAdFiEEqaflIX74DDDzMJJtb7wzTHR8rCgFAmAdJL8ACgkQb7wzTHR8 rCg5gxAAqJF+ThTCYrUpxVE9MSwVtpD4I7sfuh/LoI2fpiHrFweoK8gdbvzzeZLy bZ6Q2RLxrj8/dBcjKpZxWMhlTu5I+lLYm8Q+rJuJGSb3gwoDhtwy832zgWidkRIQ gePKvqw/o+bzECD03Ec6TcQqC/ldcMkSZ5gU+3T/zcU3xdpJZovTewzeRgINXpp+ Ivmm1jJGVS1IiKedSweRUO2M+rL+7EEyuK97c7itfjbfIFPfY8xpQS9H/JUjfI+g mnfHGnZ0FLE8bylJCoipW/QsghYqnINSPV+9dAsvBMrG2Bsu0cNsmI4DEGWM42F6 bi3EUPLUY9acMLUrhQDeanpG8z0nZ7gcUWeUWNAGAl8UDIAnhz2/S6zRQ5DShdqq jdnN6W8S0O/UToy1O67zjbJew898PesZ904R+Zly0828Swnd/dP82OJ5XUKJO2n6 QpltNc4YSUlFWUUwXRCDUOdw4CsSKExib0DW7iZCPP+eFYY/QQBYnUjXBj3Gq3d3 HFQjhtBkGWTl+WEfBuS2LNvn9e7X2gPgkoVrEnj01DqgC7lO7K99W7BrCJj0kOuI WcKPcpja8jB8IwCsyguryRqsAc4NxHMiaOjn0OGwkxOK3TPkGakZRbfbywoSfCVd Ve94hpzoh43FShgTW3Jis5ITiwT6ppo3nyjSmtptUlO4VYyjil0= =i3UT -----END PGP SIGNATURE----- Merge tag 'ib-drm-gpio-pdx86-rtc-wdt-v5.12-2' of git://git.infradead.org/linux-platform-drivers-x86 Pull the first part of Intel MID outdated platforms removal from Andy Shevchenko: "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: - Drop mistakenly added const - 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" * tag 'ib-drm-gpio-pdx86-rtc-wdt-v5.12-2' of git://git.infradead.org/linux-platform-drivers-x86: platform/x86: intel_scu_wdt: Drop mistakenly added const platform/x86: intel_scu_wdt: Get rid of custom x86 model comparison platform/x86: intel_scu_wdt: Drop SCU notification platform/x86: intel_scu_wdt: Move driver from arch/x86 watchdog: intel-mid_wdt: Postpone IRQ handler registration till SCU is ready watchdog: intel_scu_watchdog: Remove driver for deprecated platform rtc: mrst: Remove driver for deprecated platform platform/x86: intel_mid_powerbtn: Remove driver for deprecated platform platform/x86: intel_mid_thermal: Remove driver for deprecated platform gpio: msic: Remove driver for deprecated platform gpio: intel-mid: Remove driver for deprecated platform drm/gma500: Get rid of duplicate NULL checks drm/gma500: Convert to use new SCU IPC API
This commit is contained in:
commit
86b6d4f8cf
@ -8936,7 +8936,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
|
||||
@ -9095,7 +9094,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
|
||||
|
@ -1252,13 +1252,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
|
||||
@ -1454,13 +1447,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;
|
||||
|
||||
|
@ -1327,21 +1327,6 @@ config INTEL_CHTDC_TI_PWRBTN
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called intel_chtdc_ti_pwrbtn.
|
||||
|
||||
config INTEL_MFLD_THERMAL
|
||||
tristate "Thermal driver for Intel Medfield platform"
|
||||
depends on MFD_INTEL_MSIC && THERMAL
|
||||
help
|
||||
Say Y here to enable thermal driver support for the Intel Medfield
|
||||
platform.
|
||||
|
||||
config INTEL_MID_POWER_BUTTON
|
||||
tristate "power button driver for Intel MID platforms"
|
||||
depends on INTEL_SCU && INPUT
|
||||
help
|
||||
This driver handles the power button on the Intel MID platforms.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config INTEL_MRFLD_PWRBTN
|
||||
tristate "Intel Merrifield Basin Cove power button driver"
|
||||
depends on INTEL_SOC_PMIC_MRFLD
|
||||
@ -1439,6 +1424,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
|
||||
|
@ -137,8 +137,6 @@ obj-$(CONFIG_INTEL_UNCORE_FREQ_CONTROL) += intel-uncore-frequency.o
|
||||
# Intel PMIC / PMC / P-Unit devices
|
||||
obj-$(CONFIG_INTEL_BXTWC_PMIC_TMU) += intel_bxtwc_tmu.o
|
||||
obj-$(CONFIG_INTEL_CHTDC_TI_PWRBTN) += intel_chtdc_ti_pwrbtn.o
|
||||
obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o
|
||||
obj-$(CONFIG_INTEL_MID_POWER_BUTTON) += intel_mid_powerbtn.o
|
||||
obj-$(CONFIG_INTEL_MRFLD_PWRBTN) += intel_mrfld_pwrbtn.o
|
||||
obj-$(CONFIG_INTEL_PMC_CORE) += intel_pmc_core.o intel_pmc_core_pltdrv.o
|
||||
obj-$(CONFIG_INTEL_PMT_CLASS) += intel_pmt_class.o
|
||||
@ -148,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 \
|
||||
|
@ -1,233 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Power button driver for Intel MID platforms.
|
||||
*
|
||||
* Copyright (C) 2010,2017 Intel Corp
|
||||
*
|
||||
* Author: Hong Liu <hong.liu@intel.com>
|
||||
* Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/intel_msic.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
|
||||
#define DRIVER_NAME "msic_power_btn"
|
||||
|
||||
#define MSIC_PB_LEVEL (1 << 3) /* 1 - release, 0 - press */
|
||||
|
||||
/*
|
||||
* MSIC document ti_datasheet defines the 1st bit reg 0x21 is used to mask
|
||||
* power button interrupt
|
||||
*/
|
||||
#define MSIC_PWRBTNM (1 << 0)
|
||||
|
||||
/* Intel Tangier */
|
||||
#define BCOVE_PB_LEVEL (1 << 4) /* 1 - release, 0 - press */
|
||||
|
||||
/* Basin Cove PMIC */
|
||||
#define BCOVE_PBIRQ 0x02
|
||||
#define BCOVE_IRQLVL1MSK 0x0c
|
||||
#define BCOVE_PBIRQMASK 0x0d
|
||||
#define BCOVE_PBSTATUS 0x27
|
||||
|
||||
struct mid_pb_ddata {
|
||||
struct device *dev;
|
||||
int irq;
|
||||
struct input_dev *input;
|
||||
unsigned short mirqlvl1_addr;
|
||||
unsigned short pbstat_addr;
|
||||
u8 pbstat_mask;
|
||||
struct intel_scu_ipc_dev *scu;
|
||||
int (*setup)(struct mid_pb_ddata *ddata);
|
||||
};
|
||||
|
||||
static int mid_pbstat(struct mid_pb_ddata *ddata, int *value)
|
||||
{
|
||||
struct input_dev *input = ddata->input;
|
||||
int ret;
|
||||
u8 pbstat;
|
||||
|
||||
ret = intel_scu_ipc_dev_ioread8(ddata->scu, ddata->pbstat_addr,
|
||||
&pbstat);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(input->dev.parent, "PB_INT status= %d\n", pbstat);
|
||||
|
||||
*value = !(pbstat & ddata->pbstat_mask);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mid_irq_ack(struct mid_pb_ddata *ddata)
|
||||
{
|
||||
return intel_scu_ipc_dev_update(ddata->scu, ddata->mirqlvl1_addr, 0,
|
||||
MSIC_PWRBTNM);
|
||||
}
|
||||
|
||||
static int mrfld_setup(struct mid_pb_ddata *ddata)
|
||||
{
|
||||
/* Unmask the PBIRQ and MPBIRQ on Tangier */
|
||||
intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQ, 0, MSIC_PWRBTNM);
|
||||
intel_scu_ipc_dev_update(ddata->scu, BCOVE_PBIRQMASK, 0, MSIC_PWRBTNM);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t mid_pb_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct mid_pb_ddata *ddata = dev_id;
|
||||
struct input_dev *input = ddata->input;
|
||||
int value = 0;
|
||||
int ret;
|
||||
|
||||
ret = mid_pbstat(ddata, &value);
|
||||
if (ret < 0) {
|
||||
dev_err(input->dev.parent,
|
||||
"Read error %d while reading MSIC_PB_STATUS\n", ret);
|
||||
} else {
|
||||
input_event(input, EV_KEY, KEY_POWER, value);
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
mid_irq_ack(ddata);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct mid_pb_ddata mfld_ddata = {
|
||||
.mirqlvl1_addr = INTEL_MSIC_IRQLVL1MSK,
|
||||
.pbstat_addr = INTEL_MSIC_PBSTATUS,
|
||||
.pbstat_mask = MSIC_PB_LEVEL,
|
||||
};
|
||||
|
||||
static const struct mid_pb_ddata mrfld_ddata = {
|
||||
.mirqlvl1_addr = BCOVE_IRQLVL1MSK,
|
||||
.pbstat_addr = BCOVE_PBSTATUS,
|
||||
.pbstat_mask = BCOVE_PB_LEVEL,
|
||||
.setup = mrfld_setup,
|
||||
};
|
||||
|
||||
static const struct x86_cpu_id mid_pb_cpu_ids[] = {
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_SALTWELL_MID, &mfld_ddata),
|
||||
X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT_MID, &mrfld_ddata),
|
||||
{}
|
||||
};
|
||||
|
||||
static int mid_pb_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct x86_cpu_id *id;
|
||||
struct mid_pb_ddata *ddata;
|
||||
struct input_dev *input;
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
int error;
|
||||
|
||||
id = x86_match_cpu(mid_pb_cpu_ids);
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
|
||||
if (irq < 0) {
|
||||
dev_err(&pdev->dev, "Failed to get IRQ: %d\n", irq);
|
||||
return irq;
|
||||
}
|
||||
|
||||
input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
input->name = pdev->name;
|
||||
input->phys = "power-button/input0";
|
||||
input->id.bustype = BUS_HOST;
|
||||
input->dev.parent = &pdev->dev;
|
||||
|
||||
input_set_capability(input, EV_KEY, KEY_POWER);
|
||||
|
||||
ddata = devm_kmemdup(&pdev->dev, (void *)id->driver_data,
|
||||
sizeof(*ddata), GFP_KERNEL);
|
||||
if (!ddata)
|
||||
return -ENOMEM;
|
||||
|
||||
ddata->dev = &pdev->dev;
|
||||
ddata->irq = irq;
|
||||
ddata->input = input;
|
||||
|
||||
if (ddata->setup) {
|
||||
error = ddata->setup(ddata);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
ddata->scu = devm_intel_scu_ipc_dev_get(&pdev->dev);
|
||||
if (!ddata->scu)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
error = devm_request_threaded_irq(&pdev->dev, irq, NULL, mid_pb_isr,
|
||||
IRQF_ONESHOT, DRIVER_NAME, ddata);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to request irq %d for MID power button\n", irq);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to register input dev, error %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ddata);
|
||||
|
||||
/*
|
||||
* SCU firmware might send power button interrupts to IA core before
|
||||
* kernel boots and doesn't get EOI from IA core. The first bit of
|
||||
* MSIC reg 0x21 is kept masked, and SCU firmware doesn't send new
|
||||
* power interrupt to Android kernel. Unmask the bit when probing
|
||||
* power button in kernel.
|
||||
* There is a very narrow race between irq handler and power button
|
||||
* initialization. The race happens rarely. So we needn't worry
|
||||
* about it.
|
||||
*/
|
||||
error = mid_irq_ack(ddata);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"Unable to clear power button interrupt, error: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
dev_pm_set_wake_irq(&pdev->dev, irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mid_pb_remove(struct platform_device *pdev)
|
||||
{
|
||||
dev_pm_clear_wake_irq(&pdev->dev);
|
||||
device_init_wakeup(&pdev->dev, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver mid_pb_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
},
|
||||
.probe = mid_pb_probe,
|
||||
.remove = mid_pb_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(mid_pb_driver);
|
||||
|
||||
MODULE_AUTHOR("Hong Liu <hong.liu@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel MID Power Button Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRIVER_NAME);
|
@ -1,560 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Intel MID platform thermal driver
|
||||
*
|
||||
* Copyright (C) 2011 Intel Corporation
|
||||
*
|
||||
* Author: Durgadoss R <durgadoss.r@intel.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "intel_mid_thermal: " fmt
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mfd/intel_msic.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/param.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/thermal.h>
|
||||
|
||||
/* Number of thermal sensors */
|
||||
#define MSIC_THERMAL_SENSORS 4
|
||||
|
||||
/* ADC1 - thermal registers */
|
||||
#define MSIC_ADC_ENBL 0x10
|
||||
#define MSIC_ADC_START 0x08
|
||||
|
||||
#define MSIC_ADCTHERM_ENBL 0x04
|
||||
#define MSIC_ADCRRDATA_ENBL 0x05
|
||||
#define MSIC_CHANL_MASK_VAL 0x0F
|
||||
|
||||
#define MSIC_STOPBIT_MASK 16
|
||||
#define MSIC_ADCTHERM_MASK 4
|
||||
/* Number of ADC channels */
|
||||
#define ADC_CHANLS_MAX 15
|
||||
#define ADC_LOOP_MAX (ADC_CHANLS_MAX - MSIC_THERMAL_SENSORS)
|
||||
|
||||
/* ADC channel code values */
|
||||
#define SKIN_SENSOR0_CODE 0x08
|
||||
#define SKIN_SENSOR1_CODE 0x09
|
||||
#define SYS_SENSOR_CODE 0x0A
|
||||
#define MSIC_DIE_SENSOR_CODE 0x03
|
||||
|
||||
#define SKIN_THERM_SENSOR0 0
|
||||
#define SKIN_THERM_SENSOR1 1
|
||||
#define SYS_THERM_SENSOR2 2
|
||||
#define MSIC_DIE_THERM_SENSOR3 3
|
||||
|
||||
/* ADC code range */
|
||||
#define ADC_MAX 977
|
||||
#define ADC_MIN 162
|
||||
#define ADC_VAL0C 887
|
||||
#define ADC_VAL20C 720
|
||||
#define ADC_VAL40C 508
|
||||
#define ADC_VAL60C 315
|
||||
|
||||
/* ADC base addresses */
|
||||
#define ADC_CHNL_START_ADDR INTEL_MSIC_ADC1ADDR0 /* increments by 1 */
|
||||
#define ADC_DATA_START_ADDR INTEL_MSIC_ADC1SNS0H /* increments by 2 */
|
||||
|
||||
/* MSIC die attributes */
|
||||
#define MSIC_DIE_ADC_MIN 488
|
||||
#define MSIC_DIE_ADC_MAX 1004
|
||||
|
||||
/* This holds the address of the first free ADC channel,
|
||||
* among the 15 channels
|
||||
*/
|
||||
static int channel_index;
|
||||
|
||||
struct platform_info {
|
||||
struct platform_device *pdev;
|
||||
struct thermal_zone_device *tzd[MSIC_THERMAL_SENSORS];
|
||||
};
|
||||
|
||||
struct thermal_device_info {
|
||||
unsigned int chnl_addr;
|
||||
int direct;
|
||||
/* This holds the current temperature in millidegree celsius */
|
||||
long curr_temp;
|
||||
};
|
||||
|
||||
/**
|
||||
* to_msic_die_temp - converts adc_val to msic_die temperature
|
||||
* @adc_val: ADC value to be converted
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int to_msic_die_temp(uint16_t adc_val)
|
||||
{
|
||||
return (368 * (adc_val) / 1000) - 220;
|
||||
}
|
||||
|
||||
/**
|
||||
* is_valid_adc - checks whether the adc code is within the defined range
|
||||
* @min: minimum value for the sensor
|
||||
* @max: maximum value for the sensor
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int is_valid_adc(uint16_t adc_val, uint16_t min, uint16_t max)
|
||||
{
|
||||
return (adc_val >= min) && (adc_val <= max);
|
||||
}
|
||||
|
||||
/**
|
||||
* adc_to_temp - converts the ADC code to temperature in C
|
||||
* @direct: true if ths channel is direct index
|
||||
* @adc_val: the adc_val that needs to be converted
|
||||
* @tp: temperature return value
|
||||
*
|
||||
* Linear approximation is used to covert the skin adc value into temperature.
|
||||
* This technique is used to avoid very long look-up table to get
|
||||
* the appropriate temp value from ADC value.
|
||||
* The adc code vs sensor temp curve is split into five parts
|
||||
* to achieve very close approximate temp value with less than
|
||||
* 0.5C error
|
||||
*/
|
||||
static int adc_to_temp(int direct, uint16_t adc_val, int *tp)
|
||||
{
|
||||
int temp;
|
||||
|
||||
/* Direct conversion for die temperature */
|
||||
if (direct) {
|
||||
if (is_valid_adc(adc_val, MSIC_DIE_ADC_MIN, MSIC_DIE_ADC_MAX)) {
|
||||
*tp = to_msic_die_temp(adc_val) * 1000;
|
||||
return 0;
|
||||
}
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
if (!is_valid_adc(adc_val, ADC_MIN, ADC_MAX))
|
||||
return -ERANGE;
|
||||
|
||||
/* Linear approximation for skin temperature */
|
||||
if (adc_val > ADC_VAL0C)
|
||||
temp = 177 - (adc_val/5);
|
||||
else if ((adc_val <= ADC_VAL0C) && (adc_val > ADC_VAL20C))
|
||||
temp = 111 - (adc_val/8);
|
||||
else if ((adc_val <= ADC_VAL20C) && (adc_val > ADC_VAL40C))
|
||||
temp = 92 - (adc_val/10);
|
||||
else if ((adc_val <= ADC_VAL40C) && (adc_val > ADC_VAL60C))
|
||||
temp = 91 - (adc_val/10);
|
||||
else
|
||||
temp = 112 - (adc_val/6);
|
||||
|
||||
/* Convert temperature in celsius to milli degree celsius */
|
||||
*tp = temp * 1000;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* mid_read_temp - read sensors for temperature
|
||||
* @temp: holds the current temperature for the sensor after reading
|
||||
*
|
||||
* reads the adc_code from the channel and converts it to real
|
||||
* temperature. The converted value is stored in temp.
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int mid_read_temp(struct thermal_zone_device *tzd, int *temp)
|
||||
{
|
||||
struct thermal_device_info *td_info = tzd->devdata;
|
||||
uint16_t adc_val, addr;
|
||||
uint8_t data = 0;
|
||||
int ret;
|
||||
int curr_temp;
|
||||
|
||||
addr = td_info->chnl_addr;
|
||||
|
||||
/* Enable the msic for conversion before reading */
|
||||
ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, MSIC_ADCRRDATA_ENBL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Re-toggle the RRDATARD bit (temporary workaround) */
|
||||
ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, MSIC_ADCTHERM_ENBL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Read the higher bits of data */
|
||||
ret = intel_msic_reg_read(addr, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Shift bits to accommodate the lower two data bits */
|
||||
adc_val = (data << 2);
|
||||
addr++;
|
||||
|
||||
ret = intel_msic_reg_read(addr, &data);/* Read lower bits */
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Adding lower two bits to the higher bits */
|
||||
data &= 03;
|
||||
adc_val += data;
|
||||
|
||||
/* Convert ADC value to temperature */
|
||||
ret = adc_to_temp(td_info->direct, adc_val, &curr_temp);
|
||||
if (ret == 0)
|
||||
*temp = td_info->curr_temp = curr_temp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* configure_adc - enables/disables the ADC for conversion
|
||||
* @val: zero: disables the ADC non-zero:enables the ADC
|
||||
*
|
||||
* Enable/Disable the ADC depending on the argument
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int configure_adc(int val)
|
||||
{
|
||||
int ret;
|
||||
uint8_t data;
|
||||
|
||||
ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL1, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (val) {
|
||||
/* Enable and start the ADC */
|
||||
data |= (MSIC_ADC_ENBL | MSIC_ADC_START);
|
||||
} else {
|
||||
/* Just stop the ADC */
|
||||
data &= (~MSIC_ADC_START);
|
||||
}
|
||||
return intel_msic_reg_write(INTEL_MSIC_ADC1CNTL1, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* set_up_therm_channel - enable thermal channel for conversion
|
||||
* @base_addr: index of free msic ADC channel
|
||||
*
|
||||
* Enable all the three channels for conversion
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int set_up_therm_channel(u16 base_addr)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Enable all the sensor channels */
|
||||
ret = intel_msic_reg_write(base_addr, SKIN_SENSOR0_CODE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = intel_msic_reg_write(base_addr + 1, SKIN_SENSOR1_CODE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = intel_msic_reg_write(base_addr + 2, SYS_SENSOR_CODE);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Since this is the last channel, set the stop bit
|
||||
* to 1 by ORing the DIE_SENSOR_CODE with 0x10 */
|
||||
ret = intel_msic_reg_write(base_addr + 3,
|
||||
(MSIC_DIE_SENSOR_CODE | 0x10));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Enable ADC and start it */
|
||||
return configure_adc(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* reset_stopbit - sets the stop bit to 0 on the given channel
|
||||
* @addr: address of the channel
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int reset_stopbit(uint16_t addr)
|
||||
{
|
||||
int ret;
|
||||
uint8_t data;
|
||||
ret = intel_msic_reg_read(addr, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
/* Set the stop bit to zero */
|
||||
return intel_msic_reg_write(addr, (data & 0xEF));
|
||||
}
|
||||
|
||||
/**
|
||||
* find_free_channel - finds an empty channel for conversion
|
||||
*
|
||||
* If the ADC is not enabled then start using 0th channel
|
||||
* itself. Otherwise find an empty channel by looking for a
|
||||
* channel in which the stopbit is set to 1. returns the index
|
||||
* of the first free channel if succeeds or an error code.
|
||||
*
|
||||
* Context: can sleep
|
||||
*
|
||||
* FIXME: Ultimately the channel allocator will move into the intel_scu_ipc
|
||||
* code.
|
||||
*/
|
||||
static int find_free_channel(void)
|
||||
{
|
||||
int ret;
|
||||
int i;
|
||||
uint8_t data;
|
||||
|
||||
/* check whether ADC is enabled */
|
||||
ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL1, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if ((data & MSIC_ADC_ENBL) == 0)
|
||||
return 0;
|
||||
|
||||
/* ADC is already enabled; Looking for an empty channel */
|
||||
for (i = 0; i < ADC_CHANLS_MAX; i++) {
|
||||
ret = intel_msic_reg_read(ADC_CHNL_START_ADDR + i, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (data & MSIC_STOPBIT_MASK) {
|
||||
ret = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (ret > ADC_LOOP_MAX) ? (-EINVAL) : ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* mid_initialize_adc - initializing the ADC
|
||||
* @dev: our device structure
|
||||
*
|
||||
* Initialize the ADC for reading thermistor values. Can sleep.
|
||||
*/
|
||||
static int mid_initialize_adc(struct device *dev)
|
||||
{
|
||||
u8 data;
|
||||
u16 base_addr;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Ensure that adctherm is disabled before we
|
||||
* initialize the ADC
|
||||
*/
|
||||
ret = intel_msic_reg_read(INTEL_MSIC_ADC1CNTL3, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data &= ~MSIC_ADCTHERM_MASK;
|
||||
ret = intel_msic_reg_write(INTEL_MSIC_ADC1CNTL3, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Index of the first channel in which the stop bit is set */
|
||||
channel_index = find_free_channel();
|
||||
if (channel_index < 0) {
|
||||
dev_err(dev, "No free ADC channels");
|
||||
return channel_index;
|
||||
}
|
||||
|
||||
base_addr = ADC_CHNL_START_ADDR + channel_index;
|
||||
|
||||
if (!(channel_index == 0 || channel_index == ADC_LOOP_MAX)) {
|
||||
/* Reset stop bit for channels other than 0 and 12 */
|
||||
ret = reset_stopbit(base_addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Index of the first free channel */
|
||||
base_addr++;
|
||||
channel_index++;
|
||||
}
|
||||
|
||||
ret = set_up_therm_channel(base_addr);
|
||||
if (ret) {
|
||||
dev_err(dev, "unable to enable ADC");
|
||||
return ret;
|
||||
}
|
||||
dev_dbg(dev, "ADC initialization successful");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* initialize_sensor - sets default temp and timer ranges
|
||||
* @index: index of the sensor
|
||||
*
|
||||
* Context: can sleep
|
||||
*/
|
||||
static struct thermal_device_info *initialize_sensor(int index)
|
||||
{
|
||||
struct thermal_device_info *td_info =
|
||||
kzalloc(sizeof(struct thermal_device_info), GFP_KERNEL);
|
||||
|
||||
if (!td_info)
|
||||
return NULL;
|
||||
|
||||
/* Set the base addr of the channel for this sensor */
|
||||
td_info->chnl_addr = ADC_DATA_START_ADDR + 2 * (channel_index + index);
|
||||
/* Sensor 3 is direct conversion */
|
||||
if (index == 3)
|
||||
td_info->direct = 1;
|
||||
return td_info;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/**
|
||||
* mid_thermal_resume - resume routine
|
||||
* @dev: device structure
|
||||
*
|
||||
* mid thermal resume: re-initializes the adc. Can sleep.
|
||||
*/
|
||||
static int mid_thermal_resume(struct device *dev)
|
||||
{
|
||||
return mid_initialize_adc(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* mid_thermal_suspend - suspend routine
|
||||
* @dev: device structure
|
||||
*
|
||||
* mid thermal suspend implements the suspend functionality
|
||||
* by stopping the ADC. Can sleep.
|
||||
*/
|
||||
static int mid_thermal_suspend(struct device *dev)
|
||||
{
|
||||
/*
|
||||
* This just stops the ADC and does not disable it.
|
||||
* temporary workaround until we have a generic ADC driver.
|
||||
* If 0 is passed, it disables the ADC.
|
||||
*/
|
||||
return configure_adc(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mid_thermal_pm,
|
||||
mid_thermal_suspend, mid_thermal_resume);
|
||||
|
||||
/**
|
||||
* read_curr_temp - reads the current temperature and stores in temp
|
||||
* @temp: holds the current temperature value after reading
|
||||
*
|
||||
* Can sleep
|
||||
*/
|
||||
static int read_curr_temp(struct thermal_zone_device *tzd, int *temp)
|
||||
{
|
||||
WARN_ON(tzd == NULL);
|
||||
return mid_read_temp(tzd, temp);
|
||||
}
|
||||
|
||||
/* Can't be const */
|
||||
static struct thermal_zone_device_ops tzd_ops = {
|
||||
.get_temp = read_curr_temp,
|
||||
};
|
||||
|
||||
/**
|
||||
* mid_thermal_probe - mfld thermal initialize
|
||||
* @pdev: platform device structure
|
||||
*
|
||||
* mid thermal probe initializes the hardware and registers
|
||||
* all the sensors with the generic thermal framework. Can sleep.
|
||||
*/
|
||||
static int mid_thermal_probe(struct platform_device *pdev)
|
||||
{
|
||||
static char *name[MSIC_THERMAL_SENSORS] = {
|
||||
"skin0", "skin1", "sys", "msicdie"
|
||||
};
|
||||
|
||||
int ret;
|
||||
int i;
|
||||
struct platform_info *pinfo;
|
||||
|
||||
pinfo = devm_kzalloc(&pdev->dev, sizeof(struct platform_info),
|
||||
GFP_KERNEL);
|
||||
if (!pinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Initializing the hardware */
|
||||
ret = mid_initialize_adc(&pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "ADC init failed");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Register each sensor with the generic thermal framework*/
|
||||
for (i = 0; i < MSIC_THERMAL_SENSORS; i++) {
|
||||
struct thermal_device_info *td_info = initialize_sensor(i);
|
||||
|
||||
if (!td_info) {
|
||||
ret = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
pinfo->tzd[i] = thermal_zone_device_register(name[i],
|
||||
0, 0, td_info, &tzd_ops, NULL, 0, 0);
|
||||
if (IS_ERR(pinfo->tzd[i])) {
|
||||
kfree(td_info);
|
||||
ret = PTR_ERR(pinfo->tzd[i]);
|
||||
goto err;
|
||||
}
|
||||
ret = thermal_zone_device_enable(pinfo->tzd[i]);
|
||||
if (ret) {
|
||||
kfree(td_info);
|
||||
thermal_zone_device_unregister(pinfo->tzd[i]);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
pinfo->pdev = pdev;
|
||||
platform_set_drvdata(pdev, pinfo);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
while (--i >= 0) {
|
||||
kfree(pinfo->tzd[i]->devdata);
|
||||
thermal_zone_device_unregister(pinfo->tzd[i]);
|
||||
}
|
||||
configure_adc(0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* mid_thermal_remove - mfld thermal finalize
|
||||
* @dev: platform device structure
|
||||
*
|
||||
* MLFD thermal remove unregisters all the sensors from the generic
|
||||
* thermal framework. Can sleep.
|
||||
*/
|
||||
static int mid_thermal_remove(struct platform_device *pdev)
|
||||
{
|
||||
int i;
|
||||
struct platform_info *pinfo = platform_get_drvdata(pdev);
|
||||
|
||||
for (i = 0; i < MSIC_THERMAL_SENSORS; i++) {
|
||||
kfree(pinfo->tzd[i]->devdata);
|
||||
thermal_zone_device_unregister(pinfo->tzd[i]);
|
||||
}
|
||||
|
||||
/* Stop the ADC */
|
||||
return configure_adc(0);
|
||||
}
|
||||
|
||||
#define DRIVER_NAME "msic_thermal"
|
||||
|
||||
static const struct platform_device_id therm_id_table[] = {
|
||||
{ DRIVER_NAME, 1 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, therm_id_table);
|
||||
|
||||
static struct platform_driver mid_thermal_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.pm = &mid_thermal_pm,
|
||||
},
|
||||
.probe = mid_thermal_probe,
|
||||
.remove = mid_thermal_remove,
|
||||
.id_table = therm_id_table,
|
||||
};
|
||||
|
||||
module_platform_driver(mid_thermal_driver);
|
||||
|
||||
MODULE_AUTHOR("Durgadoss R <durgadoss.r@intel.com>");
|
||||
MODULE_DESCRIPTION("Intel Medfield Platform Thermal Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -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 = (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 */
|
Loading…
x
Reference in New Issue
Block a user