diff options
author | Markus Pargmann <mpa@pengutronix.de> | 2013-12-20 08:11:29 -0500 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2014-01-08 12:18:30 -0500 |
commit | 9368acc4383bd8cca671fdc49c5f7e241b6909b3 (patch) | |
tree | 9f3dfcf23c95a91b0aa622d6b4dfd37dd0551580 /sound/soc/fsl | |
parent | 2841be9afa6c9d37d41386af30cd8813acd06739 (diff) |
ASoC: fsl-ssi: Move sysfs stats to debugfs
Interrupt statistics of fsl_ssi are mainly for debugging purpose. Most
of those interrupts show error states, e.g. under/overflow.
The stats should be exposed via debugfs instead of sysfs.
This patch moves the statistics file to debugfs.
Signed-off-by: Markus Pargmann <mpa@pengutronix.de>
Signed-off-by: Mark Brown <broonie@linaro.org>
Diffstat (limited to 'sound/soc/fsl')
-rw-r--r-- | sound/soc/fsl/fsl_ssi.c | 184 |
1 files changed, 117 insertions, 67 deletions
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c index 19891f2a5de4..e483e9d84f8b 100644 --- a/sound/soc/fsl/fsl_ssi.c +++ b/sound/soc/fsl/fsl_ssi.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/module.h> | 35 | #include <linux/module.h> |
36 | #include <linux/interrupt.h> | 36 | #include <linux/interrupt.h> |
37 | #include <linux/clk.h> | 37 | #include <linux/clk.h> |
38 | #include <linux/debugfs.h> | ||
38 | #include <linux/device.h> | 39 | #include <linux/device.h> |
39 | #include <linux/delay.h> | 40 | #include <linux/delay.h> |
40 | #include <linux/slab.h> | 41 | #include <linux/slab.h> |
@@ -114,6 +115,14 @@ static inline void write_ssi_mask(u32 __iomem *addr, u32 clear, u32 set) | |||
114 | CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | \ | 115 | CCSR_SSI_SIER_RDMAE | CCSR_SSI_SIER_RIE | \ |
115 | CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN) | 116 | CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_ROE1_EN) |
116 | 117 | ||
118 | #define FSLSSI_SIER_DBG_RX_FLAGS (CCSR_SSI_SIER_RFF0_EN | \ | ||
119 | CCSR_SSI_SIER_RLS_EN | CCSR_SSI_SIER_RFS_EN | \ | ||
120 | CCSR_SSI_SIER_ROE0_EN | CCSR_SSI_SIER_RFRC_EN) | ||
121 | #define FSLSSI_SIER_DBG_TX_FLAGS (CCSR_SSI_SIER_TFE0_EN | \ | ||
122 | CCSR_SSI_SIER_TLS_EN | CCSR_SSI_SIER_TFS_EN | \ | ||
123 | CCSR_SSI_SIER_TUE0_EN | CCSR_SSI_SIER_TFRC_EN) | ||
124 | #define FSLSSI_SISR_MASK (FSLSSI_SIER_DBG_RX_FLAGS | FSLSSI_SIER_DBG_TX_FLAGS) | ||
125 | |||
117 | /** | 126 | /** |
118 | * fsl_ssi_private: per-SSI private data | 127 | * fsl_ssi_private: per-SSI private data |
119 | * | 128 | * |
@@ -133,7 +142,6 @@ struct fsl_ssi_private { | |||
133 | unsigned int irq; | 142 | unsigned int irq; |
134 | unsigned int fifo_depth; | 143 | unsigned int fifo_depth; |
135 | struct snd_soc_dai_driver cpu_dai_drv; | 144 | struct snd_soc_dai_driver cpu_dai_drv; |
136 | struct device_attribute dev_attr; | ||
137 | struct platform_device *pdev; | 145 | struct platform_device *pdev; |
138 | 146 | ||
139 | bool new_binding; | 147 | bool new_binding; |
@@ -175,6 +183,8 @@ struct fsl_ssi_private { | |||
175 | unsigned int tfe1; | 183 | unsigned int tfe1; |
176 | unsigned int tfe0; | 184 | unsigned int tfe0; |
177 | } stats; | 185 | } stats; |
186 | struct dentry *dbg_dir; | ||
187 | struct dentry *dbg_stats; | ||
178 | 188 | ||
179 | char name[1]; | 189 | char name[1]; |
180 | }; | 190 | }; |
@@ -203,7 +213,7 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) | |||
203 | were interrupted for. We mask it with the Interrupt Enable register | 213 | were interrupted for. We mask it with the Interrupt Enable register |
204 | so that we only check for events that we're interested in. | 214 | so that we only check for events that we're interested in. |
205 | */ | 215 | */ |
206 | sisr = read_ssi(&ssi->sisr) & SIER_FLAGS; | 216 | sisr = read_ssi(&ssi->sisr) & FSLSSI_SISR_MASK; |
207 | 217 | ||
208 | if (sisr & CCSR_SSI_SISR_RFRC) { | 218 | if (sisr & CCSR_SSI_SISR_RFRC) { |
209 | ssi_private->stats.rfrc++; | 219 | ssi_private->stats.rfrc++; |
@@ -323,6 +333,102 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id) | |||
323 | return ret; | 333 | return ret; |
324 | } | 334 | } |
325 | 335 | ||
336 | #if IS_ENABLED(CONFIG_DEBUG_FS) | ||
337 | /* Show the statistics of a flag only if its interrupt is enabled. The | ||
338 | * compiler will optimze this code to a no-op if the interrupt is not | ||
339 | * enabled. | ||
340 | */ | ||
341 | #define SIER_SHOW(flag, name) \ | ||
342 | do { \ | ||
343 | if (FSLSSI_SISR_MASK & CCSR_SSI_SIER_##flag) \ | ||
344 | seq_printf(s, #name "=%u\n", ssi_private->stats.name); \ | ||
345 | } while (0) | ||
346 | |||
347 | |||
348 | /** | ||
349 | * fsl_sysfs_ssi_show: display SSI statistics | ||
350 | * | ||
351 | * Display the statistics for the current SSI device. To avoid confusion, | ||
352 | * we only show those counts that are enabled. | ||
353 | */ | ||
354 | static ssize_t fsl_ssi_stats_show(struct seq_file *s, void *unused) | ||
355 | { | ||
356 | struct fsl_ssi_private *ssi_private = s->private; | ||
357 | |||
358 | SIER_SHOW(RFRC_EN, rfrc); | ||
359 | SIER_SHOW(TFRC_EN, tfrc); | ||
360 | SIER_SHOW(CMDAU_EN, cmdau); | ||
361 | SIER_SHOW(CMDDU_EN, cmddu); | ||
362 | SIER_SHOW(RXT_EN, rxt); | ||
363 | SIER_SHOW(RDR1_EN, rdr1); | ||
364 | SIER_SHOW(RDR0_EN, rdr0); | ||
365 | SIER_SHOW(TDE1_EN, tde1); | ||
366 | SIER_SHOW(TDE0_EN, tde0); | ||
367 | SIER_SHOW(ROE1_EN, roe1); | ||
368 | SIER_SHOW(ROE0_EN, roe0); | ||
369 | SIER_SHOW(TUE1_EN, tue1); | ||
370 | SIER_SHOW(TUE0_EN, tue0); | ||
371 | SIER_SHOW(TFS_EN, tfs); | ||
372 | SIER_SHOW(RFS_EN, rfs); | ||
373 | SIER_SHOW(TLS_EN, tls); | ||
374 | SIER_SHOW(RLS_EN, rls); | ||
375 | SIER_SHOW(RFF1_EN, rff1); | ||
376 | SIER_SHOW(RFF0_EN, rff0); | ||
377 | SIER_SHOW(TFE1_EN, tfe1); | ||
378 | SIER_SHOW(TFE0_EN, tfe0); | ||
379 | |||
380 | return 0; | ||
381 | } | ||
382 | |||
383 | static int fsl_ssi_stats_open(struct inode *inode, struct file *file) | ||
384 | { | ||
385 | return single_open(file, fsl_ssi_stats_show, inode->i_private); | ||
386 | } | ||
387 | |||
388 | static const struct file_operations fsl_ssi_stats_ops = { | ||
389 | .open = fsl_ssi_stats_open, | ||
390 | .read = seq_read, | ||
391 | .llseek = seq_lseek, | ||
392 | .release = single_release, | ||
393 | }; | ||
394 | |||
395 | static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private, | ||
396 | struct device *dev) | ||
397 | { | ||
398 | ssi_private->dbg_dir = debugfs_create_dir(dev_name(dev), NULL); | ||
399 | if (!ssi_private->dbg_dir) | ||
400 | return -ENOMEM; | ||
401 | |||
402 | ssi_private->dbg_stats = debugfs_create_file("stats", S_IRUGO, | ||
403 | ssi_private->dbg_dir, ssi_private, &fsl_ssi_stats_ops); | ||
404 | if (!ssi_private->dbg_stats) { | ||
405 | debugfs_remove(ssi_private->dbg_dir); | ||
406 | return -ENOMEM; | ||
407 | } | ||
408 | |||
409 | return 0; | ||
410 | } | ||
411 | |||
412 | static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private) | ||
413 | { | ||
414 | debugfs_remove(ssi_private->dbg_stats); | ||
415 | debugfs_remove(ssi_private->dbg_dir); | ||
416 | } | ||
417 | |||
418 | #else | ||
419 | |||
420 | static int fsl_ssi_debugfs_create(struct fsl_ssi_private *ssi_private, | ||
421 | struct device *dev) | ||
422 | { | ||
423 | return 0; | ||
424 | } | ||
425 | |||
426 | static void fsl_ssi_debugfs_remove(struct fsl_ssi_private *ssi_private) | ||
427 | { | ||
428 | } | ||
429 | |||
430 | #endif /* IS_ENABLED(CONFIG_DEBUG_FS) */ | ||
431 | |||
326 | static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private) | 432 | static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private) |
327 | { | 433 | { |
328 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; | 434 | struct ccsr_ssi __iomem *ssi = ssi_private->ssi; |
@@ -991,56 +1097,6 @@ static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = { | |||
991 | .write = fsl_ssi_ac97_write, | 1097 | .write = fsl_ssi_ac97_write, |
992 | }; | 1098 | }; |
993 | 1099 | ||
994 | /* Show the statistics of a flag only if its interrupt is enabled. The | ||
995 | * compiler will optimze this code to a no-op if the interrupt is not | ||
996 | * enabled. | ||
997 | */ | ||
998 | #define SIER_SHOW(flag, name) \ | ||
999 | do { \ | ||
1000 | if (SIER_FLAGS & CCSR_SSI_SIER_##flag) \ | ||
1001 | length += sprintf(buf + length, #name "=%u\n", \ | ||
1002 | ssi_private->stats.name); \ | ||
1003 | } while (0) | ||
1004 | |||
1005 | |||
1006 | /** | ||
1007 | * fsl_sysfs_ssi_show: display SSI statistics | ||
1008 | * | ||
1009 | * Display the statistics for the current SSI device. To avoid confusion, | ||
1010 | * we only show those counts that are enabled. | ||
1011 | */ | ||
1012 | static ssize_t fsl_sysfs_ssi_show(struct device *dev, | ||
1013 | struct device_attribute *attr, char *buf) | ||
1014 | { | ||
1015 | struct fsl_ssi_private *ssi_private = | ||
1016 | container_of(attr, struct fsl_ssi_private, dev_attr); | ||
1017 | ssize_t length = 0; | ||
1018 | |||
1019 | SIER_SHOW(RFRC_EN, rfrc); | ||
1020 | SIER_SHOW(TFRC_EN, tfrc); | ||
1021 | SIER_SHOW(CMDAU_EN, cmdau); | ||
1022 | SIER_SHOW(CMDDU_EN, cmddu); | ||
1023 | SIER_SHOW(RXT_EN, rxt); | ||
1024 | SIER_SHOW(RDR1_EN, rdr1); | ||
1025 | SIER_SHOW(RDR0_EN, rdr0); | ||
1026 | SIER_SHOW(TDE1_EN, tde1); | ||
1027 | SIER_SHOW(TDE0_EN, tde0); | ||
1028 | SIER_SHOW(ROE1_EN, roe1); | ||
1029 | SIER_SHOW(ROE0_EN, roe0); | ||
1030 | SIER_SHOW(TUE1_EN, tue1); | ||
1031 | SIER_SHOW(TUE0_EN, tue0); | ||
1032 | SIER_SHOW(TFS_EN, tfs); | ||
1033 | SIER_SHOW(RFS_EN, rfs); | ||
1034 | SIER_SHOW(TLS_EN, tls); | ||
1035 | SIER_SHOW(RLS_EN, rls); | ||
1036 | SIER_SHOW(RFF1_EN, rff1); | ||
1037 | SIER_SHOW(RFF0_EN, rff0); | ||
1038 | SIER_SHOW(TFE1_EN, tfe1); | ||
1039 | SIER_SHOW(TFE0_EN, tfe0); | ||
1040 | |||
1041 | return length; | ||
1042 | } | ||
1043 | |||
1044 | /** | 1100 | /** |
1045 | * Make every character in a string lower-case | 1101 | * Make every character in a string lower-case |
1046 | */ | 1102 | */ |
@@ -1233,20 +1289,6 @@ static int fsl_ssi_probe(struct platform_device *pdev) | |||
1233 | } | 1289 | } |
1234 | } | 1290 | } |
1235 | 1291 | ||
1236 | /* Initialize the the device_attribute structure */ | ||
1237 | dev_attr = &ssi_private->dev_attr; | ||
1238 | sysfs_attr_init(&dev_attr->attr); | ||
1239 | dev_attr->attr.name = "statistics"; | ||
1240 | dev_attr->attr.mode = S_IRUGO; | ||
1241 | dev_attr->show = fsl_sysfs_ssi_show; | ||
1242 | |||
1243 | ret = device_create_file(&pdev->dev, dev_attr); | ||
1244 | if (ret) { | ||
1245 | dev_err(&pdev->dev, "could not create sysfs %s file\n", | ||
1246 | ssi_private->dev_attr.attr.name); | ||
1247 | goto error_clk; | ||
1248 | } | ||
1249 | |||
1250 | /* Register with ASoC */ | 1292 | /* Register with ASoC */ |
1251 | dev_set_drvdata(&pdev->dev, ssi_private); | 1293 | dev_set_drvdata(&pdev->dev, ssi_private); |
1252 | 1294 | ||
@@ -1257,6 +1299,10 @@ static int fsl_ssi_probe(struct platform_device *pdev) | |||
1257 | goto error_dev; | 1299 | goto error_dev; |
1258 | } | 1300 | } |
1259 | 1301 | ||
1302 | ret = fsl_ssi_debugfs_create(ssi_private, &pdev->dev); | ||
1303 | if (ret) | ||
1304 | goto error_dbgfs; | ||
1305 | |||
1260 | if (ssi_private->ssi_on_imx) { | 1306 | if (ssi_private->ssi_on_imx) { |
1261 | if (!ssi_private->use_dma) { | 1307 | if (!ssi_private->use_dma) { |
1262 | 1308 | ||
@@ -1326,6 +1372,9 @@ error_dai: | |||
1326 | imx_pcm_fiq_exit(pdev); | 1372 | imx_pcm_fiq_exit(pdev); |
1327 | 1373 | ||
1328 | error_pcm: | 1374 | error_pcm: |
1375 | fsl_ssi_debugfs_remove(ssi_private); | ||
1376 | |||
1377 | error_dbgfs: | ||
1329 | snd_soc_unregister_component(&pdev->dev); | 1378 | snd_soc_unregister_component(&pdev->dev); |
1330 | 1379 | ||
1331 | error_dev: | 1380 | error_dev: |
@@ -1349,10 +1398,11 @@ static int fsl_ssi_remove(struct platform_device *pdev) | |||
1349 | { | 1398 | { |
1350 | struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev); | 1399 | struct fsl_ssi_private *ssi_private = dev_get_drvdata(&pdev->dev); |
1351 | 1400 | ||
1401 | fsl_ssi_debugfs_remove(ssi_private); | ||
1402 | |||
1352 | if (!ssi_private->new_binding) | 1403 | if (!ssi_private->new_binding) |
1353 | platform_device_unregister(ssi_private->pdev); | 1404 | platform_device_unregister(ssi_private->pdev); |
1354 | snd_soc_unregister_component(&pdev->dev); | 1405 | snd_soc_unregister_component(&pdev->dev); |
1355 | device_remove_file(&pdev->dev, &ssi_private->dev_attr); | ||
1356 | if (ssi_private->ssi_on_imx) { | 1406 | if (ssi_private->ssi_on_imx) { |
1357 | if (!IS_ERR(ssi_private->baudclk)) | 1407 | if (!IS_ERR(ssi_private->baudclk)) |
1358 | clk_disable_unprepare(ssi_private->baudclk); | 1408 | clk_disable_unprepare(ssi_private->baudclk); |