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