162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2015-2018, Intel Corporation.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#define pr_fmt(fmt) "aspeed-kcs-bmc: " fmt
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/atomic.h>
962306a36Sopenharmony_ci#include <linux/errno.h>
1062306a36Sopenharmony_ci#include <linux/interrupt.h>
1162306a36Sopenharmony_ci#include <linux/io.h>
1262306a36Sopenharmony_ci#include <linux/irq.h>
1362306a36Sopenharmony_ci#include <linux/mfd/syscon.h>
1462306a36Sopenharmony_ci#include <linux/module.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/of_address.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/poll.h>
1962306a36Sopenharmony_ci#include <linux/regmap.h>
2062306a36Sopenharmony_ci#include <linux/sched.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci#include <linux/timer.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "kcs_bmc_device.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define DEVICE_NAME     "ast-kcs-bmc"
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define KCS_CHANNEL_MAX     4
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/*
3262306a36Sopenharmony_ci * Field class descriptions
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * LPCyE	Enable LPC channel y
3562306a36Sopenharmony_ci * IBFIEy	Input Buffer Full IRQ Enable for LPC channel y
3662306a36Sopenharmony_ci * IRQxEy	Assert SerIRQ x for LPC channel y (Deprecated, use IDyIRQX, IRQXEy)
3762306a36Sopenharmony_ci * IDyIRQX	Use the specified 4-bit SerIRQ for LPC channel y
3862306a36Sopenharmony_ci * SELyIRQX	SerIRQ polarity for LPC channel y (low: 0, high: 1)
3962306a36Sopenharmony_ci * IRQXEy	Assert the SerIRQ specified in IDyIRQX for LPC channel y
4062306a36Sopenharmony_ci */
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define LPC_TYIRQX_LOW       0b00
4362306a36Sopenharmony_ci#define LPC_TYIRQX_HIGH      0b01
4462306a36Sopenharmony_ci#define LPC_TYIRQX_RSVD      0b10
4562306a36Sopenharmony_ci#define LPC_TYIRQX_RISING    0b11
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci#define LPC_HICR0            0x000
4862306a36Sopenharmony_ci#define     LPC_HICR0_LPC3E          BIT(7)
4962306a36Sopenharmony_ci#define     LPC_HICR0_LPC2E          BIT(6)
5062306a36Sopenharmony_ci#define     LPC_HICR0_LPC1E          BIT(5)
5162306a36Sopenharmony_ci#define LPC_HICR2            0x008
5262306a36Sopenharmony_ci#define     LPC_HICR2_IBFIE3         BIT(3)
5362306a36Sopenharmony_ci#define     LPC_HICR2_IBFIE2         BIT(2)
5462306a36Sopenharmony_ci#define     LPC_HICR2_IBFIE1         BIT(1)
5562306a36Sopenharmony_ci#define LPC_HICR4            0x010
5662306a36Sopenharmony_ci#define     LPC_HICR4_LADR12AS       BIT(7)
5762306a36Sopenharmony_ci#define     LPC_HICR4_KCSENBL        BIT(2)
5862306a36Sopenharmony_ci#define LPC_SIRQCR0	     0x070
5962306a36Sopenharmony_ci/* IRQ{12,1}E1 are deprecated as of AST2600 A3 but necessary for prior chips */
6062306a36Sopenharmony_ci#define     LPC_SIRQCR0_IRQ12E1	     BIT(1)
6162306a36Sopenharmony_ci#define     LPC_SIRQCR0_IRQ1E1	     BIT(0)
6262306a36Sopenharmony_ci#define LPC_HICR5	     0x080
6362306a36Sopenharmony_ci#define     LPC_HICR5_ID3IRQX_MASK   GENMASK(23, 20)
6462306a36Sopenharmony_ci#define     LPC_HICR5_ID3IRQX_SHIFT  20
6562306a36Sopenharmony_ci#define     LPC_HICR5_ID2IRQX_MASK   GENMASK(19, 16)
6662306a36Sopenharmony_ci#define     LPC_HICR5_ID2IRQX_SHIFT  16
6762306a36Sopenharmony_ci#define     LPC_HICR5_SEL3IRQX       BIT(15)
6862306a36Sopenharmony_ci#define     LPC_HICR5_IRQXE3         BIT(14)
6962306a36Sopenharmony_ci#define     LPC_HICR5_SEL2IRQX       BIT(13)
7062306a36Sopenharmony_ci#define     LPC_HICR5_IRQXE2         BIT(12)
7162306a36Sopenharmony_ci#define LPC_LADR3H           0x014
7262306a36Sopenharmony_ci#define LPC_LADR3L           0x018
7362306a36Sopenharmony_ci#define LPC_LADR12H          0x01C
7462306a36Sopenharmony_ci#define LPC_LADR12L          0x020
7562306a36Sopenharmony_ci#define LPC_IDR1             0x024
7662306a36Sopenharmony_ci#define LPC_IDR2             0x028
7762306a36Sopenharmony_ci#define LPC_IDR3             0x02C
7862306a36Sopenharmony_ci#define LPC_ODR1             0x030
7962306a36Sopenharmony_ci#define LPC_ODR2             0x034
8062306a36Sopenharmony_ci#define LPC_ODR3             0x038
8162306a36Sopenharmony_ci#define LPC_STR1             0x03C
8262306a36Sopenharmony_ci#define LPC_STR2             0x040
8362306a36Sopenharmony_ci#define LPC_STR3             0x044
8462306a36Sopenharmony_ci#define LPC_HICRB            0x100
8562306a36Sopenharmony_ci#define     LPC_HICRB_EN16LADR2      BIT(5)
8662306a36Sopenharmony_ci#define     LPC_HICRB_EN16LADR1      BIT(4)
8762306a36Sopenharmony_ci#define     LPC_HICRB_IBFIE4         BIT(1)
8862306a36Sopenharmony_ci#define     LPC_HICRB_LPC4E          BIT(0)
8962306a36Sopenharmony_ci#define LPC_HICRC            0x104
9062306a36Sopenharmony_ci#define     LPC_HICRC_ID4IRQX_MASK   GENMASK(7, 4)
9162306a36Sopenharmony_ci#define     LPC_HICRC_ID4IRQX_SHIFT  4
9262306a36Sopenharmony_ci#define     LPC_HICRC_TY4IRQX_MASK   GENMASK(3, 2)
9362306a36Sopenharmony_ci#define     LPC_HICRC_TY4IRQX_SHIFT  2
9462306a36Sopenharmony_ci#define     LPC_HICRC_OBF4_AUTO_CLR  BIT(1)
9562306a36Sopenharmony_ci#define     LPC_HICRC_IRQXE4         BIT(0)
9662306a36Sopenharmony_ci#define LPC_LADR4            0x110
9762306a36Sopenharmony_ci#define LPC_IDR4             0x114
9862306a36Sopenharmony_ci#define LPC_ODR4             0x118
9962306a36Sopenharmony_ci#define LPC_STR4             0x11C
10062306a36Sopenharmony_ci#define LPC_LSADR12	     0x120
10162306a36Sopenharmony_ci#define     LPC_LSADR12_LSADR2_MASK  GENMASK(31, 16)
10262306a36Sopenharmony_ci#define     LPC_LSADR12_LSADR2_SHIFT 16
10362306a36Sopenharmony_ci#define     LPC_LSADR12_LSADR1_MASK  GENMASK(15, 0)
10462306a36Sopenharmony_ci#define     LPC_LSADR12_LSADR1_SHIFT 0
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci#define OBE_POLL_PERIOD	     (HZ / 2)
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cienum aspeed_kcs_irq_mode {
10962306a36Sopenharmony_ci	aspeed_kcs_irq_none,
11062306a36Sopenharmony_ci	aspeed_kcs_irq_serirq,
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistruct aspeed_kcs_bmc {
11462306a36Sopenharmony_ci	struct kcs_bmc_device kcs_bmc;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	struct regmap *map;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	struct {
11962306a36Sopenharmony_ci		enum aspeed_kcs_irq_mode mode;
12062306a36Sopenharmony_ci		int id;
12162306a36Sopenharmony_ci	} upstream_irq;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	struct {
12462306a36Sopenharmony_ci		spinlock_t lock;
12562306a36Sopenharmony_ci		bool remove;
12662306a36Sopenharmony_ci		struct timer_list timer;
12762306a36Sopenharmony_ci	} obe;
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic inline struct aspeed_kcs_bmc *to_aspeed_kcs_bmc(struct kcs_bmc_device *kcs_bmc)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	return container_of(kcs_bmc, struct aspeed_kcs_bmc, kcs_bmc);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic u8 aspeed_kcs_inb(struct kcs_bmc_device *kcs_bmc, u32 reg)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
13862306a36Sopenharmony_ci	u32 val = 0;
13962306a36Sopenharmony_ci	int rc;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	rc = regmap_read(priv->map, reg, &val);
14262306a36Sopenharmony_ci	WARN(rc != 0, "regmap_read() failed: %d\n", rc);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	return rc == 0 ? (u8) val : 0;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic void aspeed_kcs_outb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 data)
14862306a36Sopenharmony_ci{
14962306a36Sopenharmony_ci	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
15062306a36Sopenharmony_ci	int rc;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	rc = regmap_write(priv->map, reg, data);
15362306a36Sopenharmony_ci	WARN(rc != 0, "regmap_write() failed: %d\n", rc);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* Trigger the upstream IRQ on ODR writes, if enabled */
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	switch (reg) {
15862306a36Sopenharmony_ci	case LPC_ODR1:
15962306a36Sopenharmony_ci	case LPC_ODR2:
16062306a36Sopenharmony_ci	case LPC_ODR3:
16162306a36Sopenharmony_ci	case LPC_ODR4:
16262306a36Sopenharmony_ci		break;
16362306a36Sopenharmony_ci	default:
16462306a36Sopenharmony_ci		return;
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (priv->upstream_irq.mode != aspeed_kcs_irq_serirq)
16862306a36Sopenharmony_ci		return;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	switch (kcs_bmc->channel) {
17162306a36Sopenharmony_ci	case 1:
17262306a36Sopenharmony_ci		switch (priv->upstream_irq.id) {
17362306a36Sopenharmony_ci		case 12:
17462306a36Sopenharmony_ci			regmap_update_bits(priv->map, LPC_SIRQCR0, LPC_SIRQCR0_IRQ12E1,
17562306a36Sopenharmony_ci					   LPC_SIRQCR0_IRQ12E1);
17662306a36Sopenharmony_ci			break;
17762306a36Sopenharmony_ci		case 1:
17862306a36Sopenharmony_ci			regmap_update_bits(priv->map, LPC_SIRQCR0, LPC_SIRQCR0_IRQ1E1,
17962306a36Sopenharmony_ci					   LPC_SIRQCR0_IRQ1E1);
18062306a36Sopenharmony_ci			break;
18162306a36Sopenharmony_ci		default:
18262306a36Sopenharmony_ci			break;
18362306a36Sopenharmony_ci		}
18462306a36Sopenharmony_ci		break;
18562306a36Sopenharmony_ci	case 2:
18662306a36Sopenharmony_ci		regmap_update_bits(priv->map, LPC_HICR5, LPC_HICR5_IRQXE2, LPC_HICR5_IRQXE2);
18762306a36Sopenharmony_ci		break;
18862306a36Sopenharmony_ci	case 3:
18962306a36Sopenharmony_ci		regmap_update_bits(priv->map, LPC_HICR5, LPC_HICR5_IRQXE3, LPC_HICR5_IRQXE3);
19062306a36Sopenharmony_ci		break;
19162306a36Sopenharmony_ci	case 4:
19262306a36Sopenharmony_ci		regmap_update_bits(priv->map, LPC_HICRC, LPC_HICRC_IRQXE4, LPC_HICRC_IRQXE4);
19362306a36Sopenharmony_ci		break;
19462306a36Sopenharmony_ci	default:
19562306a36Sopenharmony_ci		break;
19662306a36Sopenharmony_ci	}
19762306a36Sopenharmony_ci}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_cistatic void aspeed_kcs_updateb(struct kcs_bmc_device *kcs_bmc, u32 reg, u8 mask, u8 val)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
20262306a36Sopenharmony_ci	int rc;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	rc = regmap_update_bits(priv->map, reg, mask, val);
20562306a36Sopenharmony_ci	WARN(rc != 0, "regmap_update_bits() failed: %d\n", rc);
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/*
20962306a36Sopenharmony_ci * We note D for Data, and C for Cmd/Status, default rules are
21062306a36Sopenharmony_ci *
21162306a36Sopenharmony_ci * 1. Only the D address is given:
21262306a36Sopenharmony_ci *   A. KCS1/KCS2 (D/C: X/X+4)
21362306a36Sopenharmony_ci *      D/C: CA0h/CA4h
21462306a36Sopenharmony_ci *      D/C: CA8h/CACh
21562306a36Sopenharmony_ci *   B. KCS3 (D/C: XX2/XX3h)
21662306a36Sopenharmony_ci *      D/C: CA2h/CA3h
21762306a36Sopenharmony_ci *   C. KCS4 (D/C: X/X+1)
21862306a36Sopenharmony_ci *      D/C: CA4h/CA5h
21962306a36Sopenharmony_ci *
22062306a36Sopenharmony_ci * 2. Both the D/C addresses are given:
22162306a36Sopenharmony_ci *   A. KCS1/KCS2/KCS4 (D/C: X/Y)
22262306a36Sopenharmony_ci *      D/C: CA0h/CA1h
22362306a36Sopenharmony_ci *      D/C: CA8h/CA9h
22462306a36Sopenharmony_ci *      D/C: CA4h/CA5h
22562306a36Sopenharmony_ci *   B. KCS3 (D/C: XX2/XX3h)
22662306a36Sopenharmony_ci *      D/C: CA2h/CA3h
22762306a36Sopenharmony_ci */
22862306a36Sopenharmony_cistatic int aspeed_kcs_set_address(struct kcs_bmc_device *kcs_bmc, u32 addrs[2], int nr_addrs)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if (WARN_ON(nr_addrs < 1 || nr_addrs > 2))
23362306a36Sopenharmony_ci		return -EINVAL;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	switch (priv->kcs_bmc.channel) {
23662306a36Sopenharmony_ci	case 1:
23762306a36Sopenharmony_ci		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS, 0);
23862306a36Sopenharmony_ci		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
23962306a36Sopenharmony_ci		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
24062306a36Sopenharmony_ci		if (nr_addrs == 2) {
24162306a36Sopenharmony_ci			regmap_update_bits(priv->map, LPC_LSADR12, LPC_LSADR12_LSADR1_MASK,
24262306a36Sopenharmony_ci					   addrs[1] << LPC_LSADR12_LSADR1_SHIFT);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_EN16LADR1,
24562306a36Sopenharmony_ci					   LPC_HICRB_EN16LADR1);
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci		break;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	case 2:
25062306a36Sopenharmony_ci		regmap_update_bits(priv->map, LPC_HICR4, LPC_HICR4_LADR12AS, LPC_HICR4_LADR12AS);
25162306a36Sopenharmony_ci		regmap_write(priv->map, LPC_LADR12H, addrs[0] >> 8);
25262306a36Sopenharmony_ci		regmap_write(priv->map, LPC_LADR12L, addrs[0] & 0xFF);
25362306a36Sopenharmony_ci		if (nr_addrs == 2) {
25462306a36Sopenharmony_ci			regmap_update_bits(priv->map, LPC_LSADR12, LPC_LSADR12_LSADR2_MASK,
25562306a36Sopenharmony_ci					   addrs[1] << LPC_LSADR12_LSADR2_SHIFT);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_EN16LADR2,
25862306a36Sopenharmony_ci					   LPC_HICRB_EN16LADR2);
25962306a36Sopenharmony_ci		}
26062306a36Sopenharmony_ci		break;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	case 3:
26362306a36Sopenharmony_ci		if (nr_addrs == 2) {
26462306a36Sopenharmony_ci			dev_err(priv->kcs_bmc.dev,
26562306a36Sopenharmony_ci				"Channel 3 only supports inferred status IO address\n");
26662306a36Sopenharmony_ci			return -EINVAL;
26762306a36Sopenharmony_ci		}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci		regmap_write(priv->map, LPC_LADR3H, addrs[0] >> 8);
27062306a36Sopenharmony_ci		regmap_write(priv->map, LPC_LADR3L, addrs[0] & 0xFF);
27162306a36Sopenharmony_ci		break;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	case 4:
27462306a36Sopenharmony_ci		if (nr_addrs == 1)
27562306a36Sopenharmony_ci			regmap_write(priv->map, LPC_LADR4, ((addrs[0] + 1) << 16) | addrs[0]);
27662306a36Sopenharmony_ci		else
27762306a36Sopenharmony_ci			regmap_write(priv->map, LPC_LADR4, (addrs[1] << 16) | addrs[0]);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		break;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	default:
28262306a36Sopenharmony_ci		return -EINVAL;
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	return 0;
28662306a36Sopenharmony_ci}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic inline int aspeed_kcs_map_serirq_type(u32 dt_type)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	switch (dt_type) {
29162306a36Sopenharmony_ci	case IRQ_TYPE_EDGE_RISING:
29262306a36Sopenharmony_ci		return LPC_TYIRQX_RISING;
29362306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_HIGH:
29462306a36Sopenharmony_ci		return LPC_TYIRQX_HIGH;
29562306a36Sopenharmony_ci	case IRQ_TYPE_LEVEL_LOW:
29662306a36Sopenharmony_ci		return LPC_TYIRQX_LOW;
29762306a36Sopenharmony_ci	default:
29862306a36Sopenharmony_ci		return -EINVAL;
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_cistatic int aspeed_kcs_config_upstream_irq(struct aspeed_kcs_bmc *priv, u32 id, u32 dt_type)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	unsigned int mask, val, hw_type;
30562306a36Sopenharmony_ci	int ret;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (id > 15)
30862306a36Sopenharmony_ci		return -EINVAL;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	ret = aspeed_kcs_map_serirq_type(dt_type);
31162306a36Sopenharmony_ci	if (ret < 0)
31262306a36Sopenharmony_ci		return ret;
31362306a36Sopenharmony_ci	hw_type = ret;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	priv->upstream_irq.mode = aspeed_kcs_irq_serirq;
31662306a36Sopenharmony_ci	priv->upstream_irq.id = id;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	switch (priv->kcs_bmc.channel) {
31962306a36Sopenharmony_ci	case 1:
32062306a36Sopenharmony_ci		/* Needs IRQxE1 rather than (ID1IRQX, SEL1IRQX, IRQXE1) before AST2600 A3 */
32162306a36Sopenharmony_ci		break;
32262306a36Sopenharmony_ci	case 2:
32362306a36Sopenharmony_ci		if (!(hw_type == LPC_TYIRQX_LOW || hw_type == LPC_TYIRQX_HIGH))
32462306a36Sopenharmony_ci			return -EINVAL;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		mask = LPC_HICR5_SEL2IRQX | LPC_HICR5_ID2IRQX_MASK;
32762306a36Sopenharmony_ci		val = (id << LPC_HICR5_ID2IRQX_SHIFT);
32862306a36Sopenharmony_ci		val |= (hw_type == LPC_TYIRQX_HIGH) ? LPC_HICR5_SEL2IRQX : 0;
32962306a36Sopenharmony_ci		regmap_update_bits(priv->map, LPC_HICR5, mask, val);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci		break;
33262306a36Sopenharmony_ci	case 3:
33362306a36Sopenharmony_ci		if (!(hw_type == LPC_TYIRQX_LOW || hw_type == LPC_TYIRQX_HIGH))
33462306a36Sopenharmony_ci			return -EINVAL;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		mask = LPC_HICR5_SEL3IRQX | LPC_HICR5_ID3IRQX_MASK;
33762306a36Sopenharmony_ci		val = (id << LPC_HICR5_ID3IRQX_SHIFT);
33862306a36Sopenharmony_ci		val |= (hw_type == LPC_TYIRQX_HIGH) ? LPC_HICR5_SEL3IRQX : 0;
33962306a36Sopenharmony_ci		regmap_update_bits(priv->map, LPC_HICR5, mask, val);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci		break;
34262306a36Sopenharmony_ci	case 4:
34362306a36Sopenharmony_ci		mask = LPC_HICRC_ID4IRQX_MASK | LPC_HICRC_TY4IRQX_MASK | LPC_HICRC_OBF4_AUTO_CLR;
34462306a36Sopenharmony_ci		val = (id << LPC_HICRC_ID4IRQX_SHIFT) | (hw_type << LPC_HICRC_TY4IRQX_SHIFT);
34562306a36Sopenharmony_ci		regmap_update_bits(priv->map, LPC_HICRC, mask, val);
34662306a36Sopenharmony_ci		break;
34762306a36Sopenharmony_ci	default:
34862306a36Sopenharmony_ci		dev_warn(priv->kcs_bmc.dev,
34962306a36Sopenharmony_ci			 "SerIRQ configuration not supported on KCS channel %d\n",
35062306a36Sopenharmony_ci			 priv->kcs_bmc.channel);
35162306a36Sopenharmony_ci		return -EINVAL;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	return 0;
35562306a36Sopenharmony_ci}
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_cistatic void aspeed_kcs_enable_channel(struct kcs_bmc_device *kcs_bmc, bool enable)
35862306a36Sopenharmony_ci{
35962306a36Sopenharmony_ci	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	switch (kcs_bmc->channel) {
36262306a36Sopenharmony_ci	case 1:
36362306a36Sopenharmony_ci		regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC1E, enable * LPC_HICR0_LPC1E);
36462306a36Sopenharmony_ci		return;
36562306a36Sopenharmony_ci	case 2:
36662306a36Sopenharmony_ci		regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC2E, enable * LPC_HICR0_LPC2E);
36762306a36Sopenharmony_ci		return;
36862306a36Sopenharmony_ci	case 3:
36962306a36Sopenharmony_ci		regmap_update_bits(priv->map, LPC_HICR0, LPC_HICR0_LPC3E, enable * LPC_HICR0_LPC3E);
37062306a36Sopenharmony_ci		regmap_update_bits(priv->map, LPC_HICR4,
37162306a36Sopenharmony_ci				   LPC_HICR4_KCSENBL, enable * LPC_HICR4_KCSENBL);
37262306a36Sopenharmony_ci		return;
37362306a36Sopenharmony_ci	case 4:
37462306a36Sopenharmony_ci		regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_LPC4E, enable * LPC_HICRB_LPC4E);
37562306a36Sopenharmony_ci		return;
37662306a36Sopenharmony_ci	default:
37762306a36Sopenharmony_ci		pr_warn("%s: Unsupported channel: %d", __func__, kcs_bmc->channel);
37862306a36Sopenharmony_ci		return;
37962306a36Sopenharmony_ci	}
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic void aspeed_kcs_check_obe(struct timer_list *timer)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	struct aspeed_kcs_bmc *priv = container_of(timer, struct aspeed_kcs_bmc, obe.timer);
38562306a36Sopenharmony_ci	unsigned long flags;
38662306a36Sopenharmony_ci	u8 str;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	spin_lock_irqsave(&priv->obe.lock, flags);
38962306a36Sopenharmony_ci	if (priv->obe.remove) {
39062306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->obe.lock, flags);
39162306a36Sopenharmony_ci		return;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	str = aspeed_kcs_inb(&priv->kcs_bmc, priv->kcs_bmc.ioreg.str);
39562306a36Sopenharmony_ci	if (str & KCS_BMC_STR_OBF) {
39662306a36Sopenharmony_ci		mod_timer(timer, jiffies + OBE_POLL_PERIOD);
39762306a36Sopenharmony_ci		spin_unlock_irqrestore(&priv->obe.lock, flags);
39862306a36Sopenharmony_ci		return;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci	spin_unlock_irqrestore(&priv->obe.lock, flags);
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	kcs_bmc_handle_event(&priv->kcs_bmc);
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic void aspeed_kcs_irq_mask_update(struct kcs_bmc_device *kcs_bmc, u8 mask, u8 state)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	struct aspeed_kcs_bmc *priv = to_aspeed_kcs_bmc(kcs_bmc);
40862306a36Sopenharmony_ci	int rc;
40962306a36Sopenharmony_ci	u8 str;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	/* We don't have an OBE IRQ, emulate it */
41262306a36Sopenharmony_ci	if (mask & KCS_BMC_EVENT_TYPE_OBE) {
41362306a36Sopenharmony_ci		if (KCS_BMC_EVENT_TYPE_OBE & state) {
41462306a36Sopenharmony_ci			/*
41562306a36Sopenharmony_ci			 * Given we don't have an OBE IRQ, delay by polling briefly to see if we can
41662306a36Sopenharmony_ci			 * observe such an event before returning to the caller. This is not
41762306a36Sopenharmony_ci			 * incorrect because OBF may have already become clear before enabling the
41862306a36Sopenharmony_ci			 * IRQ if we had one, under which circumstance no event will be propagated
41962306a36Sopenharmony_ci			 * anyway.
42062306a36Sopenharmony_ci			 *
42162306a36Sopenharmony_ci			 * The onus is on the client to perform a race-free check that it hasn't
42262306a36Sopenharmony_ci			 * missed the event.
42362306a36Sopenharmony_ci			 */
42462306a36Sopenharmony_ci			rc = read_poll_timeout_atomic(aspeed_kcs_inb, str,
42562306a36Sopenharmony_ci						      !(str & KCS_BMC_STR_OBF), 1, 100, false,
42662306a36Sopenharmony_ci						      &priv->kcs_bmc, priv->kcs_bmc.ioreg.str);
42762306a36Sopenharmony_ci			/* Time for the slow path? */
42862306a36Sopenharmony_ci			if (rc == -ETIMEDOUT)
42962306a36Sopenharmony_ci				mod_timer(&priv->obe.timer, jiffies + OBE_POLL_PERIOD);
43062306a36Sopenharmony_ci		} else {
43162306a36Sopenharmony_ci			del_timer(&priv->obe.timer);
43262306a36Sopenharmony_ci		}
43362306a36Sopenharmony_ci	}
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	if (mask & KCS_BMC_EVENT_TYPE_IBF) {
43662306a36Sopenharmony_ci		const bool enable = !!(state & KCS_BMC_EVENT_TYPE_IBF);
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		switch (kcs_bmc->channel) {
43962306a36Sopenharmony_ci		case 1:
44062306a36Sopenharmony_ci			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIE1,
44162306a36Sopenharmony_ci					   enable * LPC_HICR2_IBFIE1);
44262306a36Sopenharmony_ci			return;
44362306a36Sopenharmony_ci		case 2:
44462306a36Sopenharmony_ci			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIE2,
44562306a36Sopenharmony_ci					   enable * LPC_HICR2_IBFIE2);
44662306a36Sopenharmony_ci			return;
44762306a36Sopenharmony_ci		case 3:
44862306a36Sopenharmony_ci			regmap_update_bits(priv->map, LPC_HICR2, LPC_HICR2_IBFIE3,
44962306a36Sopenharmony_ci					   enable * LPC_HICR2_IBFIE3);
45062306a36Sopenharmony_ci			return;
45162306a36Sopenharmony_ci		case 4:
45262306a36Sopenharmony_ci			regmap_update_bits(priv->map, LPC_HICRB, LPC_HICRB_IBFIE4,
45362306a36Sopenharmony_ci					   enable * LPC_HICRB_IBFIE4);
45462306a36Sopenharmony_ci			return;
45562306a36Sopenharmony_ci		default:
45662306a36Sopenharmony_ci			pr_warn("%s: Unsupported channel: %d", __func__, kcs_bmc->channel);
45762306a36Sopenharmony_ci			return;
45862306a36Sopenharmony_ci		}
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic const struct kcs_bmc_device_ops aspeed_kcs_ops = {
46362306a36Sopenharmony_ci	.irq_mask_update = aspeed_kcs_irq_mask_update,
46462306a36Sopenharmony_ci	.io_inputb = aspeed_kcs_inb,
46562306a36Sopenharmony_ci	.io_outputb = aspeed_kcs_outb,
46662306a36Sopenharmony_ci	.io_updateb = aspeed_kcs_updateb,
46762306a36Sopenharmony_ci};
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic irqreturn_t aspeed_kcs_irq(int irq, void *arg)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	struct kcs_bmc_device *kcs_bmc = arg;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	return kcs_bmc_handle_event(kcs_bmc);
47462306a36Sopenharmony_ci}
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_cistatic int aspeed_kcs_config_downstream_irq(struct kcs_bmc_device *kcs_bmc,
47762306a36Sopenharmony_ci			struct platform_device *pdev)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
48062306a36Sopenharmony_ci	int irq;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
48362306a36Sopenharmony_ci	if (irq < 0)
48462306a36Sopenharmony_ci		return irq;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	return devm_request_irq(dev, irq, aspeed_kcs_irq, IRQF_SHARED,
48762306a36Sopenharmony_ci				dev_name(dev), kcs_bmc);
48862306a36Sopenharmony_ci}
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_cistatic const struct kcs_ioreg ast_kcs_bmc_ioregs[KCS_CHANNEL_MAX] = {
49162306a36Sopenharmony_ci	{ .idr = LPC_IDR1, .odr = LPC_ODR1, .str = LPC_STR1 },
49262306a36Sopenharmony_ci	{ .idr = LPC_IDR2, .odr = LPC_ODR2, .str = LPC_STR2 },
49362306a36Sopenharmony_ci	{ .idr = LPC_IDR3, .odr = LPC_ODR3, .str = LPC_STR3 },
49462306a36Sopenharmony_ci	{ .idr = LPC_IDR4, .odr = LPC_ODR4, .str = LPC_STR4 },
49562306a36Sopenharmony_ci};
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic int aspeed_kcs_of_get_channel(struct platform_device *pdev)
49862306a36Sopenharmony_ci{
49962306a36Sopenharmony_ci	struct device_node *np;
50062306a36Sopenharmony_ci	struct kcs_ioreg ioreg;
50162306a36Sopenharmony_ci	const __be32 *reg;
50262306a36Sopenharmony_ci	int i;
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	np = pdev->dev.of_node;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	/* Don't translate addresses, we want offsets for the regmaps */
50762306a36Sopenharmony_ci	reg = of_get_address(np, 0, NULL, NULL);
50862306a36Sopenharmony_ci	if (!reg)
50962306a36Sopenharmony_ci		return -EINVAL;
51062306a36Sopenharmony_ci	ioreg.idr = be32_to_cpup(reg);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	reg = of_get_address(np, 1, NULL, NULL);
51362306a36Sopenharmony_ci	if (!reg)
51462306a36Sopenharmony_ci		return -EINVAL;
51562306a36Sopenharmony_ci	ioreg.odr = be32_to_cpup(reg);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	reg = of_get_address(np, 2, NULL, NULL);
51862306a36Sopenharmony_ci	if (!reg)
51962306a36Sopenharmony_ci		return -EINVAL;
52062306a36Sopenharmony_ci	ioreg.str = be32_to_cpup(reg);
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ast_kcs_bmc_ioregs); i++) {
52362306a36Sopenharmony_ci		if (!memcmp(&ast_kcs_bmc_ioregs[i], &ioreg, sizeof(ioreg)))
52462306a36Sopenharmony_ci			return i + 1;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci	return -EINVAL;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic int
53062306a36Sopenharmony_ciaspeed_kcs_of_get_io_address(struct platform_device *pdev, u32 addrs[2])
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	int rc;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	rc = of_property_read_variable_u32_array(pdev->dev.of_node,
53562306a36Sopenharmony_ci						 "aspeed,lpc-io-reg",
53662306a36Sopenharmony_ci						 addrs, 1, 2);
53762306a36Sopenharmony_ci	if (rc < 0) {
53862306a36Sopenharmony_ci		dev_err(&pdev->dev, "No valid 'aspeed,lpc-io-reg' configured\n");
53962306a36Sopenharmony_ci		return rc;
54062306a36Sopenharmony_ci	}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (addrs[0] > 0xffff) {
54362306a36Sopenharmony_ci		dev_err(&pdev->dev, "Invalid data address in 'aspeed,lpc-io-reg'\n");
54462306a36Sopenharmony_ci		return -EINVAL;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (rc == 2 && addrs[1] > 0xffff) {
54862306a36Sopenharmony_ci		dev_err(&pdev->dev, "Invalid status address in 'aspeed,lpc-io-reg'\n");
54962306a36Sopenharmony_ci		return -EINVAL;
55062306a36Sopenharmony_ci	}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	return rc;
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic int aspeed_kcs_probe(struct platform_device *pdev)
55662306a36Sopenharmony_ci{
55762306a36Sopenharmony_ci	struct kcs_bmc_device *kcs_bmc;
55862306a36Sopenharmony_ci	struct aspeed_kcs_bmc *priv;
55962306a36Sopenharmony_ci	struct device_node *np;
56062306a36Sopenharmony_ci	bool have_upstream_irq;
56162306a36Sopenharmony_ci	u32 upstream_irq[2];
56262306a36Sopenharmony_ci	int rc, channel;
56362306a36Sopenharmony_ci	int nr_addrs;
56462306a36Sopenharmony_ci	u32 addrs[2];
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	np = pdev->dev.of_node->parent;
56762306a36Sopenharmony_ci	if (!of_device_is_compatible(np, "aspeed,ast2400-lpc-v2") &&
56862306a36Sopenharmony_ci	    !of_device_is_compatible(np, "aspeed,ast2500-lpc-v2") &&
56962306a36Sopenharmony_ci	    !of_device_is_compatible(np, "aspeed,ast2600-lpc-v2")) {
57062306a36Sopenharmony_ci		dev_err(&pdev->dev, "unsupported LPC device binding\n");
57162306a36Sopenharmony_ci		return -ENODEV;
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	channel = aspeed_kcs_of_get_channel(pdev);
57562306a36Sopenharmony_ci	if (channel < 0)
57662306a36Sopenharmony_ci		return channel;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci	nr_addrs = aspeed_kcs_of_get_io_address(pdev, addrs);
57962306a36Sopenharmony_ci	if (nr_addrs < 0)
58062306a36Sopenharmony_ci		return nr_addrs;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	np = pdev->dev.of_node;
58362306a36Sopenharmony_ci	rc = of_property_read_u32_array(np, "aspeed,lpc-interrupts", upstream_irq, 2);
58462306a36Sopenharmony_ci	if (rc && rc != -EINVAL)
58562306a36Sopenharmony_ci		return -EINVAL;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	have_upstream_irq = !rc;
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
59062306a36Sopenharmony_ci	if (!priv)
59162306a36Sopenharmony_ci		return -ENOMEM;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	kcs_bmc = &priv->kcs_bmc;
59462306a36Sopenharmony_ci	kcs_bmc->dev = &pdev->dev;
59562306a36Sopenharmony_ci	kcs_bmc->channel = channel;
59662306a36Sopenharmony_ci	kcs_bmc->ioreg = ast_kcs_bmc_ioregs[channel - 1];
59762306a36Sopenharmony_ci	kcs_bmc->ops = &aspeed_kcs_ops;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	priv->map = syscon_node_to_regmap(pdev->dev.parent->of_node);
60062306a36Sopenharmony_ci	if (IS_ERR(priv->map)) {
60162306a36Sopenharmony_ci		dev_err(&pdev->dev, "Couldn't get regmap\n");
60262306a36Sopenharmony_ci		return -ENODEV;
60362306a36Sopenharmony_ci	}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	spin_lock_init(&priv->obe.lock);
60662306a36Sopenharmony_ci	priv->obe.remove = false;
60762306a36Sopenharmony_ci	timer_setup(&priv->obe.timer, aspeed_kcs_check_obe, 0);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	rc = aspeed_kcs_set_address(kcs_bmc, addrs, nr_addrs);
61062306a36Sopenharmony_ci	if (rc)
61162306a36Sopenharmony_ci		return rc;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	/* Host to BMC IRQ */
61462306a36Sopenharmony_ci	rc = aspeed_kcs_config_downstream_irq(kcs_bmc, pdev);
61562306a36Sopenharmony_ci	if (rc)
61662306a36Sopenharmony_ci		return rc;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	/* BMC to Host IRQ */
61962306a36Sopenharmony_ci	if (have_upstream_irq) {
62062306a36Sopenharmony_ci		rc = aspeed_kcs_config_upstream_irq(priv, upstream_irq[0], upstream_irq[1]);
62162306a36Sopenharmony_ci		if (rc < 0)
62262306a36Sopenharmony_ci			return rc;
62362306a36Sopenharmony_ci	} else {
62462306a36Sopenharmony_ci		priv->upstream_irq.mode = aspeed_kcs_irq_none;
62562306a36Sopenharmony_ci	}
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	platform_set_drvdata(pdev, priv);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
63062306a36Sopenharmony_ci	aspeed_kcs_enable_channel(kcs_bmc, true);
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	rc = kcs_bmc_add_device(&priv->kcs_bmc);
63362306a36Sopenharmony_ci	if (rc) {
63462306a36Sopenharmony_ci		dev_warn(&pdev->dev, "Failed to register channel %d: %d\n", kcs_bmc->channel, rc);
63562306a36Sopenharmony_ci		return rc;
63662306a36Sopenharmony_ci	}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	dev_info(&pdev->dev, "Initialised channel %d at 0x%x\n",
63962306a36Sopenharmony_ci			kcs_bmc->channel, addrs[0]);
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	return 0;
64262306a36Sopenharmony_ci}
64362306a36Sopenharmony_ci
64462306a36Sopenharmony_cistatic int aspeed_kcs_remove(struct platform_device *pdev)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	struct aspeed_kcs_bmc *priv = platform_get_drvdata(pdev);
64762306a36Sopenharmony_ci	struct kcs_bmc_device *kcs_bmc = &priv->kcs_bmc;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	kcs_bmc_remove_device(kcs_bmc);
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	aspeed_kcs_enable_channel(kcs_bmc, false);
65262306a36Sopenharmony_ci	aspeed_kcs_irq_mask_update(kcs_bmc, (KCS_BMC_EVENT_TYPE_IBF | KCS_BMC_EVENT_TYPE_OBE), 0);
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	/* Make sure it's proper dead */
65562306a36Sopenharmony_ci	spin_lock_irq(&priv->obe.lock);
65662306a36Sopenharmony_ci	priv->obe.remove = true;
65762306a36Sopenharmony_ci	spin_unlock_irq(&priv->obe.lock);
65862306a36Sopenharmony_ci	del_timer_sync(&priv->obe.timer);
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci	return 0;
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic const struct of_device_id ast_kcs_bmc_match[] = {
66462306a36Sopenharmony_ci	{ .compatible = "aspeed,ast2400-kcs-bmc-v2" },
66562306a36Sopenharmony_ci	{ .compatible = "aspeed,ast2500-kcs-bmc-v2" },
66662306a36Sopenharmony_ci	{ .compatible = "aspeed,ast2600-kcs-bmc" },
66762306a36Sopenharmony_ci	{ }
66862306a36Sopenharmony_ci};
66962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ast_kcs_bmc_match);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_cistatic struct platform_driver ast_kcs_bmc_driver = {
67262306a36Sopenharmony_ci	.driver = {
67362306a36Sopenharmony_ci		.name           = DEVICE_NAME,
67462306a36Sopenharmony_ci		.of_match_table = ast_kcs_bmc_match,
67562306a36Sopenharmony_ci	},
67662306a36Sopenharmony_ci	.probe  = aspeed_kcs_probe,
67762306a36Sopenharmony_ci	.remove = aspeed_kcs_remove,
67862306a36Sopenharmony_ci};
67962306a36Sopenharmony_cimodule_platform_driver(ast_kcs_bmc_driver);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
68262306a36Sopenharmony_ciMODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
68362306a36Sopenharmony_ciMODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
68462306a36Sopenharmony_ciMODULE_DESCRIPTION("Aspeed device interface to the KCS BMC device");
685