linux/drivers/hwtracing/intel_th/core.c
<<
>>
Prefs
   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>");
 717