Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1/*
  2 * Intel(R) Trace Hub driver core
  3 *
  4 * Copyright (C) 2014-2015 Intel Corporation.
  5 *
  6 * This program is free software; you can redistribute it and/or modify it
  7 * under the terms and conditions of the GNU General Public License,
  8 * version 2, as published by the Free Software Foundation.
  9 *
 10 * This program is distributed in the hope it will be useful, but WITHOUT
 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 13 * more details.
 14 */
 15
 16#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
 17
 18#include <linux/types.h>
 19#include <linux/module.h>
 20#include <linux/device.h>
 21#include <linux/sysfs.h>
 22#include <linux/kdev_t.h>
 23#include <linux/debugfs.h>
 24#include <linux/idr.h>
 25#include <linux/pci.h>
 26#include <linux/dma-mapping.h>
 27
 28#include "intel_th.h"
 29#include "debug.h"
 30
 31static DEFINE_IDA(intel_th_ida);
 32
 33static int intel_th_match(struct device *dev, struct device_driver *driver)
 34{
 35	struct intel_th_driver *thdrv = to_intel_th_driver(driver);
 36	struct intel_th_device *thdev = to_intel_th_device(dev);
 37
 38	if (thdev->type == INTEL_TH_SWITCH &&
 39	    (!thdrv->enable || !thdrv->disable))
 40		return 0;
 41
 42	return !strcmp(thdev->name, driver->name);
 43}
 44
 45static int intel_th_child_remove(struct device *dev, void *data)
 46{
 47	device_release_driver(dev);
 48
 49	return 0;
 50}
 51
 52static int intel_th_probe(struct device *dev)
 53{
 54	struct intel_th_driver *thdrv = to_intel_th_driver(dev->driver);
 55	struct intel_th_device *thdev = to_intel_th_device(dev);
 56	struct intel_th_driver *hubdrv;
 57	struct intel_th_device *hub = NULL;
 58	int ret;
 59
 60	if (thdev->type == INTEL_TH_SWITCH)
 61		hub = thdev;
 62	else if (dev->parent)
 63		hub = to_intel_th_device(dev->parent);
 64
 65	if (!hub || !hub->dev.driver)
 66		return -EPROBE_DEFER;
 67
 68	hubdrv = to_intel_th_driver(hub->dev.driver);
 69
 70	ret = thdrv->probe(to_intel_th_device(dev));
 71	if (ret)
 72		return ret;
 73
 74	if (thdev->type == INTEL_TH_OUTPUT &&
 75	    !intel_th_output_assigned(thdev))
 76		ret = hubdrv->assign(hub, thdev);
 77
 78	return ret;
 79}
 80
 81static int intel_th_remove(struct device *dev)
 82{
 83	struct intel_th_driver *thdrv = to_intel_th_driver(dev->driver);
 84	struct intel_th_device *thdev = to_intel_th_device(dev);
 85	struct intel_th_device *hub = to_intel_th_device(dev->parent);
 86	int err;
 87
 88	if (thdev->type == INTEL_TH_SWITCH) {
 89		err = device_for_each_child(dev, thdev, intel_th_child_remove);
 90		if (err)
 91			return err;
 92	}
 93
 94	thdrv->remove(thdev);
 95
 96	if (intel_th_output_assigned(thdev)) {
 97		struct intel_th_driver *hubdrv =
 98			to_intel_th_driver(dev->parent->driver);
 99
100		if (hub->dev.driver)
101			hubdrv->unassign(hub, thdev);
102	}
103
104	return 0;
105}
106
107static struct bus_type intel_th_bus = {
108	.name		= "intel_th",
109	.dev_attrs	= NULL,
110	.match		= intel_th_match,
111	.probe		= intel_th_probe,
112	.remove		= intel_th_remove,
113};
114
115static void intel_th_device_free(struct intel_th_device *thdev);
116
117static void intel_th_device_release(struct device *dev)
118{
119	intel_th_device_free(to_intel_th_device(dev));
120}
121
122static struct device_type intel_th_source_device_type = {
123	.name		= "intel_th_source_device",
124	.release	= intel_th_device_release,
125};
126
127static struct intel_th *to_intel_th(struct intel_th_device *thdev)
128{
129	/*
130	 * subdevice tree is flat: if this one is not a switch, its
131	 * parent must be
132	 */
133	if (thdev->type != INTEL_TH_SWITCH)
134		thdev = to_intel_th_hub(thdev);
135
136	if (WARN_ON_ONCE(!thdev || thdev->type != INTEL_TH_SWITCH))
137		return NULL;
138
139	return dev_get_drvdata(thdev->dev.parent);
140}
141
142static char *intel_th_output_devnode(struct device *dev, umode_t *mode,
143				     kuid_t *uid, kgid_t *gid)
144{
145	struct intel_th_device *thdev = to_intel_th_device(dev);
146	struct intel_th *th = to_intel_th(thdev);
147	char *node;
148
149	if (thdev->id >= 0)
150		node = kasprintf(GFP_KERNEL, "intel_th%d/%s%d", th->id,
151				 thdev->name, thdev->id);
152	else
153		node = kasprintf(GFP_KERNEL, "intel_th%d/%s", th->id,
154				 thdev->name);
155
156	return node;
157}
158
159static ssize_t port_show(struct device *dev, struct device_attribute *attr,
160			 char *buf)
161{
162	struct intel_th_device *thdev = to_intel_th_device(dev);
163
164	if (thdev->output.port >= 0)
165		return scnprintf(buf, PAGE_SIZE, "%u\n", thdev->output.port);
166
167	return scnprintf(buf, PAGE_SIZE, "unassigned\n");
168}
169
170static DEVICE_ATTR_RO(port);
171
172static int intel_th_output_activate(struct intel_th_device *thdev)
173{
174	struct intel_th_driver *thdrv = to_intel_th_driver(thdev->dev.driver);
175
176	if (thdrv->activate)
177		return thdrv->activate(thdev);
178
179	intel_th_trace_enable(thdev);
180
181	return 0;
182}
183
184static void intel_th_output_deactivate(struct intel_th_device *thdev)
185{
186	struct intel_th_driver *thdrv = to_intel_th_driver(thdev->dev.driver);
187
188	if (thdrv->deactivate)
189		thdrv->deactivate(thdev);
190	else
191		intel_th_trace_disable(thdev);
192}
193
194static ssize_t active_show(struct device *dev, struct device_attribute *attr,
195			   char *buf)
196{
197	struct intel_th_device *thdev = to_intel_th_device(dev);
198
199	return scnprintf(buf, PAGE_SIZE, "%d\n", thdev->output.active);
200}
201
202static ssize_t active_store(struct device *dev, struct device_attribute *attr,
203			    const char *buf, size_t size)
204{
205	struct intel_th_device *thdev = to_intel_th_device(dev);
206	unsigned long val;
207	int ret;
208
209	ret = kstrtoul(buf, 10, &val);
210	if (ret)
211		return ret;
212
213	if (!!val != thdev->output.active) {
214		if (val)
215			ret = intel_th_output_activate(thdev);
216		else
217			intel_th_output_deactivate(thdev);
218	}
219
220	return ret ? ret : size;
221}
222
223static DEVICE_ATTR_RW(active);
224
225static struct attribute *intel_th_output_attrs[] = {
226	&dev_attr_port.attr,
227	&dev_attr_active.attr,
228	NULL,
229};
230
231ATTRIBUTE_GROUPS(intel_th_output);
232
233static struct device_type intel_th_output_device_type = {
234	.name		= "intel_th_output_device",
235	.groups		= intel_th_output_groups,
236	.release	= intel_th_device_release,
237	.devnode	= intel_th_output_devnode,
238};
239
240static struct device_type intel_th_switch_device_type = {
241	.name		= "intel_th_switch_device",
242	.release	= intel_th_device_release,
243};
244
245static struct device_type *intel_th_device_type[] = {
246	[INTEL_TH_SOURCE]	= &intel_th_source_device_type,
247	[INTEL_TH_OUTPUT]	= &intel_th_output_device_type,
248	[INTEL_TH_SWITCH]	= &intel_th_switch_device_type,
249};
250
251int intel_th_driver_register(struct intel_th_driver *thdrv)
252{
253	if (!thdrv->probe || !thdrv->remove)
254		return -EINVAL;
255
256	thdrv->driver.bus = &intel_th_bus;
257
258	return driver_register(&thdrv->driver);
259}
260EXPORT_SYMBOL_GPL(intel_th_driver_register);
261
262void intel_th_driver_unregister(struct intel_th_driver *thdrv)
263{
264	driver_unregister(&thdrv->driver);
265}
266EXPORT_SYMBOL_GPL(intel_th_driver_unregister);
267
268static struct intel_th_device *
269intel_th_device_alloc(struct intel_th *th, unsigned int type, const char *name,
270		      int id)
271{
272	struct device *parent;
273	struct intel_th_device *thdev;
274
275	if (type == INTEL_TH_SWITCH)
276		parent = th->dev;
277	else
278		parent = &th->hub->dev;
279
280	thdev = kzalloc(sizeof(*thdev) + strlen(name) + 1, GFP_KERNEL);
281	if (!thdev)
282		return NULL;
283
284	thdev->id = id;
285	thdev->type = type;
286
287	strcpy(thdev->name, name);
288	device_initialize(&thdev->dev);
289	thdev->dev.bus = &intel_th_bus;
290	thdev->dev.type = intel_th_device_type[type];
291	thdev->dev.parent = parent;
292	thdev->dev.dma_mask = parent->dma_mask;
293	thdev->dev.dma_parms = parent->dma_parms;
294	dma_set_coherent_mask(&thdev->dev, parent->coherent_dma_mask);
295	if (id >= 0)
296		dev_set_name(&thdev->dev, "%d-%s%d", th->id, name, id);
297	else
298		dev_set_name(&thdev->dev, "%d-%s", th->id, name);
299
300	return thdev;
301}
302
303static int intel_th_device_add_resources(struct intel_th_device *thdev,
304					 struct resource *res, int nres)
305{
306	struct resource *r;
307
308	r = kmemdup(res, sizeof(*res) * nres, GFP_KERNEL);
309	if (!r)
310		return -ENOMEM;
311
312	thdev->resource = r;
313	thdev->num_resources = nres;
314
315	return 0;
316}
317
318static void intel_th_device_remove(struct intel_th_device *thdev)
319{
320	device_del(&thdev->dev);
321	put_device(&thdev->dev);
322}
323
324static void intel_th_device_free(struct intel_th_device *thdev)
325{
326	kfree(thdev->resource);
327	kfree(thdev);
328}
329
330/*
331 * Intel(R) Trace Hub subdevices
332 */
333static struct intel_th_subdevice {
334	const char		*name;
335	struct resource		res[3];
336	unsigned		nres;
337	unsigned		type;
338	unsigned		otype;
339	unsigned		scrpd;
340	int			id;
341} intel_th_subdevices[TH_SUBDEVICE_MAX] = {
342	{
343		.nres	= 1,
344		.res	= {
345			{
346				.start	= REG_GTH_OFFSET,
347				.end	= REG_GTH_OFFSET + REG_GTH_LENGTH - 1,
348				.flags	= IORESOURCE_MEM,
349			},
350		},
351		.name	= "gth",
352		.type	= INTEL_TH_SWITCH,
353		.id	= -1,
354	},
355	{
356		.nres	= 2,
357		.res	= {
358			{
359				.start	= REG_MSU_OFFSET,
360				.end	= REG_MSU_OFFSET + REG_MSU_LENGTH - 1,
361				.flags	= IORESOURCE_MEM,
362			},
363			{
364				.start	= BUF_MSU_OFFSET,
365				.end	= BUF_MSU_OFFSET + BUF_MSU_LENGTH - 1,
366				.flags	= IORESOURCE_MEM,
367			},
368		},
369		.name	= "msc",
370		.id	= 0,
371		.type	= INTEL_TH_OUTPUT,
372		.otype	= GTH_MSU,
373		.scrpd	= SCRPD_MEM_IS_PRIM_DEST | SCRPD_MSC0_IS_ENABLED,
374	},
375	{
376		.nres	= 2,
377		.res	= {
378			{
379				.start	= REG_MSU_OFFSET,
380				.end	= REG_MSU_OFFSET + REG_MSU_LENGTH - 1,
381				.flags	= IORESOURCE_MEM,
382			},
383			{
384				.start	= BUF_MSU_OFFSET,
385				.end	= BUF_MSU_OFFSET + BUF_MSU_LENGTH - 1,
386				.flags	= IORESOURCE_MEM,
387			},
388		},
389		.name	= "msc",
390		.id	= 1,
391		.type	= INTEL_TH_OUTPUT,
392		.otype	= GTH_MSU,
393		.scrpd	= SCRPD_MEM_IS_PRIM_DEST | SCRPD_MSC1_IS_ENABLED,
394	},
395	{
396		.nres	= 2,
397		.res	= {
398			{
399				.start	= REG_STH_OFFSET,
400				.end	= REG_STH_OFFSET + REG_STH_LENGTH - 1,
401				.flags	= IORESOURCE_MEM,
402			},
403			{
404				.start	= TH_MMIO_SW,
405				.end	= 0,
406				.flags	= IORESOURCE_MEM,
407			},
408		},
409		.id	= -1,
410		.name	= "sth",
411		.type	= INTEL_TH_SOURCE,
412	},
413	{
414		.nres	= 1,
415		.res	= {
416			{
417				.start	= REG_PTI_OFFSET,
418				.end	= REG_PTI_OFFSET + REG_PTI_LENGTH - 1,
419				.flags	= IORESOURCE_MEM,
420			},
421		},
422		.id	= -1,
423		.name	= "pti",
424		.type	= INTEL_TH_OUTPUT,
425		.otype	= GTH_PTI,
426		.scrpd	= SCRPD_PTI_IS_PRIM_DEST,
427	},
428	{
429		.nres	= 1,
430		.res	= {
431			{
432				.start	= REG_DCIH_OFFSET,
433				.end	= REG_DCIH_OFFSET + REG_DCIH_LENGTH - 1,
434				.flags	= IORESOURCE_MEM,
435			},
436		},
437		.id	= -1,
438		.name	= "dcih",
439		.type	= INTEL_TH_OUTPUT,
440	},
441};
442
443static int intel_th_populate(struct intel_th *th, struct resource *devres,
444			     unsigned int ndevres, int irq)
445{
446	struct resource res[3];
447	unsigned int req = 0;
448	int i, err;
449
450	/* create devices for each intel_th_subdevice */
451	for (i = 0; i < ARRAY_SIZE(intel_th_subdevices); i++) {
452		struct intel_th_subdevice *subdev = &intel_th_subdevices[i];
453		struct intel_th_device *thdev;
454		int r;
455
456		thdev = intel_th_device_alloc(th, subdev->type, subdev->name,
457					      subdev->id);
458		if (!thdev) {
459			err = -ENOMEM;
460			goto kill_subdevs;
461		}
462
463		memcpy(res, subdev->res,
464		       sizeof(struct resource) * subdev->nres);
465
466		for (r = 0; r < subdev->nres; r++) {
467			int bar = TH_MMIO_CONFIG;
468
469			/*
470			 * Take .end == 0 to mean 'take the whole bar',
471			 * .start then tells us which bar it is. Default to
472			 * TH_MMIO_CONFIG.
473			 */
474			if (!res[r].end && res[r].flags == IORESOURCE_MEM) {
475				bar = res[r].start;
476				res[r].start = 0;
477				res[r].end = resource_size(&devres[bar]) - 1;
478			}
479
480			if (res[r].flags & IORESOURCE_MEM) {
481				res[r].start	+= devres[bar].start;
482				res[r].end	+= devres[bar].start;
483
484				dev_dbg(th->dev, "%s:%d @ %pR\n",
485					subdev->name, r, &res[r]);
486			} else if (res[r].flags & IORESOURCE_IRQ) {
487				res[r].start	= irq;
488			}
489		}
490
491		err = intel_th_device_add_resources(thdev, res, subdev->nres);
492		if (err) {
493			put_device(&thdev->dev);
494			goto kill_subdevs;
495		}
496
497		if (subdev->type == INTEL_TH_OUTPUT) {
498			thdev->dev.devt = MKDEV(th->major, i);
499			thdev->output.type = subdev->otype;
500			thdev->output.port = -1;
501			thdev->output.scratchpad = subdev->scrpd;
502		}
503
504		err = device_add(&thdev->dev);
505		if (err) {
506			put_device(&thdev->dev);
507			goto kill_subdevs;
508		}
509
510		/* need switch driver to be loaded to enumerate the rest */
511		if (subdev->type == INTEL_TH_SWITCH && !req) {
512			th->hub = thdev;
513			err = request_module("intel_th_%s", subdev->name);
514			if (!err)
515				req++;
516		}
517
518		th->thdev[i] = thdev;
519	}
520
521	return 0;
522
523kill_subdevs:
524	for (i-- ; i >= 0; i--)
525		intel_th_device_remove(th->thdev[i]);
526
527	return err;
528}
529
530static int match_devt(struct device *dev, void *data)
531{
532	dev_t devt = (dev_t)(unsigned long)data;
533
534	return dev->devt == devt;
535}
536
537static int intel_th_output_open(struct inode *inode, struct file *file)
538{
539	const struct file_operations *fops;
540	struct intel_th_driver *thdrv;
541	struct device *dev;
542	int err;
543
544	dev = bus_find_device(&intel_th_bus, NULL,
545			      (void *)(unsigned long)inode->i_rdev,
546			      match_devt);
547	if (!dev || !dev->driver)
548		return -ENODEV;
549
550	thdrv = to_intel_th_driver(dev->driver);
551	fops = fops_get(thdrv->fops);
552	if (!fops)
553		return -ENODEV;
554
555	replace_fops(file, fops);
556
557	file->private_data = to_intel_th_device(dev);
558
559	if (file->f_op->open) {
560		err = file->f_op->open(inode, file);
561		return err;
562	}
563
564	return 0;
565}
566
567static const struct file_operations intel_th_output_fops = {
568	.open	= intel_th_output_open,
569	.llseek	= noop_llseek,
570};
571
572/**
573 * intel_th_alloc() - allocate a new Intel TH device and its subdevices
574 * @dev:	parent device
575 * @devres:	parent's resources
576 * @ndevres:	number of resources
577 * @irq:	irq number
578 */
579struct intel_th *
580intel_th_alloc(struct device *dev, struct resource *devres,
581	       unsigned int ndevres, int irq)
582{
583	struct intel_th *th;
584	int err;
585
586	th = kzalloc(sizeof(*th), GFP_KERNEL);
587	if (!th)
588		return ERR_PTR(-ENOMEM);
589
590	th->id = ida_simple_get(&intel_th_ida, 0, 0, GFP_KERNEL);
591	if (th->id < 0) {
592		err = th->id;
593		goto err_alloc;
594	}
595
596	th->major = __register_chrdev(0, 0, TH_POSSIBLE_OUTPUTS,
597				      "intel_th/output", &intel_th_output_fops);
598	if (th->major < 0) {
599		err = th->major;
600		goto err_ida;
601	}
602	th->dev = dev;
603
604	dev_set_drvdata(dev, th);
605
606	err = intel_th_populate(th, devres, ndevres, irq);
607	if (err)
608		goto err_chrdev;
609
610	return th;
611
612err_chrdev:
613	__unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS,
614			    "intel_th/output");
615
616err_ida:
617	ida_simple_remove(&intel_th_ida, th->id);
618
619err_alloc:
620	kfree(th);
621
622	return ERR_PTR(err);
623}
624EXPORT_SYMBOL_GPL(intel_th_alloc);
625
626void intel_th_free(struct intel_th *th)
627{
628	int i;
629
630	for (i = 0; i < TH_SUBDEVICE_MAX; i++)
631		if (th->thdev[i] != th->hub)
632			intel_th_device_remove(th->thdev[i]);
633
634	intel_th_device_remove(th->hub);
635
636	__unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS,
637			    "intel_th/output");
638
639	ida_simple_remove(&intel_th_ida, th->id);
640
641	kfree(th);
642}
643EXPORT_SYMBOL_GPL(intel_th_free);
644
645/**
646 * intel_th_trace_enable() - enable tracing for an output device
647 * @thdev:	output device that requests tracing be enabled
648 */
649int intel_th_trace_enable(struct intel_th_device *thdev)
650{
651	struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent);
652	struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver);
653
654	if (WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH))
655		return -EINVAL;
656
657	if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT))
658		return -EINVAL;
659
660	hubdrv->enable(hub, &thdev->output);
661
662	return 0;
663}
664EXPORT_SYMBOL_GPL(intel_th_trace_enable);
665
666/**
667 * intel_th_trace_disable() - disable tracing for an output device
668 * @thdev:	output device that requests tracing be disabled
669 */
670int intel_th_trace_disable(struct intel_th_device *thdev)
671{
672	struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent);
673	struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver);
674
675	WARN_ON_ONCE(hub->type != INTEL_TH_SWITCH);
676	if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT))
677		return -EINVAL;
678
679	hubdrv->disable(hub, &thdev->output);
680
681	return 0;
682}
683EXPORT_SYMBOL_GPL(intel_th_trace_disable);
684
685int intel_th_set_output(struct intel_th_device *thdev,
686			unsigned int master)
687{
688	struct intel_th_device *hub = to_intel_th_device(thdev->dev.parent);
689	struct intel_th_driver *hubdrv = to_intel_th_driver(hub->dev.driver);
690
691	if (!hubdrv->set_output)
692		return -ENOTSUPP;
693
694	return hubdrv->set_output(hub, master);
695}
696EXPORT_SYMBOL_GPL(intel_th_set_output);
697
698static int __init intel_th_init(void)
699{
700	intel_th_debug_init();
701
702	return bus_register(&intel_th_bus);
703}
704subsys_initcall(intel_th_init);
705
706static void __exit intel_th_exit(void)
707{
708	intel_th_debug_done();
709
710	bus_unregister(&intel_th_bus);
711}
712module_exit(intel_th_exit);
713
714MODULE_LICENSE("GPL v2");
715MODULE_DESCRIPTION("Intel(R) Trace Hub controller driver");
716MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");