114cdbf04SAlexander Shishkin /* 214cdbf04SAlexander Shishkin * Intel(R) Trace Hub PTI output driver 314cdbf04SAlexander Shishkin * 4*f77d22bcSAlexander Shishkin * Copyright (C) 2014-2016 Intel Corporation. 514cdbf04SAlexander Shishkin * 614cdbf04SAlexander Shishkin * This program is free software; you can redistribute it and/or modify it 714cdbf04SAlexander Shishkin * under the terms and conditions of the GNU General Public License, 814cdbf04SAlexander Shishkin * version 2, as published by the Free Software Foundation. 914cdbf04SAlexander Shishkin * 1014cdbf04SAlexander Shishkin * This program is distributed in the hope it will be useful, but WITHOUT 1114cdbf04SAlexander Shishkin * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1214cdbf04SAlexander Shishkin * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 1314cdbf04SAlexander Shishkin * more details. 1414cdbf04SAlexander Shishkin */ 1514cdbf04SAlexander Shishkin 1614cdbf04SAlexander Shishkin #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1714cdbf04SAlexander Shishkin 1814cdbf04SAlexander Shishkin #include <linux/types.h> 1914cdbf04SAlexander Shishkin #include <linux/module.h> 2014cdbf04SAlexander Shishkin #include <linux/device.h> 2114cdbf04SAlexander Shishkin #include <linux/sizes.h> 2214cdbf04SAlexander Shishkin #include <linux/printk.h> 2314cdbf04SAlexander Shishkin #include <linux/slab.h> 2414cdbf04SAlexander Shishkin #include <linux/mm.h> 2514cdbf04SAlexander Shishkin #include <linux/io.h> 2614cdbf04SAlexander Shishkin 2714cdbf04SAlexander Shishkin #include "intel_th.h" 2814cdbf04SAlexander Shishkin #include "pti.h" 2914cdbf04SAlexander Shishkin 3014cdbf04SAlexander Shishkin struct pti_device { 3114cdbf04SAlexander Shishkin void __iomem *base; 3214cdbf04SAlexander Shishkin struct intel_th_device *thdev; 3314cdbf04SAlexander Shishkin unsigned int mode; 3414cdbf04SAlexander Shishkin unsigned int freeclk; 3514cdbf04SAlexander Shishkin unsigned int clkdiv; 3614cdbf04SAlexander Shishkin unsigned int patgen; 37*f77d22bcSAlexander Shishkin unsigned int lpp_dest_mask; 38*f77d22bcSAlexander Shishkin unsigned int lpp_dest; 3914cdbf04SAlexander Shishkin }; 4014cdbf04SAlexander Shishkin 4114cdbf04SAlexander Shishkin /* map PTI widths to MODE settings of PTI_CTL register */ 4214cdbf04SAlexander Shishkin static const unsigned int pti_mode[] = { 4314cdbf04SAlexander Shishkin 0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 4414cdbf04SAlexander Shishkin }; 4514cdbf04SAlexander Shishkin 4614cdbf04SAlexander Shishkin static int pti_width_mode(unsigned int width) 4714cdbf04SAlexander Shishkin { 4814cdbf04SAlexander Shishkin int i; 4914cdbf04SAlexander Shishkin 5014cdbf04SAlexander Shishkin for (i = 0; i < ARRAY_SIZE(pti_mode); i++) 5114cdbf04SAlexander Shishkin if (pti_mode[i] == width) 5214cdbf04SAlexander Shishkin return i; 5314cdbf04SAlexander Shishkin 5414cdbf04SAlexander Shishkin return -EINVAL; 5514cdbf04SAlexander Shishkin } 5614cdbf04SAlexander Shishkin 5714cdbf04SAlexander Shishkin static ssize_t mode_show(struct device *dev, struct device_attribute *attr, 5814cdbf04SAlexander Shishkin char *buf) 5914cdbf04SAlexander Shishkin { 6014cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 6114cdbf04SAlexander Shishkin 6214cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]); 6314cdbf04SAlexander Shishkin } 6414cdbf04SAlexander Shishkin 6514cdbf04SAlexander Shishkin static ssize_t mode_store(struct device *dev, struct device_attribute *attr, 6614cdbf04SAlexander Shishkin const char *buf, size_t size) 6714cdbf04SAlexander Shishkin { 6814cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 6914cdbf04SAlexander Shishkin unsigned long val; 7014cdbf04SAlexander Shishkin int ret; 7114cdbf04SAlexander Shishkin 7214cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val); 7314cdbf04SAlexander Shishkin if (ret) 7414cdbf04SAlexander Shishkin return ret; 7514cdbf04SAlexander Shishkin 7614cdbf04SAlexander Shishkin ret = pti_width_mode(val); 7714cdbf04SAlexander Shishkin if (ret < 0) 7814cdbf04SAlexander Shishkin return ret; 7914cdbf04SAlexander Shishkin 8014cdbf04SAlexander Shishkin pti->mode = ret; 8114cdbf04SAlexander Shishkin 8214cdbf04SAlexander Shishkin return size; 8314cdbf04SAlexander Shishkin } 8414cdbf04SAlexander Shishkin 8514cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(mode); 8614cdbf04SAlexander Shishkin 8714cdbf04SAlexander Shishkin static ssize_t 8814cdbf04SAlexander Shishkin freerunning_clock_show(struct device *dev, struct device_attribute *attr, 8914cdbf04SAlexander Shishkin char *buf) 9014cdbf04SAlexander Shishkin { 9114cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 9214cdbf04SAlexander Shishkin 9314cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk); 9414cdbf04SAlexander Shishkin } 9514cdbf04SAlexander Shishkin 9614cdbf04SAlexander Shishkin static ssize_t 9714cdbf04SAlexander Shishkin freerunning_clock_store(struct device *dev, struct device_attribute *attr, 9814cdbf04SAlexander Shishkin const char *buf, size_t size) 9914cdbf04SAlexander Shishkin { 10014cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 10114cdbf04SAlexander Shishkin unsigned long val; 10214cdbf04SAlexander Shishkin int ret; 10314cdbf04SAlexander Shishkin 10414cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val); 10514cdbf04SAlexander Shishkin if (ret) 10614cdbf04SAlexander Shishkin return ret; 10714cdbf04SAlexander Shishkin 10814cdbf04SAlexander Shishkin pti->freeclk = !!val; 10914cdbf04SAlexander Shishkin 11014cdbf04SAlexander Shishkin return size; 11114cdbf04SAlexander Shishkin } 11214cdbf04SAlexander Shishkin 11314cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(freerunning_clock); 11414cdbf04SAlexander Shishkin 11514cdbf04SAlexander Shishkin static ssize_t 11614cdbf04SAlexander Shishkin clock_divider_show(struct device *dev, struct device_attribute *attr, 11714cdbf04SAlexander Shishkin char *buf) 11814cdbf04SAlexander Shishkin { 11914cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 12014cdbf04SAlexander Shishkin 12114cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv); 12214cdbf04SAlexander Shishkin } 12314cdbf04SAlexander Shishkin 12414cdbf04SAlexander Shishkin static ssize_t 12514cdbf04SAlexander Shishkin clock_divider_store(struct device *dev, struct device_attribute *attr, 12614cdbf04SAlexander Shishkin const char *buf, size_t size) 12714cdbf04SAlexander Shishkin { 12814cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 12914cdbf04SAlexander Shishkin unsigned long val; 13014cdbf04SAlexander Shishkin int ret; 13114cdbf04SAlexander Shishkin 13214cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val); 13314cdbf04SAlexander Shishkin if (ret) 13414cdbf04SAlexander Shishkin return ret; 13514cdbf04SAlexander Shishkin 13614cdbf04SAlexander Shishkin if (!is_power_of_2(val) || val > 8 || !val) 13714cdbf04SAlexander Shishkin return -EINVAL; 13814cdbf04SAlexander Shishkin 13914cdbf04SAlexander Shishkin pti->clkdiv = val; 14014cdbf04SAlexander Shishkin 14114cdbf04SAlexander Shishkin return size; 14214cdbf04SAlexander Shishkin } 14314cdbf04SAlexander Shishkin 14414cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(clock_divider); 14514cdbf04SAlexander Shishkin 14614cdbf04SAlexander Shishkin static struct attribute *pti_output_attrs[] = { 14714cdbf04SAlexander Shishkin &dev_attr_mode.attr, 14814cdbf04SAlexander Shishkin &dev_attr_freerunning_clock.attr, 14914cdbf04SAlexander Shishkin &dev_attr_clock_divider.attr, 15014cdbf04SAlexander Shishkin NULL, 15114cdbf04SAlexander Shishkin }; 15214cdbf04SAlexander Shishkin 15314cdbf04SAlexander Shishkin static struct attribute_group pti_output_group = { 15414cdbf04SAlexander Shishkin .attrs = pti_output_attrs, 15514cdbf04SAlexander Shishkin }; 15614cdbf04SAlexander Shishkin 15714cdbf04SAlexander Shishkin static int intel_th_pti_activate(struct intel_th_device *thdev) 15814cdbf04SAlexander Shishkin { 15914cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(&thdev->dev); 16014cdbf04SAlexander Shishkin u32 ctl = PTI_EN; 16114cdbf04SAlexander Shishkin 16214cdbf04SAlexander Shishkin if (pti->patgen) 16314cdbf04SAlexander Shishkin ctl |= pti->patgen << __ffs(PTI_PATGENMODE); 16414cdbf04SAlexander Shishkin if (pti->freeclk) 16514cdbf04SAlexander Shishkin ctl |= PTI_FCEN; 16614cdbf04SAlexander Shishkin ctl |= pti->mode << __ffs(PTI_MODE); 16714cdbf04SAlexander Shishkin ctl |= pti->clkdiv << __ffs(PTI_CLKDIV); 168*f77d22bcSAlexander Shishkin ctl |= pti->lpp_dest << __ffs(LPP_DEST); 16914cdbf04SAlexander Shishkin 17014cdbf04SAlexander Shishkin iowrite32(ctl, pti->base + REG_PTI_CTL); 17114cdbf04SAlexander Shishkin 17214cdbf04SAlexander Shishkin intel_th_trace_enable(thdev); 17314cdbf04SAlexander Shishkin 17414cdbf04SAlexander Shishkin return 0; 17514cdbf04SAlexander Shishkin } 17614cdbf04SAlexander Shishkin 17714cdbf04SAlexander Shishkin static void intel_th_pti_deactivate(struct intel_th_device *thdev) 17814cdbf04SAlexander Shishkin { 17914cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(&thdev->dev); 18014cdbf04SAlexander Shishkin 18114cdbf04SAlexander Shishkin intel_th_trace_disable(thdev); 18214cdbf04SAlexander Shishkin 18314cdbf04SAlexander Shishkin iowrite32(0, pti->base + REG_PTI_CTL); 18414cdbf04SAlexander Shishkin } 18514cdbf04SAlexander Shishkin 18614cdbf04SAlexander Shishkin static void read_hw_config(struct pti_device *pti) 18714cdbf04SAlexander Shishkin { 18814cdbf04SAlexander Shishkin u32 ctl = ioread32(pti->base + REG_PTI_CTL); 18914cdbf04SAlexander Shishkin 19014cdbf04SAlexander Shishkin pti->mode = (ctl & PTI_MODE) >> __ffs(PTI_MODE); 19114cdbf04SAlexander Shishkin pti->clkdiv = (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV); 19214cdbf04SAlexander Shishkin pti->freeclk = !!(ctl & PTI_FCEN); 19314cdbf04SAlexander Shishkin 19414cdbf04SAlexander Shishkin if (!pti_mode[pti->mode]) 19514cdbf04SAlexander Shishkin pti->mode = pti_width_mode(4); 19614cdbf04SAlexander Shishkin if (!pti->clkdiv) 19714cdbf04SAlexander Shishkin pti->clkdiv = 1; 198*f77d22bcSAlexander Shishkin 199*f77d22bcSAlexander Shishkin if (pti->thdev->output.type == GTH_LPP) { 200*f77d22bcSAlexander Shishkin if (ctl & LPP_PTIPRESENT) 201*f77d22bcSAlexander Shishkin pti->lpp_dest_mask |= LPP_DEST_PTI; 202*f77d22bcSAlexander Shishkin if (ctl & LPP_BSSBPRESENT) 203*f77d22bcSAlexander Shishkin pti->lpp_dest_mask |= LPP_DEST_EXI; 204*f77d22bcSAlexander Shishkin if (ctl & LPP_DEST) 205*f77d22bcSAlexander Shishkin pti->lpp_dest = 1; 206*f77d22bcSAlexander Shishkin } 20714cdbf04SAlexander Shishkin } 20814cdbf04SAlexander Shishkin 20914cdbf04SAlexander Shishkin static int intel_th_pti_probe(struct intel_th_device *thdev) 21014cdbf04SAlexander Shishkin { 21114cdbf04SAlexander Shishkin struct device *dev = &thdev->dev; 21214cdbf04SAlexander Shishkin struct resource *res; 21314cdbf04SAlexander Shishkin struct pti_device *pti; 21414cdbf04SAlexander Shishkin void __iomem *base; 21514cdbf04SAlexander Shishkin 21614cdbf04SAlexander Shishkin res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0); 21714cdbf04SAlexander Shishkin if (!res) 21814cdbf04SAlexander Shishkin return -ENODEV; 21914cdbf04SAlexander Shishkin 22014cdbf04SAlexander Shishkin base = devm_ioremap(dev, res->start, resource_size(res)); 22173061da0SDan Carpenter if (!base) 22273061da0SDan Carpenter return -ENOMEM; 22314cdbf04SAlexander Shishkin 22414cdbf04SAlexander Shishkin pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL); 22514cdbf04SAlexander Shishkin if (!pti) 22614cdbf04SAlexander Shishkin return -ENOMEM; 22714cdbf04SAlexander Shishkin 22814cdbf04SAlexander Shishkin pti->thdev = thdev; 22914cdbf04SAlexander Shishkin pti->base = base; 23014cdbf04SAlexander Shishkin 23114cdbf04SAlexander Shishkin read_hw_config(pti); 23214cdbf04SAlexander Shishkin 23314cdbf04SAlexander Shishkin dev_set_drvdata(dev, pti); 23414cdbf04SAlexander Shishkin 23514cdbf04SAlexander Shishkin return 0; 23614cdbf04SAlexander Shishkin } 23714cdbf04SAlexander Shishkin 23814cdbf04SAlexander Shishkin static void intel_th_pti_remove(struct intel_th_device *thdev) 23914cdbf04SAlexander Shishkin { 24014cdbf04SAlexander Shishkin } 24114cdbf04SAlexander Shishkin 24214cdbf04SAlexander Shishkin static struct intel_th_driver intel_th_pti_driver = { 24314cdbf04SAlexander Shishkin .probe = intel_th_pti_probe, 24414cdbf04SAlexander Shishkin .remove = intel_th_pti_remove, 24514cdbf04SAlexander Shishkin .activate = intel_th_pti_activate, 24614cdbf04SAlexander Shishkin .deactivate = intel_th_pti_deactivate, 247e8644e4cSAlexander Shishkin .attr_group = &pti_output_group, 24814cdbf04SAlexander Shishkin .driver = { 24914cdbf04SAlexander Shishkin .name = "pti", 25014cdbf04SAlexander Shishkin .owner = THIS_MODULE, 25114cdbf04SAlexander Shishkin }, 25214cdbf04SAlexander Shishkin }; 25314cdbf04SAlexander Shishkin 254*f77d22bcSAlexander Shishkin static const char * const lpp_dest_str[] = { "pti", "exi" }; 255*f77d22bcSAlexander Shishkin 256*f77d22bcSAlexander Shishkin static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr, 257*f77d22bcSAlexander Shishkin char *buf) 258*f77d22bcSAlexander Shishkin { 259*f77d22bcSAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 260*f77d22bcSAlexander Shishkin ssize_t ret = 0; 261*f77d22bcSAlexander Shishkin int i; 262*f77d22bcSAlexander Shishkin 263*f77d22bcSAlexander Shishkin for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) { 264*f77d22bcSAlexander Shishkin const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s "; 265*f77d22bcSAlexander Shishkin 266*f77d22bcSAlexander Shishkin if (!(pti->lpp_dest_mask & BIT(i))) 267*f77d22bcSAlexander Shishkin continue; 268*f77d22bcSAlexander Shishkin 269*f77d22bcSAlexander Shishkin ret += scnprintf(buf + ret, PAGE_SIZE - ret, 270*f77d22bcSAlexander Shishkin fmt, lpp_dest_str[i]); 271*f77d22bcSAlexander Shishkin } 272*f77d22bcSAlexander Shishkin 273*f77d22bcSAlexander Shishkin if (ret) 274*f77d22bcSAlexander Shishkin buf[ret - 1] = '\n'; 275*f77d22bcSAlexander Shishkin 276*f77d22bcSAlexander Shishkin return ret; 277*f77d22bcSAlexander Shishkin } 278*f77d22bcSAlexander Shishkin 279*f77d22bcSAlexander Shishkin static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr, 280*f77d22bcSAlexander Shishkin const char *buf, size_t size) 281*f77d22bcSAlexander Shishkin { 282*f77d22bcSAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 283*f77d22bcSAlexander Shishkin ssize_t ret = -EINVAL; 284*f77d22bcSAlexander Shishkin int i; 285*f77d22bcSAlexander Shishkin 286*f77d22bcSAlexander Shishkin for (i = 0; i < ARRAY_SIZE(lpp_dest_str); i++) 287*f77d22bcSAlexander Shishkin if (sysfs_streq(buf, lpp_dest_str[i])) 288*f77d22bcSAlexander Shishkin break; 289*f77d22bcSAlexander Shishkin 290*f77d22bcSAlexander Shishkin if (i < ARRAY_SIZE(lpp_dest_str) && pti->lpp_dest_mask & BIT(i)) { 291*f77d22bcSAlexander Shishkin pti->lpp_dest = i; 292*f77d22bcSAlexander Shishkin ret = size; 293*f77d22bcSAlexander Shishkin } 294*f77d22bcSAlexander Shishkin 295*f77d22bcSAlexander Shishkin return ret; 296*f77d22bcSAlexander Shishkin } 297*f77d22bcSAlexander Shishkin 298*f77d22bcSAlexander Shishkin static DEVICE_ATTR_RW(lpp_dest); 299*f77d22bcSAlexander Shishkin 300*f77d22bcSAlexander Shishkin static struct attribute *lpp_output_attrs[] = { 301*f77d22bcSAlexander Shishkin &dev_attr_mode.attr, 302*f77d22bcSAlexander Shishkin &dev_attr_freerunning_clock.attr, 303*f77d22bcSAlexander Shishkin &dev_attr_clock_divider.attr, 304*f77d22bcSAlexander Shishkin &dev_attr_lpp_dest.attr, 305*f77d22bcSAlexander Shishkin NULL, 306*f77d22bcSAlexander Shishkin }; 307*f77d22bcSAlexander Shishkin 308*f77d22bcSAlexander Shishkin static struct attribute_group lpp_output_group = { 309*f77d22bcSAlexander Shishkin .attrs = lpp_output_attrs, 310*f77d22bcSAlexander Shishkin }; 311*f77d22bcSAlexander Shishkin 312*f77d22bcSAlexander Shishkin static struct intel_th_driver intel_th_lpp_driver = { 313*f77d22bcSAlexander Shishkin .probe = intel_th_pti_probe, 314*f77d22bcSAlexander Shishkin .remove = intel_th_pti_remove, 315*f77d22bcSAlexander Shishkin .activate = intel_th_pti_activate, 316*f77d22bcSAlexander Shishkin .deactivate = intel_th_pti_deactivate, 317*f77d22bcSAlexander Shishkin .attr_group = &lpp_output_group, 318*f77d22bcSAlexander Shishkin .driver = { 319*f77d22bcSAlexander Shishkin .name = "lpp", 320*f77d22bcSAlexander Shishkin .owner = THIS_MODULE, 321*f77d22bcSAlexander Shishkin }, 322*f77d22bcSAlexander Shishkin }; 323*f77d22bcSAlexander Shishkin 324*f77d22bcSAlexander Shishkin static int __init intel_th_pti_lpp_init(void) 325*f77d22bcSAlexander Shishkin { 326*f77d22bcSAlexander Shishkin int err; 327*f77d22bcSAlexander Shishkin 328*f77d22bcSAlexander Shishkin err = intel_th_driver_register(&intel_th_pti_driver); 329*f77d22bcSAlexander Shishkin if (err) 330*f77d22bcSAlexander Shishkin return err; 331*f77d22bcSAlexander Shishkin 332*f77d22bcSAlexander Shishkin err = intel_th_driver_register(&intel_th_lpp_driver); 333*f77d22bcSAlexander Shishkin if (err) { 334*f77d22bcSAlexander Shishkin intel_th_driver_unregister(&intel_th_pti_driver); 335*f77d22bcSAlexander Shishkin return err; 336*f77d22bcSAlexander Shishkin } 337*f77d22bcSAlexander Shishkin 338*f77d22bcSAlexander Shishkin return 0; 339*f77d22bcSAlexander Shishkin } 340*f77d22bcSAlexander Shishkin 341*f77d22bcSAlexander Shishkin module_init(intel_th_pti_lpp_init); 342*f77d22bcSAlexander Shishkin 343*f77d22bcSAlexander Shishkin static void __exit intel_th_pti_lpp_exit(void) 344*f77d22bcSAlexander Shishkin { 345*f77d22bcSAlexander Shishkin intel_th_driver_unregister(&intel_th_pti_driver); 346*f77d22bcSAlexander Shishkin intel_th_driver_unregister(&intel_th_lpp_driver); 347*f77d22bcSAlexander Shishkin } 348*f77d22bcSAlexander Shishkin 349*f77d22bcSAlexander Shishkin module_exit(intel_th_pti_lpp_exit); 35014cdbf04SAlexander Shishkin 35114cdbf04SAlexander Shishkin MODULE_LICENSE("GPL v2"); 352*f77d22bcSAlexander Shishkin MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver"); 35314cdbf04SAlexander Shishkin MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); 354