// SPDX-License-Identifier: GPL-2.0
/*
* Gemini power management controller
* Copyright ( C ) 2017 Linus Walleij < linus . walleij @ linaro . org >
*
* Inspired by code from the SL3516 board support by Jason Lee
* Inspired by code from Janos Laube < janos . dev @ gmail . com >
*/
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/bitops.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/reboot.h>
#define GEMINI_PWC_ID 0 x00010500
#define GEMINI_PWC_IDREG 0 x00
#define GEMINI_PWC_CTRLREG 0 x04
#define GEMINI_PWC_STATREG 0 x08
#define GEMINI_CTRL_SHUTDOWN BIT(0 )
#define GEMINI_CTRL_ENABLE BIT(1 )
#define GEMINI_CTRL_IRQ_CLR BIT(2 )
#define GEMINI_STAT_CIR BIT(4 )
#define GEMINI_STAT_RTC BIT(5 )
#define GEMINI_STAT_POWERBUTTON BIT(6 )
struct gemini_powercon {
struct device *dev;
void __iomem *base;
};
static irqreturn_t gemini_powerbutton_interrupt(int irq, void *data)
{
struct gemini_powercon *gpw = data;
u32 val;
/* ACK the IRQ */
val = readl(gpw->base + GEMINI_PWC_CTRLREG);
val |= GEMINI_CTRL_IRQ_CLR;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
val = readl(gpw->base + GEMINI_PWC_STATREG);
val &= 0 x70U;
switch (val) {
case GEMINI_STAT_CIR:
/*
* We do not yet have a driver for the infrared
* controller so it can cause spurious poweroff
* events . Ignore those for now .
*/
dev_info(gpw->dev, "infrared poweroff - ignored\n" );
break ;
case GEMINI_STAT_RTC:
dev_info(gpw->dev, "RTC poweroff\n" );
orderly_poweroff(true );
break ;
case GEMINI_STAT_POWERBUTTON:
dev_info(gpw->dev, "poweroff button pressed\n" );
orderly_poweroff(true );
break ;
default :
dev_info(gpw->dev, "other power management IRQ\n" );
break ;
}
return IRQ_HANDLED;
}
static int gemini_poweroff(struct sys_off_data *data)
{
struct gemini_powercon *gpw = data->cb_data;
u32 val;
dev_crit(gpw->dev, "Gemini power off\n" );
val = readl(gpw->base + GEMINI_PWC_CTRLREG);
val |= GEMINI_CTRL_ENABLE | GEMINI_CTRL_IRQ_CLR;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
val &= ~GEMINI_CTRL_ENABLE;
val |= GEMINI_CTRL_SHUTDOWN;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
return NOTIFY_DONE;
}
static int gemini_poweroff_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct gemini_powercon *gpw;
u32 val;
int irq;
int ret;
gpw = devm_kzalloc(dev, sizeof (*gpw), GFP_KERNEL);
if (!gpw)
return -ENOMEM;
gpw->base = devm_platform_ioremap_resource(pdev, 0 );
if (IS_ERR(gpw->base))
return PTR_ERR(gpw->base);
irq = platform_get_irq(pdev, 0 );
if (irq < 0 )
return irq;
gpw->dev = dev;
val = readl(gpw->base + GEMINI_PWC_IDREG);
val &= 0 xFFFFFF00U;
if (val != GEMINI_PWC_ID) {
dev_err(dev, "wrong power controller ID: %08x\n" ,
val);
return -ENODEV;
}
/*
* Enable the power controller . This is crucial on Gemini
* systems : if this is not done , pressing the power button
* will result in unconditional poweroff without any warning .
* This makes the kernel handle the poweroff .
*/
val = readl(gpw->base + GEMINI_PWC_CTRLREG);
val |= GEMINI_CTRL_ENABLE;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
/* Clear the IRQ */
val = readl(gpw->base + GEMINI_PWC_CTRLREG);
val |= GEMINI_CTRL_IRQ_CLR;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
/* Wait for this to clear */
val = readl(gpw->base + GEMINI_PWC_STATREG);
while (val & 0 x70U)
val = readl(gpw->base + GEMINI_PWC_STATREG);
/* Clear the IRQ again */
val = readl(gpw->base + GEMINI_PWC_CTRLREG);
val |= GEMINI_CTRL_IRQ_CLR;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
ret = devm_request_irq(dev, irq, gemini_powerbutton_interrupt, 0 ,
"poweroff" , gpw);
if (ret)
return ret;
ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF,
SYS_OFF_PRIO_DEFAULT,
gemini_poweroff, gpw);
if (ret)
return ret;
dev_info(dev, "Gemini poweroff driver registered\n" );
return 0 ;
}
static const struct of_device_id gemini_poweroff_of_match[] = {
{
.compatible = "cortina,gemini-power-controller" ,
},
{}
};
static struct platform_driver gemini_poweroff_driver = {
.probe = gemini_poweroff_probe,
.driver = {
.name = "gemini-poweroff" ,
.of_match_table = gemini_poweroff_of_match,
},
};
builtin_platform_driver(gemini_poweroff_driver);
Messung V0.5 in Prozent C=96 H=97 G=96
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-10-01)
¤
*© Formatika GbR, Deutschland