aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHaiyue Wang <haiyue.wang@linux.intel.com>2018-03-22 08:50:15 -0400
committerCorey Minyard <cminyard@mvista.com>2018-04-18 11:23:12 -0400
commit6b2e54f7babea6c782a9c1af9d01c7f999bb4c7d (patch)
tree095e21537c939f7e40053858fd19cff545fdb18a
parent93c303d2045b30572d8d5e74d3ad80692acfebbe (diff)
ipmi: add an NPCM7xx KCS BMC driver
This driver exposes the Keyboard Controller Style (KCS) interface on Novoton NPCM7xx SoCs as a character device. Such SOCs are commonly used as a BaseBoard Management Controller (BMC) on a server board, and KCS interface is commonly used to perform the in-band IPMI communication between the server and its BMC. Signed-off-by: Avi Fishman <avifishman70@gmail.com> Signed-off-by: Haiyue Wang <haiyue.wang@linux.intel.com> Signed-off-by: Corey Minyard <cminyard@mvista.com>
-rw-r--r--Documentation/devicetree/bindings/ipmi/npcm7xx-kcs-bmc.txt39
-rw-r--r--drivers/char/ipmi/Kconfig15
-rw-r--r--drivers/char/ipmi/Makefile1
-rw-r--r--drivers/char/ipmi/kcs_bmc_npcm7xx.c204
4 files changed, 259 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/ipmi/npcm7xx-kcs-bmc.txt b/Documentation/devicetree/bindings/ipmi/npcm7xx-kcs-bmc.txt
new file mode 100644
index 000000000000..3538a214fff1
--- /dev/null
+++ b/Documentation/devicetree/bindings/ipmi/npcm7xx-kcs-bmc.txt
@@ -0,0 +1,39 @@
1* Nuvoton NPCM7xx KCS (Keyboard Controller Style) IPMI interface
2
3The Nuvoton SOCs (NPCM7xx) are commonly used as BMCs
4(Baseboard Management Controllers) and the KCS interface can be
5used to perform in-band IPMI communication with their host.
6
7Required properties:
8- compatible : should be one of
9 "nuvoton,npcm750-kcs-bmc"
10- interrupts : interrupt generated by the controller
11- kcs_chan : The KCS channel number in the controller
12
13Example:
14
15 lpc_kcs: lpc_kcs@f0007000 {
16 compatible = "nuvoton,npcm750-lpc-kcs", "simple-mfd", "syscon";
17 reg = <0xf0007000 0x40>;
18 reg-io-width = <1>;
19
20 #address-cells = <1>;
21 #size-cells = <1>;
22 ranges = <0x0 0xf0007000 0x40>;
23
24 kcs1: kcs1@0 {
25 compatible = "nuvoton,npcm750-kcs-bmc";
26 reg = <0x0 0x40>;
27 interrupts = <0 9 4>;
28 kcs_chan = <1>;
29 status = "disabled";
30 };
31
32 kcs2: kcs2@0 {
33 compatible = "nuvoton,npcm750-kcs-bmc";
34 reg = <0x0 0x40>;
35 interrupts = <0 9 4>;
36 kcs_chan = <2>;
37 status = "disabled";
38 };
39 }; \ No newline at end of file
diff --git a/drivers/char/ipmi/Kconfig b/drivers/char/ipmi/Kconfig
index 3bda116c8aa0..470f976cf850 100644
--- a/drivers/char/ipmi/Kconfig
+++ b/drivers/char/ipmi/Kconfig
@@ -111,6 +111,21 @@ config ASPEED_KCS_IPMI_BMC
111 The driver implements the BMC side of the KCS contorller, it 111 The driver implements the BMC side of the KCS contorller, it
112 provides the access of KCS IO space for BMC side. 112 provides the access of KCS IO space for BMC side.
113 113
114config NPCM7XX_KCS_IPMI_BMC
115 depends on ARCH_NPCM7XX || COMPILE_TEST
116 select IPMI_KCS_BMC
117 select REGMAP_MMIO
118 tristate "NPCM7xx KCS IPMI BMC driver"
119 help
120 Provides a driver for the KCS (Keyboard Controller Style) IPMI
121 interface found on Nuvoton NPCM7xx SOCs.
122
123 The driver implements the BMC side of the KCS contorller, it
124 provides the access of KCS IO space for BMC side.
125
126 This support is also available as a module. If so, the module
127 will be called kcs_bmc_npcm7xx.
128
114config ASPEED_BT_IPMI_BMC 129config ASPEED_BT_IPMI_BMC
115 depends on ARCH_ASPEED || COMPILE_TEST 130 depends on ARCH_ASPEED || COMPILE_TEST
116 depends on REGMAP && REGMAP_MMIO && MFD_SYSCON 131 depends on REGMAP && REGMAP_MMIO && MFD_SYSCON
diff --git a/drivers/char/ipmi/Makefile b/drivers/char/ipmi/Makefile
index 21e9e872d973..7a3baf301a8f 100644
--- a/drivers/char/ipmi/Makefile
+++ b/drivers/char/ipmi/Makefile
@@ -24,3 +24,4 @@ obj-$(CONFIG_IPMI_POWEROFF) += ipmi_poweroff.o
24obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o 24obj-$(CONFIG_IPMI_KCS_BMC) += kcs_bmc.o
25obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o 25obj-$(CONFIG_ASPEED_BT_IPMI_BMC) += bt-bmc.o
26obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o 26obj-$(CONFIG_ASPEED_KCS_IPMI_BMC) += kcs_bmc_aspeed.o
27obj-$(CONFIG_NPCM7XX_KCS_IPMI_BMC) += kcs_bmc_npcm7xx.o
diff --git a/drivers/char/ipmi/kcs_bmc_npcm7xx.c b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
new file mode 100644
index 000000000000..7bc898c5d372
--- /dev/null
+++ b/drivers/char/ipmi/kcs_bmc_npcm7xx.c
@@ -0,0 +1,204 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2018, Nuvoton Corporation.
4 * Copyright (c) 2018, Intel Corporation.
5 */
6
7#define pr_fmt(fmt) "nuvoton-kcs-bmc: " fmt
8
9#include <linux/atomic.h>
10#include <linux/errno.h>
11#include <linux/interrupt.h>
12#include <linux/io.h>
13#include <linux/mfd/syscon.h>
14#include <linux/module.h>
15#include <linux/of.h>
16#include <linux/platform_device.h>
17#include <linux/regmap.h>
18#include <linux/slab.h>
19
20#include "kcs_bmc.h"
21
22#define DEVICE_NAME "npcm-kcs-bmc"
23#define KCS_CHANNEL_MAX 3
24
25#define KCS1ST 0x0C
26#define KCS2ST 0x1E
27#define KCS3ST 0x30
28
29#define KCS1DO 0x0E
30#define KCS2DO 0x20
31#define KCS3DO 0x32
32
33#define KCS1DI 0x10
34#define KCS2DI 0x22
35#define KCS3DI 0x34
36
37#define KCS1CTL 0x18
38#define KCS2CTL 0x2A
39#define KCS3CTL 0x3C
40#define KCS_CTL_IBFIE BIT(0)
41
42/*
43 * 7.2.4 Core KCS Registers
44 * Registers in this module are 8 bits. An 8-bit register must be accessed
45 * by an 8-bit read or write.
46 *
47 * sts: KCS Channel n Status Register (KCSnST).
48 * dob: KCS Channel n Data Out Buffer Register (KCSnDO).
49 * dib: KCS Channel n Data In Buffer Register (KCSnDI).
50 * ctl: KCS Channel n Control Register (KCSnCTL).
51 */
52struct npcm7xx_kcs_reg {
53 u32 sts;
54 u32 dob;
55 u32 dib;
56 u32 ctl;
57};
58
59struct npcm7xx_kcs_bmc {
60 struct regmap *map;
61
62 const struct npcm7xx_kcs_reg *reg;
63};
64
65static const struct npcm7xx_kcs_reg npcm7xx_kcs_reg_tbl[KCS_CHANNEL_MAX] = {
66 { .sts = KCS1ST, .dob = KCS1DO, .dib = KCS1DI, .ctl = KCS1CTL },
67 { .sts = KCS2ST, .dob = KCS2DO, .dib = KCS2DI, .ctl = KCS2CTL },
68 { .sts = KCS3ST, .dob = KCS3DO, .dib = KCS3DI, .ctl = KCS3CTL },
69};
70
71static u8 npcm7xx_kcs_inb(struct kcs_bmc *kcs_bmc, u32 reg)
72{
73 struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
74 u32 val = 0;
75 int rc;
76
77 rc = regmap_read(priv->map, reg, &val);
78 WARN(rc != 0, "regmap_read() failed: %d\n", rc);
79
80 return rc == 0 ? (u8)val : 0;
81}
82
83static void npcm7xx_kcs_outb(struct kcs_bmc *kcs_bmc, u32 reg, u8 data)
84{
85 struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
86 int rc;
87
88 rc = regmap_write(priv->map, reg, data);
89 WARN(rc != 0, "regmap_write() failed: %d\n", rc);
90}
91
92static void npcm7xx_kcs_enable_channel(struct kcs_bmc *kcs_bmc, bool enable)
93{
94 struct npcm7xx_kcs_bmc *priv = kcs_bmc_priv(kcs_bmc);
95
96 regmap_update_bits(priv->map, priv->reg->ctl, KCS_CTL_IBFIE,
97 enable ? KCS_CTL_IBFIE : 0);
98}
99
100static irqreturn_t npcm7xx_kcs_irq(int irq, void *arg)
101{
102 struct kcs_bmc *kcs_bmc = arg;
103
104 if (!kcs_bmc_handle_event(kcs_bmc))
105 return IRQ_HANDLED;
106
107 return IRQ_NONE;
108}
109
110static int npcm7xx_kcs_config_irq(struct kcs_bmc *kcs_bmc,
111 struct platform_device *pdev)
112{
113 struct device *dev = &pdev->dev;
114 int irq;
115
116 irq = platform_get_irq(pdev, 0);
117 if (irq < 0)
118 return irq;
119
120 return devm_request_irq(dev, irq, npcm7xx_kcs_irq, IRQF_SHARED,
121 dev_name(dev), kcs_bmc);
122}
123
124static int npcm7xx_kcs_probe(struct platform_device *pdev)
125{
126 struct device *dev = &pdev->dev;
127 struct npcm7xx_kcs_bmc *priv;
128 struct kcs_bmc *kcs_bmc;
129 u32 chan;
130 int rc;
131
132 rc = of_property_read_u32(dev->of_node, "kcs_chan", &chan);
133 if (rc != 0 || chan == 0 || chan > KCS_CHANNEL_MAX) {
134 dev_err(dev, "no valid 'kcs_chan' configured\n");
135 return -ENODEV;
136 }
137
138 kcs_bmc = kcs_bmc_alloc(dev, sizeof(*priv), chan);
139 if (!kcs_bmc)
140 return -ENOMEM;
141
142 priv = kcs_bmc_priv(kcs_bmc);
143 priv->map = syscon_node_to_regmap(dev->parent->of_node);
144 if (IS_ERR(priv->map)) {
145 dev_err(dev, "Couldn't get regmap\n");
146 return -ENODEV;
147 }
148 priv->reg = &npcm7xx_kcs_reg_tbl[chan - 1];
149
150 kcs_bmc->ioreg.idr = priv->reg->dib;
151 kcs_bmc->ioreg.odr = priv->reg->dob;
152 kcs_bmc->ioreg.str = priv->reg->sts;
153 kcs_bmc->io_inputb = npcm7xx_kcs_inb;
154 kcs_bmc->io_outputb = npcm7xx_kcs_outb;
155
156 dev_set_drvdata(dev, kcs_bmc);
157
158 npcm7xx_kcs_enable_channel(kcs_bmc, true);
159 rc = npcm7xx_kcs_config_irq(kcs_bmc, pdev);
160 if (rc)
161 return rc;
162
163 rc = misc_register(&kcs_bmc->miscdev);
164 if (rc) {
165 dev_err(dev, "Unable to register device\n");
166 return rc;
167 }
168
169 pr_info("channel=%u idr=0x%x odr=0x%x str=0x%x\n",
170 chan,
171 kcs_bmc->ioreg.idr, kcs_bmc->ioreg.odr, kcs_bmc->ioreg.str);
172
173 return 0;
174}
175
176static int npcm7xx_kcs_remove(struct platform_device *pdev)
177{
178 struct kcs_bmc *kcs_bmc = dev_get_drvdata(&pdev->dev);
179
180 misc_deregister(&kcs_bmc->miscdev);
181
182 return 0;
183}
184
185static const struct of_device_id npcm_kcs_bmc_match[] = {
186 { .compatible = "nuvoton,npcm750-kcs-bmc" },
187 { }
188};
189MODULE_DEVICE_TABLE(of, npcm_kcs_bmc_match);
190
191static struct platform_driver npcm_kcs_bmc_driver = {
192 .driver = {
193 .name = DEVICE_NAME,
194 .of_match_table = npcm_kcs_bmc_match,
195 },
196 .probe = npcm7xx_kcs_probe,
197 .remove = npcm7xx_kcs_remove,
198};
199module_platform_driver(npcm_kcs_bmc_driver);
200
201MODULE_LICENSE("GPL v2");
202MODULE_AUTHOR("Avi Fishman <avifishman70@gmail.com>");
203MODULE_AUTHOR("Haiyue Wang <haiyue.wang@linux.intel.com>");
204MODULE_DESCRIPTION("NPCM7xx device interface to the KCS BMC device");