18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Hisilicon Limited, All Rights Reserved.
48c2ecf20Sopenharmony_ci * Author: Jun Ma <majun258@huawei.com>
58c2ecf20Sopenharmony_ci * Author: Yun Wu <wuyun.wu@huawei.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/acpi.h>
98c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
108c2ecf20Sopenharmony_ci#include <linux/irqchip.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/msi.h>
138c2ecf20Sopenharmony_ci#include <linux/of_address.h>
148c2ecf20Sopenharmony_ci#include <linux/of_irq.h>
158c2ecf20Sopenharmony_ci#include <linux/of_platform.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* Interrupt numbers per mbigen node supported */
208c2ecf20Sopenharmony_ci#define IRQS_PER_MBIGEN_NODE		128
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* 64 irqs (Pin0-pin63) are reserved for each mbigen chip */
238c2ecf20Sopenharmony_ci#define RESERVED_IRQ_PER_MBIGEN_CHIP	64
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/* The maximum IRQ pin number of mbigen chip(start from 0) */
268c2ecf20Sopenharmony_ci#define MAXIMUM_IRQ_PIN_NUM		1407
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/**
298c2ecf20Sopenharmony_ci * In mbigen vector register
308c2ecf20Sopenharmony_ci * bit[21:12]:	event id value
318c2ecf20Sopenharmony_ci * bit[11:0]:	device id
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_ci#define IRQ_EVENT_ID_SHIFT		12
348c2ecf20Sopenharmony_ci#define IRQ_EVENT_ID_MASK		0x3ff
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/* register range of each mbigen node */
378c2ecf20Sopenharmony_ci#define MBIGEN_NODE_OFFSET		0x1000
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci/* offset of vector register in mbigen node */
408c2ecf20Sopenharmony_ci#define REG_MBIGEN_VEC_OFFSET		0x200
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/**
438c2ecf20Sopenharmony_ci * offset of clear register in mbigen node
448c2ecf20Sopenharmony_ci * This register is used to clear the status
458c2ecf20Sopenharmony_ci * of interrupt
468c2ecf20Sopenharmony_ci */
478c2ecf20Sopenharmony_ci#define REG_MBIGEN_CLEAR_OFFSET		0xa000
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/**
508c2ecf20Sopenharmony_ci * offset of interrupt type register
518c2ecf20Sopenharmony_ci * This register is used to configure interrupt
528c2ecf20Sopenharmony_ci * trigger type
538c2ecf20Sopenharmony_ci */
548c2ecf20Sopenharmony_ci#define REG_MBIGEN_TYPE_OFFSET		0x0
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/**
578c2ecf20Sopenharmony_ci * struct mbigen_device - holds the information of mbigen device.
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci * @pdev:		pointer to the platform device structure of mbigen chip.
608c2ecf20Sopenharmony_ci * @base:		mapped address of this mbigen chip.
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_cistruct mbigen_device {
638c2ecf20Sopenharmony_ci	struct platform_device	*pdev;
648c2ecf20Sopenharmony_ci	void __iomem		*base;
658c2ecf20Sopenharmony_ci};
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_cistatic inline unsigned int get_mbigen_vec_reg(irq_hw_number_t hwirq)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	unsigned int nid, pin;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP;
728c2ecf20Sopenharmony_ci	nid = hwirq / IRQS_PER_MBIGEN_NODE + 1;
738c2ecf20Sopenharmony_ci	pin = hwirq % IRQS_PER_MBIGEN_NODE;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return pin * 4 + nid * MBIGEN_NODE_OFFSET
768c2ecf20Sopenharmony_ci			+ REG_MBIGEN_VEC_OFFSET;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic inline void get_mbigen_type_reg(irq_hw_number_t hwirq,
808c2ecf20Sopenharmony_ci					u32 *mask, u32 *addr)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	unsigned int nid, irq_ofst, ofst;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	hwirq -= RESERVED_IRQ_PER_MBIGEN_CHIP;
858c2ecf20Sopenharmony_ci	nid = hwirq / IRQS_PER_MBIGEN_NODE + 1;
868c2ecf20Sopenharmony_ci	irq_ofst = hwirq % IRQS_PER_MBIGEN_NODE;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	*mask = 1 << (irq_ofst % 32);
898c2ecf20Sopenharmony_ci	ofst = irq_ofst / 32 * 4;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	*addr = ofst + nid * MBIGEN_NODE_OFFSET
928c2ecf20Sopenharmony_ci		+ REG_MBIGEN_TYPE_OFFSET;
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic inline void get_mbigen_clear_reg(irq_hw_number_t hwirq,
968c2ecf20Sopenharmony_ci					u32 *mask, u32 *addr)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	unsigned int ofst = (hwirq / 32) * 4;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	*mask = 1 << (hwirq % 32);
1018c2ecf20Sopenharmony_ci	*addr = ofst + REG_MBIGEN_CLEAR_OFFSET;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic void mbigen_eoi_irq(struct irq_data *data)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	void __iomem *base = data->chip_data;
1078c2ecf20Sopenharmony_ci	u32 mask, addr;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	get_mbigen_clear_reg(data->hwirq, &mask, &addr);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	writel_relaxed(mask, base + addr);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	irq_chip_eoi_parent(data);
1148c2ecf20Sopenharmony_ci}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_cistatic int mbigen_set_type(struct irq_data *data, unsigned int type)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	void __iomem *base = data->chip_data;
1198c2ecf20Sopenharmony_ci	u32 mask, addr, val;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (type != IRQ_TYPE_LEVEL_HIGH && type != IRQ_TYPE_EDGE_RISING)
1228c2ecf20Sopenharmony_ci		return -EINVAL;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	get_mbigen_type_reg(data->hwirq, &mask, &addr);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	val = readl_relaxed(base + addr);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (type == IRQ_TYPE_LEVEL_HIGH)
1298c2ecf20Sopenharmony_ci		val |= mask;
1308c2ecf20Sopenharmony_ci	else
1318c2ecf20Sopenharmony_ci		val &= ~mask;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	writel_relaxed(val, base + addr);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return 0;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic struct irq_chip mbigen_irq_chip = {
1398c2ecf20Sopenharmony_ci	.name =			"mbigen-v2",
1408c2ecf20Sopenharmony_ci	.irq_mask =		irq_chip_mask_parent,
1418c2ecf20Sopenharmony_ci	.irq_unmask =		irq_chip_unmask_parent,
1428c2ecf20Sopenharmony_ci	.irq_eoi =		mbigen_eoi_irq,
1438c2ecf20Sopenharmony_ci	.irq_set_type =		mbigen_set_type,
1448c2ecf20Sopenharmony_ci	.irq_set_affinity =	irq_chip_set_affinity_parent,
1458c2ecf20Sopenharmony_ci};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_cistatic void mbigen_write_msg(struct msi_desc *desc, struct msi_msg *msg)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	struct irq_data *d = irq_get_irq_data(desc->irq);
1508c2ecf20Sopenharmony_ci	void __iomem *base = d->chip_data;
1518c2ecf20Sopenharmony_ci	u32 val;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (!msg->address_lo && !msg->address_hi)
1548c2ecf20Sopenharmony_ci		return;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	base += get_mbigen_vec_reg(d->hwirq);
1578c2ecf20Sopenharmony_ci	val = readl_relaxed(base);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	val &= ~(IRQ_EVENT_ID_MASK << IRQ_EVENT_ID_SHIFT);
1608c2ecf20Sopenharmony_ci	val |= (msg->data << IRQ_EVENT_ID_SHIFT);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/* The address of doorbell is encoded in mbigen register by default
1638c2ecf20Sopenharmony_ci	 * So,we don't need to program the doorbell address at here
1648c2ecf20Sopenharmony_ci	 */
1658c2ecf20Sopenharmony_ci	writel_relaxed(val, base);
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cistatic int mbigen_domain_translate(struct irq_domain *d,
1698c2ecf20Sopenharmony_ci				    struct irq_fwspec *fwspec,
1708c2ecf20Sopenharmony_ci				    unsigned long *hwirq,
1718c2ecf20Sopenharmony_ci				    unsigned int *type)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	if (is_of_node(fwspec->fwnode) || is_acpi_device_node(fwspec->fwnode)) {
1748c2ecf20Sopenharmony_ci		if (fwspec->param_count != 2)
1758c2ecf20Sopenharmony_ci			return -EINVAL;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		if ((fwspec->param[0] > MAXIMUM_IRQ_PIN_NUM) ||
1788c2ecf20Sopenharmony_ci			(fwspec->param[0] < RESERVED_IRQ_PER_MBIGEN_CHIP))
1798c2ecf20Sopenharmony_ci			return -EINVAL;
1808c2ecf20Sopenharmony_ci		else
1818c2ecf20Sopenharmony_ci			*hwirq = fwspec->param[0];
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci		/* If there is no valid irq type, just use the default type */
1848c2ecf20Sopenharmony_ci		if ((fwspec->param[1] == IRQ_TYPE_EDGE_RISING) ||
1858c2ecf20Sopenharmony_ci			(fwspec->param[1] == IRQ_TYPE_LEVEL_HIGH))
1868c2ecf20Sopenharmony_ci			*type = fwspec->param[1];
1878c2ecf20Sopenharmony_ci		else
1888c2ecf20Sopenharmony_ci			return -EINVAL;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci		return 0;
1918c2ecf20Sopenharmony_ci	}
1928c2ecf20Sopenharmony_ci	return -EINVAL;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_cistatic int mbigen_irq_domain_alloc(struct irq_domain *domain,
1968c2ecf20Sopenharmony_ci					unsigned int virq,
1978c2ecf20Sopenharmony_ci					unsigned int nr_irqs,
1988c2ecf20Sopenharmony_ci					void *args)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct irq_fwspec *fwspec = args;
2018c2ecf20Sopenharmony_ci	irq_hw_number_t hwirq;
2028c2ecf20Sopenharmony_ci	unsigned int type;
2038c2ecf20Sopenharmony_ci	struct mbigen_device *mgn_chip;
2048c2ecf20Sopenharmony_ci	int i, err;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	err = mbigen_domain_translate(domain, fwspec, &hwirq, &type);
2078c2ecf20Sopenharmony_ci	if (err)
2088c2ecf20Sopenharmony_ci		return err;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	err = platform_msi_domain_alloc(domain, virq, nr_irqs);
2118c2ecf20Sopenharmony_ci	if (err)
2128c2ecf20Sopenharmony_ci		return err;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	mgn_chip = platform_msi_get_host_data(domain);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	for (i = 0; i < nr_irqs; i++)
2178c2ecf20Sopenharmony_ci		irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq + i,
2188c2ecf20Sopenharmony_ci				      &mbigen_irq_chip, mgn_chip->base);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return 0;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_cistatic void mbigen_irq_domain_free(struct irq_domain *domain, unsigned int virq,
2248c2ecf20Sopenharmony_ci				   unsigned int nr_irqs)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	platform_msi_domain_free(domain, virq, nr_irqs);
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic const struct irq_domain_ops mbigen_domain_ops = {
2308c2ecf20Sopenharmony_ci	.translate	= mbigen_domain_translate,
2318c2ecf20Sopenharmony_ci	.alloc		= mbigen_irq_domain_alloc,
2328c2ecf20Sopenharmony_ci	.free		= mbigen_irq_domain_free,
2338c2ecf20Sopenharmony_ci};
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistatic int mbigen_of_create_domain(struct platform_device *pdev,
2368c2ecf20Sopenharmony_ci				   struct mbigen_device *mgn_chip)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	struct device *parent;
2398c2ecf20Sopenharmony_ci	struct platform_device *child;
2408c2ecf20Sopenharmony_ci	struct irq_domain *domain;
2418c2ecf20Sopenharmony_ci	struct device_node *np;
2428c2ecf20Sopenharmony_ci	u32 num_pins;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	for_each_child_of_node(pdev->dev.of_node, np) {
2458c2ecf20Sopenharmony_ci		if (!of_property_read_bool(np, "interrupt-controller"))
2468c2ecf20Sopenharmony_ci			continue;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci		parent = platform_bus_type.dev_root;
2498c2ecf20Sopenharmony_ci		child = of_platform_device_create(np, NULL, parent);
2508c2ecf20Sopenharmony_ci		if (!child) {
2518c2ecf20Sopenharmony_ci			of_node_put(np);
2528c2ecf20Sopenharmony_ci			return -ENOMEM;
2538c2ecf20Sopenharmony_ci		}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci		if (of_property_read_u32(child->dev.of_node, "num-pins",
2568c2ecf20Sopenharmony_ci					 &num_pins) < 0) {
2578c2ecf20Sopenharmony_ci			dev_err(&pdev->dev, "No num-pins property\n");
2588c2ecf20Sopenharmony_ci			of_node_put(np);
2598c2ecf20Sopenharmony_ci			return -EINVAL;
2608c2ecf20Sopenharmony_ci		}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci		domain = platform_msi_create_device_domain(&child->dev, num_pins,
2638c2ecf20Sopenharmony_ci							   mbigen_write_msg,
2648c2ecf20Sopenharmony_ci							   &mbigen_domain_ops,
2658c2ecf20Sopenharmony_ci							   mgn_chip);
2668c2ecf20Sopenharmony_ci		if (!domain) {
2678c2ecf20Sopenharmony_ci			of_node_put(np);
2688c2ecf20Sopenharmony_ci			return -ENOMEM;
2698c2ecf20Sopenharmony_ci		}
2708c2ecf20Sopenharmony_ci	}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	return 0;
2738c2ecf20Sopenharmony_ci}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI
2768c2ecf20Sopenharmony_cistatic int mbigen_acpi_create_domain(struct platform_device *pdev,
2778c2ecf20Sopenharmony_ci				     struct mbigen_device *mgn_chip)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	struct irq_domain *domain;
2808c2ecf20Sopenharmony_ci	u32 num_pins = 0;
2818c2ecf20Sopenharmony_ci	int ret;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/*
2848c2ecf20Sopenharmony_ci	 * "num-pins" is the total number of interrupt pins implemented in
2858c2ecf20Sopenharmony_ci	 * this mbigen instance, and mbigen is an interrupt controller
2868c2ecf20Sopenharmony_ci	 * connected to ITS  converting wired interrupts into MSI, so we
2878c2ecf20Sopenharmony_ci	 * use "num-pins" to alloc MSI vectors which are needed by client
2888c2ecf20Sopenharmony_ci	 * devices connected to it.
2898c2ecf20Sopenharmony_ci	 *
2908c2ecf20Sopenharmony_ci	 * Here is the DSDT device node used for mbigen in firmware:
2918c2ecf20Sopenharmony_ci	 *	Device(MBI0) {
2928c2ecf20Sopenharmony_ci	 *		Name(_HID, "HISI0152")
2938c2ecf20Sopenharmony_ci	 *		Name(_UID, Zero)
2948c2ecf20Sopenharmony_ci	 *		Name(_CRS, ResourceTemplate() {
2958c2ecf20Sopenharmony_ci	 *			Memory32Fixed(ReadWrite, 0xa0080000, 0x10000)
2968c2ecf20Sopenharmony_ci	 *		})
2978c2ecf20Sopenharmony_ci	 *
2988c2ecf20Sopenharmony_ci	 *		Name(_DSD, Package () {
2998c2ecf20Sopenharmony_ci	 *			ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
3008c2ecf20Sopenharmony_ci	 *			Package () {
3018c2ecf20Sopenharmony_ci	 *				Package () {"num-pins", 378}
3028c2ecf20Sopenharmony_ci	 *			}
3038c2ecf20Sopenharmony_ci	 *		})
3048c2ecf20Sopenharmony_ci	 *	}
3058c2ecf20Sopenharmony_ci	 */
3068c2ecf20Sopenharmony_ci	ret = device_property_read_u32(&pdev->dev, "num-pins", &num_pins);
3078c2ecf20Sopenharmony_ci	if (ret || num_pins == 0)
3088c2ecf20Sopenharmony_ci		return -EINVAL;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	domain = platform_msi_create_device_domain(&pdev->dev, num_pins,
3118c2ecf20Sopenharmony_ci						   mbigen_write_msg,
3128c2ecf20Sopenharmony_ci						   &mbigen_domain_ops,
3138c2ecf20Sopenharmony_ci						   mgn_chip);
3148c2ecf20Sopenharmony_ci	if (!domain)
3158c2ecf20Sopenharmony_ci		return -ENOMEM;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	return 0;
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci#else
3208c2ecf20Sopenharmony_cistatic inline int mbigen_acpi_create_domain(struct platform_device *pdev,
3218c2ecf20Sopenharmony_ci					    struct mbigen_device *mgn_chip)
3228c2ecf20Sopenharmony_ci{
3238c2ecf20Sopenharmony_ci	return -ENODEV;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci#endif
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_cistatic int mbigen_device_probe(struct platform_device *pdev)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct mbigen_device *mgn_chip;
3308c2ecf20Sopenharmony_ci	struct resource *res;
3318c2ecf20Sopenharmony_ci	int err;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	mgn_chip = devm_kzalloc(&pdev->dev, sizeof(*mgn_chip), GFP_KERNEL);
3348c2ecf20Sopenharmony_ci	if (!mgn_chip)
3358c2ecf20Sopenharmony_ci		return -ENOMEM;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	mgn_chip->pdev = pdev;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3408c2ecf20Sopenharmony_ci	if (!res)
3418c2ecf20Sopenharmony_ci		return -EINVAL;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	mgn_chip->base = devm_ioremap(&pdev->dev, res->start,
3448c2ecf20Sopenharmony_ci				      resource_size(res));
3458c2ecf20Sopenharmony_ci	if (!mgn_chip->base) {
3468c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to ioremap %pR\n", res);
3478c2ecf20Sopenharmony_ci		return -ENOMEM;
3488c2ecf20Sopenharmony_ci	}
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node)
3518c2ecf20Sopenharmony_ci		err = mbigen_of_create_domain(pdev, mgn_chip);
3528c2ecf20Sopenharmony_ci	else if (ACPI_COMPANION(&pdev->dev))
3538c2ecf20Sopenharmony_ci		err = mbigen_acpi_create_domain(pdev, mgn_chip);
3548c2ecf20Sopenharmony_ci	else
3558c2ecf20Sopenharmony_ci		err = -EINVAL;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	if (err) {
3588c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Failed to create mbi-gen irqdomain\n");
3598c2ecf20Sopenharmony_ci		return err;
3608c2ecf20Sopenharmony_ci	}
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, mgn_chip);
3638c2ecf20Sopenharmony_ci	return 0;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic const struct of_device_id mbigen_of_match[] = {
3678c2ecf20Sopenharmony_ci	{ .compatible = "hisilicon,mbigen-v2" },
3688c2ecf20Sopenharmony_ci	{ /* END */ }
3698c2ecf20Sopenharmony_ci};
3708c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, mbigen_of_match);
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic const struct acpi_device_id mbigen_acpi_match[] = {
3738c2ecf20Sopenharmony_ci	{ "HISI0152", 0 },
3748c2ecf20Sopenharmony_ci	{}
3758c2ecf20Sopenharmony_ci};
3768c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, mbigen_acpi_match);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_cistatic struct platform_driver mbigen_platform_driver = {
3798c2ecf20Sopenharmony_ci	.driver = {
3808c2ecf20Sopenharmony_ci		.name		= "Hisilicon MBIGEN-V2",
3818c2ecf20Sopenharmony_ci		.of_match_table	= mbigen_of_match,
3828c2ecf20Sopenharmony_ci		.acpi_match_table = ACPI_PTR(mbigen_acpi_match),
3838c2ecf20Sopenharmony_ci		.suppress_bind_attrs = true,
3848c2ecf20Sopenharmony_ci	},
3858c2ecf20Sopenharmony_ci	.probe			= mbigen_device_probe,
3868c2ecf20Sopenharmony_ci};
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cimodule_platform_driver(mbigen_platform_driver);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jun Ma <majun258@huawei.com>");
3918c2ecf20Sopenharmony_ciMODULE_AUTHOR("Yun Wu <wuyun.wu@huawei.com>");
3928c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
3938c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Hisilicon MBI Generator driver");
394