Skip to content

Commit 708109c

Browse files
committed
Merge tag 'aspeed-6.17-drivers-1' of https://git.kernel.org/pub/scm/linux/kernel/git/bmc/linux into soc/drivers
ASPEED SoC driver updates for 6.17 The ASPEED LPC snoop driver was recently the cause of some concern. In addition to the initial fixes, the channel configuration paths are refactored to improve robustness against errors. * tag 'aspeed-6.17-drivers-1' of https://git.kernel.org/pub/scm/linux/kernel/git/bmc/linux: soc: aspeed: lpc-snoop: Lift channel config to const structs soc: aspeed: lpc-snoop: Consolidate channel initialisation soc: aspeed: lpc-snoop: Use dev_err_probe() where possible soc: aspeed: lpc-snoop: Switch to devm_clk_get_enabled() soc: aspeed: lpc-snoop: Rearrange channel paths soc: aspeed: lpc-snoop: Rename 'channel' to 'index' in channel paths soc: aspeed: lpc-snoop: Constrain parameters in channel paths soc: aspeed: lpc-snoop: Ensure model_data is valid soc: aspeed: lpc-snoop: Don't disable channels that aren't enabled soc: aspeed: lpc-snoop: Cleanup resources in stack-order Link: https://lore.kernel.org/r/9123f151280e52c63dcb645cb07d4eee3462c067.camel@codeconstruct.com.au Signed-off-by: Arnd Bergmann <arnd@arndb.de>
2 parents 404dcaa + fdf003f commit 708109c

1 file changed

Lines changed: 112 additions & 116 deletions

File tree

drivers/soc/aspeed/aspeed-lpc-snoop.c

Lines changed: 112 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include <linux/bitops.h>
1414
#include <linux/clk.h>
15+
#include <linux/dev_printk.h>
1516
#include <linux/interrupt.h>
1617
#include <linux/fs.h>
1718
#include <linux/kfifo.h>
@@ -25,7 +26,6 @@
2526

2627
#define DEVICE_NAME "aspeed-lpc-snoop"
2728

28-
#define NUM_SNOOP_CHANNELS 2
2929
#define SNOOP_FIFO_SIZE 2048
3030

3131
#define HICR5 0x80
@@ -57,7 +57,23 @@ struct aspeed_lpc_snoop_model_data {
5757
unsigned int has_hicrb_ensnp;
5858
};
5959

60+
enum aspeed_lpc_snoop_index {
61+
ASPEED_LPC_SNOOP_INDEX_0 = 0,
62+
ASPEED_LPC_SNOOP_INDEX_1 = 1,
63+
ASPEED_LPC_SNOOP_INDEX_MAX = ASPEED_LPC_SNOOP_INDEX_1,
64+
};
65+
66+
struct aspeed_lpc_snoop_channel_cfg {
67+
enum aspeed_lpc_snoop_index index;
68+
u32 hicr5_en;
69+
u32 snpwadr_mask;
70+
u32 snpwadr_shift;
71+
u32 hicrb_en;
72+
};
73+
6074
struct aspeed_lpc_snoop_channel {
75+
const struct aspeed_lpc_snoop_channel_cfg *cfg;
76+
bool enabled;
6177
struct kfifo fifo;
6278
wait_queue_head_t wq;
6379
struct miscdevice miscdev;
@@ -67,7 +83,24 @@ struct aspeed_lpc_snoop {
6783
struct regmap *regmap;
6884
int irq;
6985
struct clk *clk;
70-
struct aspeed_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS];
86+
struct aspeed_lpc_snoop_channel chan[ASPEED_LPC_SNOOP_INDEX_MAX + 1];
87+
};
88+
89+
static const struct aspeed_lpc_snoop_channel_cfg channel_cfgs[ASPEED_LPC_SNOOP_INDEX_MAX + 1] = {
90+
{
91+
.index = ASPEED_LPC_SNOOP_INDEX_0,
92+
.hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W,
93+
.snpwadr_mask = SNPWADR_CH0_MASK,
94+
.snpwadr_shift = SNPWADR_CH0_SHIFT,
95+
.hicrb_en = HICRB_ENSNP0D,
96+
},
97+
{
98+
.index = ASPEED_LPC_SNOOP_INDEX_1,
99+
.hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W,
100+
.snpwadr_mask = SNPWADR_CH1_MASK,
101+
.snpwadr_shift = SNPWADR_CH1_SHIFT,
102+
.hicrb_en = HICRB_ENSNP1D,
103+
},
71104
};
72105

73106
static struct aspeed_lpc_snoop_channel *snoop_file_to_chan(struct file *file)
@@ -181,98 +214,88 @@ static int aspeed_lpc_snoop_config_irq(struct aspeed_lpc_snoop *lpc_snoop,
181214
return 0;
182215
}
183216

