Skip to content

Commit 3772e5d

Browse files
rlippertgregkh
authored andcommitted
drivers/misc: Aspeed LPC snoop output using misc chardev
Provides the data bytes snooped over the LPC snoop bus to userspace as a (blocking) misc character device. Bytes output from the host using LPC I/O transactions to the snooped port can be watched or retrieved from the character device using a simple command like this: ~# od -w1 -A n -t x1 /dev/aspeed-lpc-snoop0 10 de ad c0 ff ee Signed-off-by: Robert Lippert <rlippert@google.com> Signed-off-by: Patrick Venture <venture@google.com> Signed-off-by: Benjamin Fair <benjaminfair@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 7f3838f commit 3772e5d

File tree

1 file changed

+74
-10
lines changed

1 file changed

+74
-10
lines changed

drivers/misc/aspeed-lpc-snoop.c

Lines changed: 74 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616

1717
#include <linux/bitops.h>
1818
#include <linux/interrupt.h>
19+
#include <linux/fs.h>
1920
#include <linux/kfifo.h>
2021
#include <linux/mfd/syscon.h>
22+
#include <linux/miscdevice.h>
2123
#include <linux/module.h>
2224
#include <linux/of.h>
2325
#include <linux/of_device.h>
2426
#include <linux/platform_device.h>
27+
#include <linux/poll.h>
2528
#include <linux/regmap.h>
2629

2730
#define DEVICE_NAME "aspeed-lpc-snoop"
@@ -59,20 +62,70 @@ struct aspeed_lpc_snoop_model_data {
5962
unsigned int has_hicrb_ensnp;
6063
};
6164

65+
struct aspeed_lpc_snoop_channel {
66+
struct kfifo fifo;
67+
wait_queue_head_t wq;
68+
struct miscdevice miscdev;
69+
};
70+
6271
struct aspeed_lpc_snoop {
6372
struct regmap *regmap;
6473
int irq;
65-
struct kfifo snoop_fifo[NUM_SNOOP_CHANNELS];
74+
struct aspeed_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS];
75+
};
76+
77+
static struct aspeed_lpc_snoop_channel *snoop_file_to_chan(struct file *file)
78+
{
79+
return container_of(file->private_data,
80+
struct aspeed_lpc_snoop_channel,
81+
miscdev);
82+
}
83+
84+
static ssize_t snoop_file_read(struct file *file, char __user *buffer,
85+
size_t count, loff_t *ppos)
86+
{
87+
struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file);
88+
unsigned int copied;
89+
int ret = 0;
90+
91+
if (kfifo_is_empty(&chan->fifo)) {
92+
if (file->f_flags & O_NONBLOCK)
93+
return -EAGAIN;
94+
ret = wait_event_interruptible(chan->wq,
95+
!kfifo_is_empty(&chan->fifo));
96+
if (ret == -ERESTARTSYS)
97+
return -EINTR;
98+
}
99+
ret = kfifo_to_user(&chan->fifo, buffer, count, &copied);
100+
101+
return ret ? ret : copied;
102+
}
103+
104+
static unsigned int snoop_file_poll(struct file *file,
105+
struct poll_table_struct *pt)
106+
{
107+
struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file);
108+
109+
poll_wait(file, &chan->wq, pt);
110+
return !kfifo_is_empty(&chan->fifo) ? POLLIN : 0;
111+
}
112+
113+
static const struct file_operations snoop_fops = {
114+
.owner = THIS_MODULE,
115+
.read = snoop_file_read,
116+
.poll = snoop_file_poll,
117+
.llseek = noop_llseek,
66118
};
67119

68120
/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */
69-
static void put_fifo_with_discard(struct kfifo *fifo, u8 val)
121+
static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val)
70122
{
71-
if (!kfifo_initialized(fifo))
123+
if (!kfifo_initialized(&chan->fifo))
72124
return;
73-
if (kfifo_is_full(fifo))
74-
kfifo_skip(fifo);
75-
kfifo_put(fifo, val);
125+
if (kfifo_is_full(&chan->fifo))
126+
kfifo_skip(&chan->fifo);
127+
kfifo_put(&chan->fifo, val);
128+
wake_up_interruptible(&chan->wq);
76129
}
77130

78131
static irqreturn_t aspeed_lpc_snoop_irq(int irq, void *arg)
@@ -97,12 +150,12 @@ static irqreturn_t aspeed_lpc_snoop_irq(int irq, void *arg)
97150
if (reg & HICR6_STR_SNP0W) {
98151
u8 val = (data & SNPWDR_CH0_MASK) >> SNPWDR_CH0_SHIFT;
99152

100-
put_fifo_with_discard(&lpc_snoop->snoop_fifo[0], val);
153+
put_fifo_with_discard(&lpc_snoop->chan[0], val);
101154
}
102155
if (reg & HICR6_STR_SNP1W) {
103156
u8 val = (data & SNPWDR_CH1_MASK) >> SNPWDR_CH1_SHIFT;
104157

105-
put_fifo_with_discard(&lpc_snoop->snoop_fifo[1], val);
158+
put_fifo_with_discard(&lpc_snoop->chan[1], val);
106159
}
107160

108161
return IRQ_HANDLED;
@@ -139,12 +192,22 @@ static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
139192
const struct aspeed_lpc_snoop_model_data *model_data =
140193
of_device_get_match_data(dev);
141194

195+
init_waitqueue_head(&lpc_snoop->chan[channel].wq);
142196
/* Create FIFO datastructure */
143-
rc = kfifo_alloc(&lpc_snoop->snoop_fifo[channel],
197+
rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo,
144198
SNOOP_FIFO_SIZE, GFP_KERNEL);
145199
if (rc)
146200
return rc;
147201

202+
lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR;
203+
lpc_snoop->chan[channel].miscdev.name =
204+
devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel);
205+
lpc_snoop->chan[channel].miscdev.fops = &snoop_fops;
206+
lpc_snoop->chan[channel].miscdev.parent = dev;
207+
rc = misc_register(&lpc_snoop->chan[channel].miscdev);
208+
if (rc)
209+
return rc;
210+
148211
/* Enable LPC snoop channel at requested port */
149212
switch (channel) {
150213
case 0:
@@ -191,7 +254,8 @@ static void aspeed_lpc_disable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
191254
return;
192255
}
193256

194-
kfifo_free(&lpc_snoop->snoop_fifo[channel]);
257+
kfifo_free(&lpc_snoop->chan[channel].fifo);
258+
misc_deregister(&lpc_snoop->chan[channel].miscdev);
195259
}
196260

197261
static int aspeed_lpc_snoop_probe(struct platform_device *pdev)

0 commit comments

Comments
 (0)