Skip to content

Commit 836c6f7

Browse files
committed
misc: npcm7xx-lpc-bpc: add NPCM7xx BIOS post code driver
Add NPCM7xx BIOS post code (BPC) driver, the BPC monitoring two I/O address written by the host on the LPC. Signed-off-by: Tomer Maimon <[email protected]>
1 parent 1148396 commit 836c6f7

File tree

3 files changed

+355
-0
lines changed

3 files changed

+355
-0
lines changed

drivers/misc/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,14 @@ config ASPEED_LPC_SNOOP
498498
allows the BMC to listen on and save the data written by
499499
the host to an arbitrary LPC I/O port.
500500

501+
config NPCM7XX_LPC_BPC
502+
tristate "NPCM7xx LPC BIOS Post Code support"
503+
depends on (ARCH_NPCM7XX || COMPILE_TEST)
504+
help
505+
Provides a NPCM7xx driver to control the LPC BIOS Post Code
506+
interface which allows the BMC to listen on and save
507+
the data written by the host to an arbitrary LPC I/O port.
508+
501509
config PCI_ENDPOINT_TEST
502510
depends on PCI
503511
select CRC32

drivers/misc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
5454
obj-$(CONFIG_CXL_BASE) += cxl/
5555
obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o
5656
obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
57+
obj-$(CONFIG_NPCM7XX_LPC_BPC) += npcm7xx-lpc-bpc.o
5758
obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
5859

5960
lkdtm-$(CONFIG_LKDTM) += lkdtm_core.o

