]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/misc/aspeed-lpc-snoop.c
e1000e: Fix e1000_check_for_copper_link_ich8lan return value.
[mirror_ubuntu-artful-kernel.git] / drivers / misc / aspeed-lpc-snoop.c
CommitLineData
9f4f9ae8
RL
1/*
2 * Copyright 2017 Google Inc
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version
7 * 2 of the License, or (at your option) any later version.
8 *
9 * Provides a simple driver to control the ASPEED LPC snoop interface which
10 * allows the BMC to listen on and save the data written by
11 * the host to an arbitrary LPC I/O port.
12 *
13 * Typically used by the BMC to "watch" host boot progress via port
14 * 0x80 writes made by the BIOS during the boot process.
15 */
16
17#include <linux/bitops.h>
18#include <linux/interrupt.h>
19#include <linux/kfifo.h>
20#include <linux/mfd/syscon.h>
21#include <linux/module.h>
22#include <linux/of.h>
23#include <linux/platform_device.h>
24#include <linux/regmap.h>
25
26#define DEVICE_NAME "aspeed-lpc-snoop"
27
28#define NUM_SNOOP_CHANNELS 2
29#define SNOOP_FIFO_SIZE 2048
30
31#define HICR5 0x0
32#define HICR5_EN_SNP0W BIT(0)
33#define HICR5_ENINT_SNP0W BIT(1)
34#define HICR5_EN_SNP1W BIT(2)
35#define HICR5_ENINT_SNP1W BIT(3)
36
37#define HICR6 0x4
38#define HICR6_STR_SNP0W BIT(0)
39#define HICR6_STR_SNP1W BIT(1)
40#define SNPWADR 0x10
41#define SNPWADR_CH0_MASK GENMASK(15, 0)
42#define SNPWADR_CH0_SHIFT 0
43#define SNPWADR_CH1_MASK GENMASK(31, 16)
44#define SNPWADR_CH1_SHIFT 16
45#define SNPWDR 0x14
46#define SNPWDR_CH0_MASK GENMASK(7, 0)
47#define SNPWDR_CH0_SHIFT 0
48#define SNPWDR_CH1_MASK GENMASK(15, 8)
49#define SNPWDR_CH1_SHIFT 8
50#define HICRB 0x80
51#define HICRB_ENSNP0D BIT(14)
52#define HICRB_ENSNP1D BIT(15)
53
54struct aspeed_lpc_snoop {
55 struct regmap *regmap;
56 int irq;
57 struct kfifo snoop_fifo[NUM_SNOOP_CHANNELS];
58};
59
60/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */
61static void put_fifo_with_discard(struct kfifo *fifo, u8 val)
62{
63 if (!kfifo_initialized(fifo))
64 return;
65 if (kfifo_is_full(fifo))
66 kfifo_skip(fifo);
67 kfifo_put(fifo, val);
68}
69
70static irqreturn_t aspeed_lpc_snoop_irq(int irq, void *arg)
71{
72 struct aspeed_lpc_snoop *lpc_snoop = arg;
73 u32 reg, data;
74
75 if (regmap_read(lpc_snoop->regmap, HICR6, &reg))
76 return IRQ_NONE;
77
78 /* Check if one of the snoop channels is interrupting */
79 reg &= (HICR6_STR_SNP0W | HICR6_STR_SNP1W);
80 if (!reg)
81 return IRQ_NONE;
82
83 /* Ack pending IRQs */
84 regmap_write(lpc_snoop->regmap, HICR6, reg);
85
86 /* Read and save most recent snoop'ed data byte to FIFO */
87 regmap_read(lpc_snoop->regmap, SNPWDR, &data);
88
89 if (reg & HICR6_STR_SNP0W) {
90 u8 val = (data & SNPWDR_CH0_MASK) >> SNPWDR_CH0_SHIFT;
91
92 put_fifo_with_discard(&lpc_snoop->snoop_fifo[0], val);
93 }
94 if (reg & HICR6_STR_SNP1W) {
95 u8 val = (data & SNPWDR_CH1_MASK) >> SNPWDR_CH1_SHIFT;
96
97 put_fifo_with_discard(&lpc_snoop->snoop_fifo[1], val);
98 }
99
100 return IRQ_HANDLED;
101}
102
103static int aspeed_lpc_snoop_config_irq(struct aspeed_lpc_snoop *lpc_snoop,
104 struct platform_device *pdev)
105{
106 struct device *dev = &pdev->dev;
107 int rc;
108
109 lpc_snoop->irq = platform_get_irq(pdev, 0);
110 if (!lpc_snoop->irq)
111 return -ENODEV;
112
113 rc = devm_request_irq(dev, lpc_snoop->irq,
114 aspeed_lpc_snoop_irq, IRQF_SHARED,
115 DEVICE_NAME, lpc_snoop);
116 if (rc < 0) {
117 dev_warn(dev, "Unable to request IRQ %d\n", lpc_snoop->irq);
118 lpc_snoop->irq = 0;
119 return rc;
120 }
121
122 return 0;
123}
124
125static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
126 int channel, u16 lpc_port)
127{
128 int rc = 0;
129 u32 hicr5_en, snpwadr_mask, snpwadr_shift, hicrb_en;
130
131 /* Create FIFO datastructure */
132 rc = kfifo_alloc(&lpc_snoop->snoop_fifo[channel],
133 SNOOP_FIFO_SIZE, GFP_KERNEL);
134 if (rc)
135 return rc;
136
137 /* Enable LPC snoop channel at requested port */
138 switch (channel) {
139 case 0:
140 hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W;
141 snpwadr_mask = SNPWADR_CH0_MASK;
142 snpwadr_shift = SNPWADR_CH0_SHIFT;
143 hicrb_en = HICRB_ENSNP0D;
144 break;
145 case 1:
146 hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W;
147 snpwadr_mask = SNPWADR_CH1_MASK;
148 snpwadr_shift = SNPWADR_CH1_SHIFT;
149 hicrb_en = HICRB_ENSNP1D;
150 break;
151 default:
152 return -EINVAL;
153 }
154
155 regmap_update_bits(lpc_snoop->regmap, HICR5, hicr5_en, hicr5_en);
156 regmap_update_bits(lpc_snoop->regmap, SNPWADR, snpwadr_mask,
157 lpc_port << snpwadr_shift);
158 regmap_update_bits(lpc_snoop->regmap, HICRB, hicrb_en, hicrb_en);
159
160 return rc;
161}
162
163static void aspeed_lpc_disable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
164 int channel)
165{
166 switch (channel) {
167 case 0:
168 regmap_update_bits(lpc_snoop->regmap, HICR5,
169 HICR5_EN_SNP0W | HICR5_ENINT_SNP0W,
170 0);
171 break;
172 case 1:
173 regmap_update_bits(lpc_snoop->regmap, HICR5,
174 HICR5_EN_SNP1W | HICR5_ENINT_SNP1W,
175 0);
176 break;
177 default:
178 return;
179 }
180
181 kfifo_free(&lpc_snoop->snoop_fifo[channel]);
182}
183
184static int aspeed_lpc_snoop_probe(struct platform_device *pdev)
185{
186 struct aspeed_lpc_snoop *lpc_snoop;
187 struct device *dev;
188 u32 port;
189 int rc;
190
191 dev = &pdev->dev;
192
193 lpc_snoop = devm_kzalloc(dev, sizeof(*lpc_snoop), GFP_KERNEL);
194 if (!lpc_snoop)
195 return -ENOMEM;
196
197 lpc_snoop->regmap = syscon_node_to_regmap(
198 pdev->dev.parent->of_node);
199 if (IS_ERR(lpc_snoop->regmap)) {
200 dev_err(dev, "Couldn't get regmap\n");
201 return -ENODEV;
202 }
203
204 dev_set_drvdata(&pdev->dev, lpc_snoop);
205
206 rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port);
207 if (rc) {
208 dev_err(dev, "no snoop ports configured\n");
209 return -ENODEV;
210 }
211
212 rc = aspeed_lpc_snoop_config_irq(lpc_snoop, pdev);
213 if (rc)
214 return rc;
215
216 rc = aspeed_lpc_enable_snoop(lpc_snoop, 0, port);
217 if (rc)
218 return rc;
219
220 /* Configuration of 2nd snoop channel port is optional */
221 if (of_property_read_u32_index(dev->of_node, "snoop-ports",
222 1, &port) == 0) {
223 rc = aspeed_lpc_enable_snoop(lpc_snoop, 1, port);
224 if (rc)
225 aspeed_lpc_disable_snoop(lpc_snoop, 0);
226 }
227
228 return rc;
229}
230
231static int aspeed_lpc_snoop_remove(struct platform_device *pdev)
232{
233 struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev);
234
235 /* Disable both snoop channels */
236 aspeed_lpc_disable_snoop(lpc_snoop, 0);
237 aspeed_lpc_disable_snoop(lpc_snoop, 1);
238
239 return 0;
240}
241
242static const struct of_device_id aspeed_lpc_snoop_match[] = {
243 { .compatible = "aspeed,ast2500-lpc-snoop" },
244 { },
245};
246
247static struct platform_driver aspeed_lpc_snoop_driver = {
248 .driver = {
249 .name = DEVICE_NAME,
250 .of_match_table = aspeed_lpc_snoop_match,
251 },
252 .probe = aspeed_lpc_snoop_probe,
253 .remove = aspeed_lpc_snoop_remove,
254};
255
256module_platform_driver(aspeed_lpc_snoop_driver);
257
258MODULE_DEVICE_TABLE(of, aspeed_lpc_snoop_match);
259MODULE_LICENSE("GPL");
260MODULE_AUTHOR("Robert Lippert <rlippert@google.com>");
261MODULE_DESCRIPTION("Linux driver to control Aspeed LPC snoop functionality");