staging: hikey9xx: split hi6421v600 irq into a separate driver
Per MFD subsystem requirements, split the IRQ part of the driver into a separate one with just the IRQ handling code and the powerkey support. Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> Link: https://lore.kernel.org/r/709e01c9ffafe6cd0ecb23336b44f9bcde2b5bc2.1626515862.git.mchehab+huawei@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
49bd5091a8
commit
bb3b6552a5
@ -208,6 +208,16 @@ config CS5535_CLOCK_EVENT_SRC
|
|||||||
MFGPTs have a better resolution and max interval than the
|
MFGPTs have a better resolution and max interval than the
|
||||||
generic PIT, and are suitable for use as high-res timers.
|
generic PIT, and are suitable for use as high-res timers.
|
||||||
|
|
||||||
|
config HI6421V600_IRQ
|
||||||
|
tristate "HiSilicon Hi6421v600 IRQ and powerkey"
|
||||||
|
depends on OF
|
||||||
|
depends on SPMI
|
||||||
|
select MFD_CORE
|
||||||
|
select REGMAP_SPMI
|
||||||
|
help
|
||||||
|
This driver provides IRQ handling for Hi6421v600, used on
|
||||||
|
some Kirin chipsets, like the one at Hikey 970.
|
||||||
|
|
||||||
config HP_ILO
|
config HP_ILO
|
||||||
tristate "Channel interface driver for the HP iLO processor"
|
tristate "Channel interface driver for the HP iLO processor"
|
||||||
depends on PCI
|
depends on PCI
|
||||||
|
@ -57,3 +57,4 @@ obj-$(CONFIG_HABANA_AI) += habanalabs/
|
|||||||
obj-$(CONFIG_UACCE) += uacce/
|
obj-$(CONFIG_UACCE) += uacce/
|
||||||
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
|
obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o
|
||||||
obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
|
obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o
|
||||||
|
obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o
|
||||||
|
307
drivers/misc/hi6421v600-irq.c
Normal file
307
drivers/misc/hi6421v600-irq.c
Normal file
@ -0,0 +1,307 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Device driver for irqs in HISI PMIC IC
|
||||||
|
*
|
||||||
|
* Copyright (c) 2013 Linaro Ltd.
|
||||||
|
* Copyright (c) 2011 Hisilicon.
|
||||||
|
* Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <linux/mfd/hi6421-spmi-pmic.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of_gpio.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/irqdomain.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
|
struct hi6421v600_irq {
|
||||||
|
struct device *dev;
|
||||||
|
struct irq_domain *domain;
|
||||||
|
int irq;
|
||||||
|
unsigned int *irqs;
|
||||||
|
struct regmap *regmap;
|
||||||
|
|
||||||
|
/* Protect IRQ mask changes */
|
||||||
|
spinlock_t lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum hi6421v600_irq_list {
|
||||||
|
OTMP = 0,
|
||||||
|
VBUS_CONNECT,
|
||||||
|
VBUS_DISCONNECT,
|
||||||
|
ALARMON_R,
|
||||||
|
HOLD_6S,
|
||||||
|
HOLD_1S,
|
||||||
|
POWERKEY_UP,
|
||||||
|
POWERKEY_DOWN,
|
||||||
|
OCP_SCP_R,
|
||||||
|
COUL_R,
|
||||||
|
SIM0_HPD_R,
|
||||||
|
SIM0_HPD_F,
|
||||||
|
SIM1_HPD_R,
|
||||||
|
SIM1_HPD_F,
|
||||||
|
|
||||||
|
PMIC_IRQ_LIST_MAX
|
||||||
|
};
|
||||||
|
|
||||||
|
#define HISI_IRQ_BANK_SIZE 2
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IRQ number for the power key button and mask for both UP and DOWN IRQs
|
||||||
|
*/
|
||||||
|
#define HISI_POWERKEY_IRQ_NUM 0
|
||||||
|
#define HISI_IRQ_POWERKEY_UP_DOWN (BIT(POWERKEY_DOWN) | BIT(POWERKEY_UP))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Registers for IRQ address and IRQ mask bits
|
||||||
|
*
|
||||||
|
* Please notice that we need to regmap a larger region, as other
|
||||||
|
* registers are used by the irqs.
|
||||||
|
* See drivers/irq/hi6421-irq.c.
|
||||||
|
*/
|
||||||
|
#define SOC_PMIC_IRQ_MASK_0_ADDR 0x0202
|
||||||
|
#define SOC_PMIC_IRQ0_ADDR 0x0212
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The IRQs are mapped as:
|
||||||
|
*
|
||||||
|
* ====================== ============= ============ =====
|
||||||
|
* IRQ MASK REGISTER IRQ REGISTER BIT
|
||||||
|
* ====================== ============= ============ =====
|
||||||
|
* OTMP 0x0202 0x212 bit 0
|
||||||
|
* VBUS_CONNECT 0x0202 0x212 bit 1
|
||||||
|
* VBUS_DISCONNECT 0x0202 0x212 bit 2
|
||||||
|
* ALARMON_R 0x0202 0x212 bit 3
|
||||||
|
* HOLD_6S 0x0202 0x212 bit 4
|
||||||
|
* HOLD_1S 0x0202 0x212 bit 5
|
||||||
|
* POWERKEY_UP 0x0202 0x212 bit 6
|
||||||
|
* POWERKEY_DOWN 0x0202 0x212 bit 7
|
||||||
|
*
|
||||||
|
* OCP_SCP_R 0x0203 0x213 bit 0
|
||||||
|
* COUL_R 0x0203 0x213 bit 1
|
||||||
|
* SIM0_HPD_R 0x0203 0x213 bit 2
|
||||||
|
* SIM0_HPD_F 0x0203 0x213 bit 3
|
||||||
|
* SIM1_HPD_R 0x0203 0x213 bit 4
|
||||||
|
* SIM1_HPD_F 0x0203 0x213 bit 5
|
||||||
|
* ====================== ============= ============ =====
|
||||||
|
*
|
||||||
|
* Each mask register contains 8 bits. The ancillary macros below
|
||||||
|
* convert a number from 0 to 14 into a register address and a bit mask
|
||||||
|
*/
|
||||||
|
#define HISI_IRQ_MASK_REG(irq_data) (SOC_PMIC_IRQ_MASK_0_ADDR + \
|
||||||
|
(irqd_to_hwirq(irq_data) / BITS_PER_BYTE))
|
||||||
|
#define HISI_IRQ_MASK_BIT(irq_data) BIT(irqd_to_hwirq(irq_data) & (BITS_PER_BYTE - 1))
|
||||||
|
#define HISI_8BITS_MASK 0xff
|
||||||
|
|
||||||
|
static irqreturn_t hi6421v600_irq_handler(int irq, void *__priv)
|
||||||
|
{
|
||||||
|
struct hi6421v600_irq *priv = __priv;
|
||||||
|
unsigned long pending;
|
||||||
|
unsigned int in;
|
||||||
|
int i, offset;
|
||||||
|
|
||||||
|
for (i = 0; i < HISI_IRQ_BANK_SIZE; i++) {
|
||||||
|
regmap_read(priv->regmap, SOC_PMIC_IRQ0_ADDR + i, &in);
|
||||||
|
|
||||||
|
/* Mark pending IRQs as handled */
|
||||||
|
regmap_write(priv->regmap, SOC_PMIC_IRQ0_ADDR + i, in);
|
||||||
|
|
||||||
|
pending = in & HISI_8BITS_MASK;
|
||||||
|
|
||||||
|
if (i == HISI_POWERKEY_IRQ_NUM &&
|
||||||
|
(pending & HISI_IRQ_POWERKEY_UP_DOWN) == HISI_IRQ_POWERKEY_UP_DOWN) {
|
||||||
|
/*
|
||||||
|
* If both powerkey down and up IRQs are received,
|
||||||
|
* handle them at the right order
|
||||||
|
*/
|
||||||
|
generic_handle_irq(priv->irqs[POWERKEY_DOWN]);
|
||||||
|
generic_handle_irq(priv->irqs[POWERKEY_UP]);
|
||||||
|
pending &= ~HISI_IRQ_POWERKEY_UP_DOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pending)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for_each_set_bit(offset, &pending, BITS_PER_BYTE) {
|
||||||
|
generic_handle_irq(priv->irqs[offset + i * BITS_PER_BYTE]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hi6421v600_irq_mask(struct irq_data *d)
|
||||||
|
{
|
||||||
|
struct hi6421v600_irq *priv = irq_data_get_irq_chip_data(d);
|
||||||
|
unsigned long flags;
|
||||||
|
unsigned int data;
|
||||||
|
u32 offset;
|
||||||
|
|
||||||
|
offset = HISI_IRQ_MASK_REG(d);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&priv->lock, flags);
|
||||||
|
|
||||||
|
regmap_read(priv->regmap, offset, &data);
|
||||||
|
data |= HISI_IRQ_MASK_BIT(d);
|
||||||
|
regmap_write(priv->regmap, offset, data);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hi6421v600_irq_unmask(struct irq_data *d)
|
||||||
|
{
|
||||||
|
struct hi6421v600_irq *priv = irq_data_get_irq_chip_data(d);
|
||||||
|
u32 data, offset;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
offset = HISI_IRQ_MASK_REG(d);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&priv->lock, flags);
|
||||||
|
|
||||||
|
regmap_read(priv->regmap, offset, &data);
|
||||||
|
data &= ~HISI_IRQ_MASK_BIT(d);
|
||||||
|
regmap_write(priv->regmap, offset, data);
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&priv->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_chip hi6421v600_pmu_irqchip = {
|
||||||
|
.name = "hi6421v600-irq",
|
||||||
|
.irq_mask = hi6421v600_irq_mask,
|
||||||
|
.irq_unmask = hi6421v600_irq_unmask,
|
||||||
|
.irq_disable = hi6421v600_irq_mask,
|
||||||
|
.irq_enable = hi6421v600_irq_unmask,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int hi6421v600_irq_map(struct irq_domain *d, unsigned int virq,
|
||||||
|
irq_hw_number_t hw)
|
||||||
|
{
|
||||||
|
struct hi6421v600_irq *priv = d->host_data;
|
||||||
|
|
||||||
|
irq_set_chip_and_handler_name(virq, &hi6421v600_pmu_irqchip,
|
||||||
|
handle_simple_irq, "hi6421v600");
|
||||||
|
irq_set_chip_data(virq, priv);
|
||||||
|
irq_set_irq_type(virq, IRQ_TYPE_NONE);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct irq_domain_ops hi6421v600_domain_ops = {
|
||||||
|
.map = hi6421v600_irq_map,
|
||||||
|
.xlate = irq_domain_xlate_twocell,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void hi6421v600_irq_init(struct hi6421v600_irq *priv)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned int pending;
|
||||||
|
|
||||||
|
/* Mask all IRQs */
|
||||||
|
for (i = 0; i < HISI_IRQ_BANK_SIZE; i++)
|
||||||
|
regmap_write(priv->regmap, SOC_PMIC_IRQ_MASK_0_ADDR + i,
|
||||||
|
HISI_8BITS_MASK);
|
||||||
|
|
||||||
|
/* Mark all IRQs as handled */
|
||||||
|
for (i = 0; i < HISI_IRQ_BANK_SIZE; i++) {
|
||||||
|
regmap_read(priv->regmap, SOC_PMIC_IRQ0_ADDR + i, &pending);
|
||||||
|
regmap_write(priv->regmap, SOC_PMIC_IRQ0_ADDR + i,
|
||||||
|
HISI_8BITS_MASK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hi6421v600_irq_probe(struct platform_device *pdev)
|
||||||
|
{
|
||||||
|
struct device *pmic_dev = pdev->dev.parent;
|
||||||
|
struct device_node *np = pmic_dev->of_node;
|
||||||
|
struct platform_device *pmic_pdev;
|
||||||
|
struct device *dev = &pdev->dev;
|
||||||
|
struct hi6421v600_irq *priv;
|
||||||
|
struct hi6421_spmi_pmic *pmic;
|
||||||
|
unsigned int virq;
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This driver is meant to be called by hi6421-spmi-core,
|
||||||
|
* which should first set drvdata. If this doesn't happen, hit
|
||||||
|
* a warn on and return.
|
||||||
|
*/
|
||||||
|
pmic = dev_get_drvdata(pmic_dev);
|
||||||
|
if (WARN_ON(!pmic))
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv->dev = dev;
|
||||||
|
priv->regmap = pmic->regmap;
|
||||||
|
|
||||||
|
spin_lock_init(&priv->lock);
|
||||||
|
|
||||||
|
pmic_pdev = container_of(pmic_dev, struct platform_device, dev);
|
||||||
|
|
||||||
|
priv->irq = platform_get_irq(pmic_pdev, 0);
|
||||||
|
if (priv->irq < 0) {
|
||||||
|
dev_err(dev, "Error %d when getting IRQs\n", priv->irq);
|
||||||
|
return priv->irq;
|
||||||
|
}
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, priv);
|
||||||
|
|
||||||
|
hi6421v600_irq_init(priv);
|
||||||
|
|
||||||
|
priv->irqs = devm_kzalloc(dev, PMIC_IRQ_LIST_MAX * sizeof(int), GFP_KERNEL);
|
||||||
|
if (!priv->irqs)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
priv->domain = irq_domain_add_simple(np, PMIC_IRQ_LIST_MAX, 0,
|
||||||
|
&hi6421v600_domain_ops, priv);
|
||||||
|
if (!priv->domain) {
|
||||||
|
dev_err(dev, "Failed to create IRQ domain\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < PMIC_IRQ_LIST_MAX; i++) {
|
||||||
|
virq = irq_create_mapping(priv->domain, i);
|
||||||
|
if (!virq) {
|
||||||
|
dev_err(dev, "Failed to map H/W IRQ\n");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
priv->irqs[i] = virq;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = devm_request_threaded_irq(dev,
|
||||||
|
priv->irq, hi6421v600_irq_handler,
|
||||||
|
NULL,
|
||||||
|
IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND,
|
||||||
|
"pmic", priv);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to start IRQ handling thread: error %d\n",
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct platform_device_id hi6421v600_irq_table[] = {
|
||||||
|
{ .name = "hi6421v600-irq" },
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
MODULE_DEVICE_TABLE(platform, hi6421v600_irq_table);
|
||||||
|
|
||||||
|
static struct platform_driver hi6421v600_irq_driver = {
|
||||||
|
.id_table = hi6421v600_irq_table,
|
||||||
|
.driver = {
|
||||||
|
.name = "hi6421v600-irq",
|
||||||
|
},
|
||||||
|
.probe = hi6421v600_irq_probe,
|
||||||
|
};
|
||||||
|
module_platform_driver(hi6421v600_irq_driver);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("HiSilicon Hi6421v600 IRQ driver");
|
||||||
|
MODULE_LICENSE("GPL v2");
|
@ -7,205 +7,19 @@
|
|||||||
* Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
|
* Copyright (c) 2020-2021 Huawei Technologies Co., Ltd.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/bitops.h>
|
|
||||||
#include <linux/interrupt.h>
|
|
||||||
#include <linux/irq.h>
|
|
||||||
#include <linux/mfd/core.h>
|
#include <linux/mfd/core.h>
|
||||||
#include <linux/mfd/hi6421-spmi-pmic.h>
|
#include <linux/mfd/hi6421-spmi-pmic.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_gpio.h>
|
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spmi.h>
|
#include <linux/spmi.h>
|
||||||
|
|
||||||
enum hi6421_spmi_pmic_irq_list {
|
|
||||||
OTMP = 0,
|
|
||||||
VBUS_CONNECT,
|
|
||||||
VBUS_DISCONNECT,
|
|
||||||
ALARMON_R,
|
|
||||||
HOLD_6S,
|
|
||||||
HOLD_1S,
|
|
||||||
POWERKEY_UP,
|
|
||||||
POWERKEY_DOWN,
|
|
||||||
OCP_SCP_R,
|
|
||||||
COUL_R,
|
|
||||||
SIM0_HPD_R,
|
|
||||||
SIM0_HPD_F,
|
|
||||||
SIM1_HPD_R,
|
|
||||||
SIM1_HPD_F,
|
|
||||||
|
|
||||||
PMIC_IRQ_LIST_MAX
|
|
||||||
};
|
|
||||||
|
|
||||||
#define HISI_IRQ_BANK_SIZE 2
|
|
||||||
|
|
||||||
/*
|
|
||||||
* IRQ number for the power key button and mask for both UP and DOWN IRQs
|
|
||||||
*/
|
|
||||||
#define HISI_POWERKEY_IRQ_NUM 0
|
|
||||||
#define HISI_IRQ_POWERKEY_UP_DOWN (BIT(POWERKEY_DOWN) | BIT(POWERKEY_UP))
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Registers for IRQ address and IRQ mask bits
|
|
||||||
*
|
|
||||||
* Please notice that we need to regmap a larger region, as other
|
|
||||||
* registers are used by the regulators.
|
|
||||||
* See drivers/regulator/hi6421-regulator.c.
|
|
||||||
*/
|
|
||||||
#define SOC_PMIC_IRQ_MASK_0_ADDR 0x0202
|
|
||||||
#define SOC_PMIC_IRQ0_ADDR 0x0212
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The IRQs are mapped as:
|
|
||||||
*
|
|
||||||
* ====================== ============= ============ =====
|
|
||||||
* IRQ MASK REGISTER IRQ REGISTER BIT
|
|
||||||
* ====================== ============= ============ =====
|
|
||||||
* OTMP 0x0202 0x212 bit 0
|
|
||||||
* VBUS_CONNECT 0x0202 0x212 bit 1
|
|
||||||
* VBUS_DISCONNECT 0x0202 0x212 bit 2
|
|
||||||
* ALARMON_R 0x0202 0x212 bit 3
|
|
||||||
* HOLD_6S 0x0202 0x212 bit 4
|
|
||||||
* HOLD_1S 0x0202 0x212 bit 5
|
|
||||||
* POWERKEY_UP 0x0202 0x212 bit 6
|
|
||||||
* POWERKEY_DOWN 0x0202 0x212 bit 7
|
|
||||||
*
|
|
||||||
* OCP_SCP_R 0x0203 0x213 bit 0
|
|
||||||
* COUL_R 0x0203 0x213 bit 1
|
|
||||||
* SIM0_HPD_R 0x0203 0x213 bit 2
|
|
||||||
* SIM0_HPD_F 0x0203 0x213 bit 3
|
|
||||||
* SIM1_HPD_R 0x0203 0x213 bit 4
|
|
||||||
* SIM1_HPD_F 0x0203 0x213 bit 5
|
|
||||||
* ====================== ============= ============ =====
|
|
||||||
*
|
|
||||||
* Each mask register contains 8 bits. The ancillary macros below
|
|
||||||
* convert a number from 0 to 14 into a register address and a bit mask
|
|
||||||
*/
|
|
||||||
#define HISI_IRQ_MASK_REG(irq_data) (SOC_PMIC_IRQ_MASK_0_ADDR + \
|
|
||||||
(irqd_to_hwirq(irq_data) / BITS_PER_BYTE))
|
|
||||||
#define HISI_IRQ_MASK_BIT(irq_data) BIT(irqd_to_hwirq(irq_data) & (BITS_PER_BYTE - 1))
|
|
||||||
#define HISI_8BITS_MASK 0xff
|
|
||||||
|
|
||||||
static const struct mfd_cell hi6421v600_devs[] = {
|
static const struct mfd_cell hi6421v600_devs[] = {
|
||||||
|
{ .name = "hi6421v600-irq", },
|
||||||
{ .name = "hi6421v600-regulator", },
|
{ .name = "hi6421v600-regulator", },
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t hi6421_spmi_irq_handler(int irq, void *priv)
|
|
||||||
{
|
|
||||||
struct hi6421_spmi_pmic *ddata = (struct hi6421_spmi_pmic *)priv;
|
|
||||||
unsigned long pending;
|
|
||||||
unsigned int in;
|
|
||||||
int i, offset;
|
|
||||||
|
|
||||||
for (i = 0; i < HISI_IRQ_BANK_SIZE; i++) {
|
|
||||||
regmap_read(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, &in);
|
|
||||||
|
|
||||||
/* Mark pending IRQs as handled */
|
|
||||||
regmap_write(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, in);
|
|
||||||
|
|
||||||
pending = in & HISI_8BITS_MASK;
|
|
||||||
|
|
||||||
if (i == HISI_POWERKEY_IRQ_NUM &&
|
|
||||||
(pending & HISI_IRQ_POWERKEY_UP_DOWN) == HISI_IRQ_POWERKEY_UP_DOWN) {
|
|
||||||
/*
|
|
||||||
* If both powerkey down and up IRQs are received,
|
|
||||||
* handle them at the right order
|
|
||||||
*/
|
|
||||||
generic_handle_irq(ddata->irqs[POWERKEY_DOWN]);
|
|
||||||
generic_handle_irq(ddata->irqs[POWERKEY_UP]);
|
|
||||||
pending &= ~HISI_IRQ_POWERKEY_UP_DOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pending)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
for_each_set_bit(offset, &pending, BITS_PER_BYTE) {
|
|
||||||
generic_handle_irq(ddata->irqs[offset + i * BITS_PER_BYTE]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hi6421_spmi_irq_mask(struct irq_data *d)
|
|
||||||
{
|
|
||||||
struct hi6421_spmi_pmic *ddata = irq_data_get_irq_chip_data(d);
|
|
||||||
unsigned long flags;
|
|
||||||
unsigned int data;
|
|
||||||
u32 offset;
|
|
||||||
|
|
||||||
offset = HISI_IRQ_MASK_REG(d);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&ddata->lock, flags);
|
|
||||||
|
|
||||||
regmap_read(ddata->regmap, offset, &data);
|
|
||||||
data |= HISI_IRQ_MASK_BIT(d);
|
|
||||||
regmap_write(ddata->regmap, offset, data);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&ddata->lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hi6421_spmi_irq_unmask(struct irq_data *d)
|
|
||||||
{
|
|
||||||
struct hi6421_spmi_pmic *ddata = irq_data_get_irq_chip_data(d);
|
|
||||||
u32 data, offset;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
offset = HISI_IRQ_MASK_REG(d);
|
|
||||||
|
|
||||||
spin_lock_irqsave(&ddata->lock, flags);
|
|
||||||
|
|
||||||
regmap_read(ddata->regmap, offset, &data);
|
|
||||||
data &= ~HISI_IRQ_MASK_BIT(d);
|
|
||||||
regmap_write(ddata->regmap, offset, data);
|
|
||||||
|
|
||||||
spin_unlock_irqrestore(&ddata->lock, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct irq_chip hi6421_spmi_pmu_irqchip = {
|
|
||||||
.name = "hi6421v600-irq",
|
|
||||||
.irq_mask = hi6421_spmi_irq_mask,
|
|
||||||
.irq_unmask = hi6421_spmi_irq_unmask,
|
|
||||||
.irq_disable = hi6421_spmi_irq_mask,
|
|
||||||
.irq_enable = hi6421_spmi_irq_unmask,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int hi6421_spmi_irq_map(struct irq_domain *d, unsigned int virq,
|
|
||||||
irq_hw_number_t hw)
|
|
||||||
{
|
|
||||||
struct hi6421_spmi_pmic *ddata = d->host_data;
|
|
||||||
|
|
||||||
irq_set_chip_and_handler_name(virq, &hi6421_spmi_pmu_irqchip,
|
|
||||||
handle_simple_irq, "hi6421v600");
|
|
||||||
irq_set_chip_data(virq, ddata);
|
|
||||||
irq_set_irq_type(virq, IRQ_TYPE_NONE);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct irq_domain_ops hi6421_spmi_domain_ops = {
|
|
||||||
.map = hi6421_spmi_irq_map,
|
|
||||||
.xlate = irq_domain_xlate_twocell,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void hi6421_spmi_pmic_irq_init(struct hi6421_spmi_pmic *ddata)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
unsigned int pending;
|
|
||||||
|
|
||||||
/* Mask all IRQs */
|
|
||||||
for (i = 0; i < HISI_IRQ_BANK_SIZE; i++)
|
|
||||||
regmap_write(ddata->regmap, SOC_PMIC_IRQ_MASK_0_ADDR + i,
|
|
||||||
HISI_8BITS_MASK);
|
|
||||||
|
|
||||||
/* Mark all IRQs as handled */
|
|
||||||
for (i = 0; i < HISI_IRQ_BANK_SIZE; i++) {
|
|
||||||
regmap_read(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i, &pending);
|
|
||||||
regmap_write(ddata->regmap, SOC_PMIC_IRQ0_ADDR + i,
|
|
||||||
HISI_8BITS_MASK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct regmap_config regmap_config = {
|
static const struct regmap_config regmap_config = {
|
||||||
.reg_bits = 16,
|
.reg_bits = 16,
|
||||||
.val_bits = BITS_PER_BYTE,
|
.val_bits = BITS_PER_BYTE,
|
||||||
@ -216,12 +30,8 @@ static const struct regmap_config regmap_config = {
|
|||||||
static int hi6421_spmi_pmic_probe(struct spmi_device *sdev)
|
static int hi6421_spmi_pmic_probe(struct spmi_device *sdev)
|
||||||
{
|
{
|
||||||
struct device *dev = &sdev->dev;
|
struct device *dev = &sdev->dev;
|
||||||
struct device_node *np = dev->of_node;
|
int ret;
|
||||||
struct hi6421_spmi_pmic *ddata;
|
struct hi6421_spmi_pmic *ddata;
|
||||||
struct platform_device *pdev;
|
|
||||||
unsigned int virq;
|
|
||||||
int ret, i;
|
|
||||||
|
|
||||||
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
|
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
|
||||||
if (!ddata)
|
if (!ddata)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
@ -230,51 +40,8 @@ static int hi6421_spmi_pmic_probe(struct spmi_device *sdev)
|
|||||||
if (IS_ERR(ddata->regmap))
|
if (IS_ERR(ddata->regmap))
|
||||||
return PTR_ERR(ddata->regmap);
|
return PTR_ERR(ddata->regmap);
|
||||||
|
|
||||||
spin_lock_init(&ddata->lock);
|
|
||||||
|
|
||||||
ddata->dev = dev;
|
ddata->dev = dev;
|
||||||
|
|
||||||
pdev = container_of(dev, struct platform_device, dev);
|
|
||||||
|
|
||||||
ddata->irq = platform_get_irq(pdev, 0);
|
|
||||||
if (ddata->irq < 0) {
|
|
||||||
dev_err(dev, "Error %d when getting IRQs\n", ddata->irq);
|
|
||||||
return ddata->irq;
|
|
||||||
}
|
|
||||||
|
|
||||||
hi6421_spmi_pmic_irq_init(ddata);
|
|
||||||
|
|
||||||
ddata->irqs = devm_kzalloc(dev, PMIC_IRQ_LIST_MAX * sizeof(int), GFP_KERNEL);
|
|
||||||
if (!ddata->irqs)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
ddata->domain = irq_domain_add_simple(np, PMIC_IRQ_LIST_MAX, 0,
|
|
||||||
&hi6421_spmi_domain_ops, ddata);
|
|
||||||
if (!ddata->domain) {
|
|
||||||
dev_err(dev, "Failed to create IRQ domain\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < PMIC_IRQ_LIST_MAX; i++) {
|
|
||||||
virq = irq_create_mapping(ddata->domain, i);
|
|
||||||
if (!virq) {
|
|
||||||
dev_err(dev, "Failed to map H/W IRQ\n");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
ddata->irqs[i] = virq;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = devm_request_threaded_irq(dev,
|
|
||||||
ddata->irq, hi6421_spmi_irq_handler,
|
|
||||||
NULL,
|
|
||||||
IRQF_TRIGGER_LOW | IRQF_SHARED | IRQF_NO_SUSPEND,
|
|
||||||
"pmic", ddata);
|
|
||||||
if (ret < 0) {
|
|
||||||
dev_err(dev, "Failed to start IRQ handling thread: error %d\n",
|
|
||||||
ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_set_drvdata(&sdev->dev, ddata);
|
dev_set_drvdata(&sdev->dev, ddata);
|
||||||
|
|
||||||
ret = devm_mfd_add_devices(&sdev->dev, PLATFORM_DEVID_NONE,
|
ret = devm_mfd_add_devices(&sdev->dev, PLATFORM_DEVID_NONE,
|
||||||
|
@ -63,7 +63,7 @@ examples:
|
|||||||
- |
|
- |
|
||||||
|
|
||||||
pmic: pmic@0 {
|
pmic: pmic@0 {
|
||||||
compatible = "hisilicon,hi6421-spmi";
|
compatible = "hisilicon,hi6421v600-spmi";
|
||||||
reg = <0 0>;
|
reg = <0 0>;
|
||||||
|
|
||||||
#interrupt-cells = <2>;
|
#interrupt-cells = <2>;
|
||||||
|
@ -19,10 +19,6 @@ struct hi6421_spmi_pmic {
|
|||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
void __iomem *regs;
|
void __iomem *regs;
|
||||||
spinlock_t lock;
|
|
||||||
struct irq_domain *domain;
|
|
||||||
int irq;
|
|
||||||
unsigned int *irqs;
|
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user