drivers/misc/npcm7xx-lpc-bpc.c

Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
// Copyright (c) 2014-2018 Nuvoton Technology corporation.
3+
4+
#include <linux/fs.h>
5+
#include <linux/bitops.h>
6+
#include <linux/interrupt.h>
7+
#include <linux/kfifo.h>
8+
#include <linux/mfd/syscon.h>
9+
#include <linux/module.h>
10+
#include <linux/of.h>
11+
#include <linux/platform_device.h>
12+
#include <linux/regmap.h>
13+
#include <linux/miscdevice.h>
14+
#include <linux/poll.h>
15+
16+
#define DEVICE_NAME "npcm7xx-lpc-bpc"
17+
18+
#define NUM_BPC_CHANNELS 2
19+
20+
/* BIOS POST Code FIFO Registers */
21+
#define NPCM7XX_BPCFA2L_REG 0x2 //BIOS POST Code FIFO Address 2 LSB
22+
#define NPCM7XX_BPCFA2M_REG 0x4 //BIOS POST Code FIFO Address 2 MSB
23+
#define NPCM7XX_BPCFEN_REG 0x6 //BIOS POST Code FIFO Enable
24+
#define NPCM7XX_BPCFSTAT_REG 0x8 //BIOS POST Code FIFO Status
25+
#define NPCM7XX_BPCFDATA_REG 0xA //BIOS POST Code FIFO Data
26+
#define NPCM7XX_BPCFMSTAT_REG 0xC //BIOS POST Code FIFO Miscellaneous Status
27+
#define NPCM7XX_BPCFA1L_REG 0x10 //BIOS POST Code FIFO Address 1 LSB
28+
#define NPCM7XX_BPCFA1M_REG 0x12 //BIOS POST Code FIFO Address 1 MSB
29+
30+
/*BIOS regiser data*/
31+
#define FIFO_IOADDR1_ENABLE 0x80
32+
#define FIFO_IOADDR2_ENABLE 0x40
33+
34+
/* BPC interface package and structure definition */
35+
#define BPC_KFIFO_SIZE 0x400
36+
37+
/*BPC regiser data*/
38+
#define FIFO_DATA_VALID 0x80
39+
#define FIFO_OVERFLOW 0x20
40+
#define FIFO_READY_INT_ENABLE 0x8
41+
#define FIFO_DWCAPTURE 0x4
42+
#define FIFO_ADDR_DECODE 0x1
43+
44+
struct npcm7xx_bpc_channel {
45+
struct npcm7xx_bpc *data;
46+
struct kfifo fifo;
47+
wait_queue_head_t wq;
48+
struct miscdevice miscdev;
49+
};
50+
51+
struct npcm7xx_bpc {
52+
void __iomem *base;
53+
int irq;
54+
bool en_dwcap;
55+
struct npcm7xx_bpc_channel ch[NUM_BPC_CHANNELS];
56+
};
57+
58+
static struct npcm7xx_bpc_channel *npcm7xx_file_to_ch(struct file *file)
59+
{
60+
return container_of(file->private_data,
61+
struct npcm7xx_bpc_channel,
62+
miscdev);
63+
}
64+
65+
static ssize_t npcm7xx_bpc_read(struct file *file, char __user *buffer,
66+
size_t count, loff_t *ppos)
67+
{
68+
struct npcm7xx_bpc_channel *chan = npcm7xx_file_to_ch(file);
69+
struct npcm7xx_bpc *lpc_bpc = chan->data;
70+
unsigned int copied;
71+
int ret = 0;
72+
int cond_size = 1;
73+
74+
if (lpc_bpc->en_dwcap)
75+
cond_size = 3;
76+
77+
if (kfifo_len(&chan->fifo) < cond_size) {
78+
if (file->f_flags & O_NONBLOCK)
79+
return -EAGAIN;
80+
81+
ret = wait_event_interruptible
82+
(chan->wq, kfifo_len(&chan->fifo) > cond_size);
83+
if (ret == -ERESTARTSYS)
84+
return -EINTR;
85+
}
86+
87+
ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
88+
89+
return ret ? ret : copied;
90+
}
91+
92+
static unsigned int npcm7xx_bpc_poll(struct file *file,
93+
struct poll_table_struct *pt)
94+
{
95+
struct npcm7xx_bpc_channel *chan = npcm7xx_file_to_ch(file);
96+
97+
poll_wait(file, &chan->wq, pt);
98+
return !kfifo_is_empty(&chan->fifo) ? POLLIN : 0;
99+
}
100+
101+
static const struct file_operations npcm7xx_bpc_fops = {
102+
.owner = THIS_MODULE,
103+
.read = npcm7xx_bpc_read,
104+
.poll = npcm7xx_bpc_poll,
105+
.llseek = noop_llseek,
106+
};
107+
108+
static irqreturn_t npcm7xx_bpc_irq(int irq, void *arg)
109+
{
110+
struct npcm7xx_bpc *lpc_bpc = arg;
111+
u8 fifo_st;
112+
u8 addr_index = 0;
113+
u8 Data;
114+
bool ISRFlag = false;
115+
116+
fifo_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFSTAT_REG);
117+
while(FIFO_DATA_VALID & fifo_st)
118+
{
119+
/* If dwcapture enabled only channel 0 (FIFO 0) used */
120+
if (!lpc_bpc->en_dwcap)
121+
addr_index = fifo_st & FIFO_ADDR_DECODE;
122+
123+
/*Read data from FIFO to clear interrupt*/
124+
Data = ioread8(lpc_bpc->base + NPCM7XX_BPCFDATA_REG);
125+
126+
if (kfifo_is_full(&lpc_bpc->ch[addr_index].fifo))
127+
kfifo_skip(&lpc_bpc->ch[addr_index].fifo);
128+
kfifo_put(&lpc_bpc->ch[addr_index].fifo, Data);
129+
wake_up_interruptible(&lpc_bpc->ch[addr_index].wq);
130+
if(fifo_st & FIFO_OVERFLOW)
131+
pr_info("BIOS Post Codes FIFO Overflow!!!\n");
132+
133+
fifo_st = ioread8(lpc_bpc->base + NPCM7XX_BPCFSTAT_REG);
134+
ISRFlag = true;
135+
}
136+
137+
if(ISRFlag)
138+
return IRQ_HANDLED;
139+
140+
return IRQ_NONE;
141+
}
142+
143+
static int npcm7xx_bpc_config_irq(struct npcm7xx_bpc *lpc_bpc,
144+
struct platform_device *pdev)
145+
{
146+
struct device *dev = &pdev->dev;
147+
int rc;
148+
149+
lpc_bpc->irq = platform_get_irq(pdev, 0);
150+
if (!lpc_bpc->irq)
151+
return -ENODEV;
152+
153+
rc = devm_request_irq(dev, lpc_bpc->irq,
154+
npcm7xx_bpc_irq, IRQF_SHARED,
155+
DEVICE_NAME, lpc_bpc);
156+
if (rc < 0) {
157+
dev_warn(dev, "Unable to request IRQ %d\n", lpc_bpc->irq);
158+
lpc_bpc->irq = 0;
159+
return rc;
160+
}
161+
162+
return 0;
163+
}
164+
165+
static int npcm7xx_enable_bpc(struct npcm7xx_bpc *lpc_bpc, struct device *dev,
166+
int channel, u16 lpc_port)
167+
{
168+
int rc = 0;
169+
u8 Addr_en, reg_en;
170+
171+
init_waitqueue_head(&lpc_bpc->ch[channel].wq);
172+
173+
rc = kfifo_alloc(&lpc_bpc->ch[channel].fifo,
174+
BPC_KFIFO_SIZE, GFP_KERNEL);
175+
if (rc)
176+
return rc;
177+
178+
lpc_bpc->ch[channel].miscdev.minor = MISC_DYNAMIC_MINOR;
179+
lpc_bpc->ch[channel].miscdev.name =
180+
devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel);
181+
lpc_bpc->ch[channel].miscdev.fops = &npcm7xx_bpc_fops;
182+
lpc_bpc->ch[channel].miscdev.parent = dev;
183+
rc = misc_register(&lpc_bpc->ch[channel].miscdev);
184+
if (rc)
185+
return rc;
186+
187+
lpc_bpc->ch[channel].data = lpc_bpc;
188+
189+
/* Enable LPC snoop channel at requested port */
190+
switch (channel) {
191+
case 0:
192+
Addr_en = FIFO_IOADDR1_ENABLE;
193+
iowrite8((u8)lpc_port & 0xFF,
194+
lpc_bpc->base + NPCM7XX_BPCFA1L_REG);
195+
iowrite8((u8)lpc_port >> 8,
196+
lpc_bpc->base + NPCM7XX_BPCFA1M_REG);
197+
break;
198+
case 1:
199+
Addr_en = FIFO_IOADDR2_ENABLE;
200+
iowrite8((u8)lpc_port & 0xFF,
201+
lpc_bpc->base + NPCM7XX_BPCFA2L_REG);
202+
iowrite8((u8)lpc_port >> 8,
203+
lpc_bpc->base + NPCM7XX_BPCFA2M_REG);
204+
break;
205+
default:
206+
return -EINVAL;
207+
}
208+
209+
if (lpc_bpc->en_dwcap)
210+
Addr_en = FIFO_DWCAPTURE;
211+
212+
/* Enable FIFO Ready Interrupt and FIFO Capture of I/O address */
213+
reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
214+
iowrite8(reg_en | Addr_en | FIFO_READY_INT_ENABLE,
215+
lpc_bpc->base + NPCM7XX_BPCFEN_REG);
216+
217+
return rc;
218+
}
219+
220+
static void npcm7xx_disable_bpc(struct npcm7xx_bpc *lpc_bpc, int channel)
221+
{
222+
u8 reg_en;
223+
224+
switch (channel) {
225+
case 0:
226+
reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
227+
if (lpc_bpc->en_dwcap)
228+
iowrite8(reg_en & ~FIFO_DWCAPTURE,
229+
lpc_bpc->base + NPCM7XX_BPCFEN_REG);
230+
else
231+
iowrite8(reg_en & ~FIFO_IOADDR1_ENABLE,
232+
lpc_bpc->base + NPCM7XX_BPCFEN_REG);
233+
break;
234+
case 1:
235+
reg_en = ioread8(lpc_bpc->base + NPCM7XX_BPCFEN_REG);
236+
iowrite8(reg_en & ~FIFO_IOADDR2_ENABLE,
237+
lpc_bpc->base + NPCM7XX_BPCFEN_REG);
238+
break;
239+
default:
240+
return;
241+
}
242+
243+
if (!(reg_en & (FIFO_IOADDR1_ENABLE | FIFO_IOADDR2_ENABLE)))
244+
iowrite8(reg_en & ~FIFO_READY_INT_ENABLE,
245+
lpc_bpc->base + NPCM7XX_BPCFEN_REG);
246+
247+
kfifo_free(&lpc_bpc->ch[channel].fifo);
248+
misc_deregister(&lpc_bpc->ch[channel].miscdev);
249+
}
250+
251+
static int npcm7xx_bpc_probe(struct platform_device *pdev)
252+
{
253+
struct npcm7xx_bpc *lpc_bpc;
254+
struct resource *res;
255+
struct device *dev;
256+
u32 port;
257+
int rc;
258+
259+
dev = &pdev->dev;
260+
261+
lpc_bpc = devm_kzalloc(dev, sizeof(*lpc_bpc), GFP_KERNEL);
262+
if (!lpc_bpc)
263+
return -ENOMEM;
264+
265+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
266+
if (!res) {
267+
dev_err(dev, "BIOS Post Code of_address_to_resource fail\n\n\n\n\n\n");
268+
return -ENODEV;
269+
}
270+
271+
dev_dbg(dev, "BIOS Post Code base resource is %pR\n", res);
272+
273+
lpc_bpc->base = devm_ioremap_resource(dev, res);
274+
if (IS_ERR(lpc_bpc->base)) {
275+
dev_err(dev, "BIOS Post Code probe failed: can't read pwm base address\n");
276+
return PTR_ERR(lpc_bpc->base);
277+
}
278+
279+
dev_set_drvdata(&pdev->dev, lpc_bpc);
280+
281+
rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port);
282+
if (rc) {
283+
dev_err(dev, "no snoop ports configured\n");
284+
return -ENODEV;
285+
}
286+
287+
lpc_bpc->en_dwcap =
288+
of_property_read_bool(dev->of_node, "bpc-en-dwcapture");
289+
290+
rc = npcm7xx_bpc_config_irq(lpc_bpc, pdev);
291+
if (rc)
292+
return rc;
293+
294+
rc = npcm7xx_enable_bpc(lpc_bpc, dev, 0, port);
295+
if (rc)
296+
return rc;
297+
298+
/*
299+
* Configuration of second BPC channel port is optional
300+
* Double-Word Capture ignoring address 2
301+
*/
302+
if (!lpc_bpc->en_dwcap) {
303+
if (of_property_read_u32_index(dev->of_node, "snoop-ports",
304+
1, &port) == 0) {
305+
pr_info("probe 1\n");
306+
rc = npcm7xx_enable_bpc(lpc_bpc, dev, 1, port);
307+
if (rc)
308+
npcm7xx_disable_bpc(lpc_bpc, 0);
309+
}
310+
}
311+
312+
pr_info("npcm7xx BIOS post code probe\n");
313+
314+
return rc;
315+
}
316+
317+
static int npcm7xx_bpc_remove(struct platform_device *pdev)
318+
{
319+
struct npcm7xx_bpc *lpc_bpc = dev_get_drvdata(&pdev->dev);
320+
321+
npcm7xx_disable_bpc(lpc_bpc, 0);
322+
npcm7xx_disable_bpc(lpc_bpc, 1);
323+
324+
return 0;
325+
}
326+
327+
static const struct of_device_id npcm7xx_bpc_match[] = {
328+
{ .compatible = "nuvoton,npcm7xx-lpc-bpc" },
329+
{ },
330+
};
331+
332+
static struct platform_driver npcm7xx_bpc_driver = {
333+
.driver = {
334+
.name = DEVICE_NAME,
335+
.of_match_table = npcm7xx_bpc_match,
336+
},
337+
.probe = npcm7xx_bpc_probe,
338+
.remove = npcm7xx_bpc_remove,
339+
};
340+
341+
module_platform_driver(npcm7xx_bpc_driver);
342+
343+
MODULE_DEVICE_TABLE(of, npcm7xx_bpc_match);
344+
MODULE_LICENSE("GPL");
345+
MODULE_AUTHOR("Tomer Maimon <[email protected]>");
346+
MODULE_DESCRIPTION("Linux driver to control NPCM7XX LPC BIOS post code monitoring");

0 commit comments

Comments
 (0)