184-
static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
185-
struct device *dev,
186-
int channel, u16 lpc_port)
217+
__attribute__((nonnull))
218+
static int aspeed_lpc_enable_snoop(struct device *dev,
219+
struct aspeed_lpc_snoop *lpc_snoop,
220+
struct aspeed_lpc_snoop_channel *channel,
221+
const struct aspeed_lpc_snoop_channel_cfg *cfg,
222+
u16 lpc_port)
187223
{
224+
const struct aspeed_lpc_snoop_model_data *model_data;
188225
int rc = 0;
189-
u32 hicr5_en, snpwadr_mask, snpwadr_shift, hicrb_en;
190-
const struct aspeed_lpc_snoop_model_data *model_data =
191-
of_device_get_match_data(dev);
192-
193-
init_waitqueue_head(&lpc_snoop->chan[channel].wq);
194-
/* Create FIFO datastructure */
195-
rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo,
196-
SNOOP_FIFO_SIZE, GFP_KERNEL);
226+
227+
if (WARN_ON(channel->enabled))
228+
return -EBUSY;
229+
230+
init_waitqueue_head(&channel->wq);
231+
232+
channel->cfg = cfg;
233+
channel->miscdev.minor = MISC_DYNAMIC_MINOR;
234+
channel->miscdev.fops = &snoop_fops;
235+
channel->miscdev.parent = dev;
236+
237+
channel->miscdev.name =
238+
devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, cfg->index);
239+
if (!channel->miscdev.name)
240+
return -ENOMEM;
241+
242+
rc = kfifo_alloc(&channel->fifo, SNOOP_FIFO_SIZE, GFP_KERNEL);
197243
if (rc)
198244
return rc;
199245

200-
lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR;
201-
lpc_snoop->chan[channel].miscdev.name =
202-
devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel);
203-
if (!lpc_snoop->chan[channel].miscdev.name) {
204-
rc = -ENOMEM;
205-
goto err_free_fifo;
206-
}
207-
lpc_snoop->chan[channel].miscdev.fops = &snoop_fops;
208-
lpc_snoop->chan[channel].miscdev.parent = dev;
209-
rc = misc_register(&lpc_snoop->chan[channel].miscdev);
246+
rc = misc_register(&channel->miscdev);
210247
if (rc)
211248
goto err_free_fifo;
212249

213250
/* Enable LPC snoop channel at requested port */
214-
switch (channel) {
215-
case 0:
216-
hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W;
217-
snpwadr_mask = SNPWADR_CH0_MASK;
218-
snpwadr_shift = SNPWADR_CH0_SHIFT;
219-
hicrb_en = HICRB_ENSNP0D;
220-
break;
221-
case 1:
222-
hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W;
223-
snpwadr_mask = SNPWADR_CH1_MASK;
224-
snpwadr_shift = SNPWADR_CH1_SHIFT;
225-
hicrb_en = HICRB_ENSNP1D;
226-
break;
227-
default:
228-
rc = -EINVAL;
229-
goto err_misc_deregister;
230-
}
251+
regmap_set_bits(lpc_snoop->regmap, HICR5, cfg->hicr5_en);
252+
regmap_update_bits(lpc_snoop->regmap, SNPWADR, cfg->snpwadr_mask,
253+
lpc_port << cfg->snpwadr_shift);
231254

232-
regmap_update_bits(lpc_snoop->regmap, HICR5, hicr5_en, hicr5_en);
233-
regmap_update_bits(lpc_snoop->regmap, SNPWADR, snpwadr_mask,
234-
lpc_port << snpwadr_shift);
235-
if (model_data->has_hicrb_ensnp)
236-
regmap_update_bits(lpc_snoop->regmap, HICRB,
237-
hicrb_en, hicrb_en);
255+
model_data = of_device_get_match_data(dev);
256+
if (model_data && model_data->has_hicrb_ensnp)
257+
regmap_set_bits(lpc_snoop->regmap, HICRB, cfg->hicrb_en);
258+
259+
channel->enabled = true;
238260

239261
return 0;
240262

241-
err_misc_deregister:
242-
misc_deregister(&lpc_snoop->chan[channel].miscdev);
243263
err_free_fifo:
244-
kfifo_free(&lpc_snoop->chan[channel].fifo);
264+
kfifo_free(&channel->fifo);
245265
return rc;
246266
}
247267

268+
__attribute__((nonnull))
248269
static void aspeed_lpc_disable_snoop(struct aspeed_lpc_snoop *lpc_snoop,
249-
int channel)
270+
struct aspeed_lpc_snoop_channel *channel)
250271
{
251-
switch (channel) {
252-
case 0:
253-
regmap_update_bits(lpc_snoop->regmap, HICR5,
254-
HICR5_EN_SNP0W | HICR5_ENINT_SNP0W,
255-
0);
256-
break;
257-
case 1:
258-
regmap_update_bits(lpc_snoop->regmap, HICR5,
259-
HICR5_EN_SNP1W | HICR5_ENINT_SNP1W,
260-
0);
261-
break;
262-
default:
272+
if (!channel->enabled)
263273
return;
264-
}
265274

266-
kfifo_free(&lpc_snoop->chan[channel].fifo);
267-
misc_deregister(&lpc_snoop->chan[channel].miscdev);
275+
/* Disable interrupts along with the device */
276+
regmap_clear_bits(lpc_snoop->regmap, HICR5, channel->cfg->hicr5_en);
277+
278+
channel->enabled = false;
279+
/* Consider improving safety wrt concurrent reader(s) */
280+
misc_deregister(&channel->miscdev);
281+
kfifo_free(&channel->fifo);
282+
}
283+
284+
static void aspeed_lpc_snoop_remove(struct platform_device *pdev)
285+
{
286+
struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev);
287+
288+
/* Disable both snoop channels */
289+
aspeed_lpc_disable_snoop(lpc_snoop, &lpc_snoop->chan[0]);
290+
aspeed_lpc_disable_snoop(lpc_snoop, &lpc_snoop->chan[1]);
268291
}
269292

270293
static int aspeed_lpc_snoop_probe(struct platform_device *pdev)
271294
{
272295
struct aspeed_lpc_snoop *lpc_snoop;
273-
struct device *dev;
274296
struct device_node *np;
275-
u32 port;
297+
struct device *dev;
298+
int idx;
276299
int rc;
277300

278301
dev = &pdev->dev;
@@ -290,67 +313,40 @@ static int aspeed_lpc_snoop_probe(struct platform_device *pdev)
290313
}
291314

292315
lpc_snoop->regmap = syscon_node_to_regmap(np);
293-
if (IS_ERR(lpc_snoop->regmap)) {
294-
dev_err(dev, "Couldn't get regmap\n");
295-
return -ENODEV;
296-
}
316+
if (IS_ERR(lpc_snoop->regmap))
317+
return dev_err_probe(dev, PTR_ERR(lpc_snoop->regmap), "Couldn't get regmap\n");
297318

298319
dev_set_drvdata(&pdev->dev, lpc_snoop);
299320

300-
rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port);
301-
if (rc) {
302-
dev_err(dev, "no snoop ports configured\n");
303-
return -ENODEV;
304-
}
305-
306-
lpc_snoop->clk = devm_clk_get(dev, NULL);
307-
if (IS_ERR(lpc_snoop->clk)) {
308-
rc = PTR_ERR(lpc_snoop->clk);
309-
if (rc != -EPROBE_DEFER)
310-
dev_err(dev, "couldn't get clock\n");
311-
return rc;
312-
}
313-
rc = clk_prepare_enable(lpc_snoop->clk);
314-
if (rc) {
315-
dev_err(dev, "couldn't enable clock\n");
316-
return rc;
317-
}
321+
lpc_snoop->clk = devm_clk_get_enabled(dev, NULL);
322+
if (IS_ERR(lpc_snoop->clk))
323+
return dev_err_probe(dev, PTR_ERR(lpc_snoop->clk), "couldn't get clock");
318324

319325
rc = aspeed_lpc_snoop_config_irq(lpc_snoop, pdev);
320326
if (rc)
321-
goto err;
322-
323-
rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 0, port);
324-
if (rc)
325-
goto err;
326-
327-
/* Configuration of 2nd snoop channel port is optional */
328-
if (of_property_read_u32_index(dev->of_node, "snoop-ports",
329-
1, &port) == 0) {
330-
rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 1, port);
331-
if (rc) {
332-
aspeed_lpc_disable_snoop(lpc_snoop, 0);
333-
goto err;
334-
}
335-
}
327+
return rc;
336328

337-
return 0;
329+
static_assert(ARRAY_SIZE(channel_cfgs) == ARRAY_SIZE(lpc_snoop->chan),
330+
"Broken implementation assumption regarding cfg count");
331+
for (idx = ASPEED_LPC_SNOOP_INDEX_0; idx <= ASPEED_LPC_SNOOP_INDEX_MAX; idx++) {
332+
u32 port;
338333

339-
err:
340-
clk_disable_unprepare(lpc_snoop->clk);
334+
rc = of_property_read_u32_index(dev->of_node, "snoop-ports", idx, &port);
335+
if (rc)
336+
break;
341337

342-
return rc;
343-
}
338+
rc = aspeed_lpc_enable_snoop(dev, lpc_snoop, &lpc_snoop->chan[idx],
339+
&channel_cfgs[idx], port);
340+
if (rc)
341+
goto cleanup_channels;
342+
}
344343

345-
static void aspeed_lpc_snoop_remove(struct platform_device *pdev)
346-
{
347-
struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev);
344+
return idx == ASPEED_LPC_SNOOP_INDEX_0 ? -ENODEV : 0;
348345

349-
/* Disable both snoop channels */
350-
aspeed_lpc_disable_snoop(lpc_snoop, 0);
351-
aspeed_lpc_disable_snoop(lpc_snoop, 1);
346+
cleanup_channels:
347+
aspeed_lpc_snoop_remove(pdev);
352348

353-
clk_disable_unprepare(lpc_snoop->clk);
349+
return rc;
354350
}
355351

356352
static const struct aspeed_lpc_snoop_model_data ast2400_model_data = {

0 commit comments

Comments
 (0)