150352fa7SAlexander Shishkin // SPDX-License-Identifier: GPL-2.0 214cdbf04SAlexander Shishkin /* 314cdbf04SAlexander Shishkin * Intel(R) Trace Hub PTI output driver 414cdbf04SAlexander Shishkin * 5f77d22bcSAlexander Shishkin * Copyright (C) 2014-2016 Intel Corporation. 614cdbf04SAlexander Shishkin */ 714cdbf04SAlexander Shishkin 814cdbf04SAlexander Shishkin #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 914cdbf04SAlexander Shishkin 1014cdbf04SAlexander Shishkin #include <linux/types.h> 1114cdbf04SAlexander Shishkin #include <linux/module.h> 1214cdbf04SAlexander Shishkin #include <linux/device.h> 1314cdbf04SAlexander Shishkin #include <linux/sizes.h> 1414cdbf04SAlexander Shishkin #include <linux/printk.h> 1514cdbf04SAlexander Shishkin #include <linux/slab.h> 1614cdbf04SAlexander Shishkin #include <linux/mm.h> 1714cdbf04SAlexander Shishkin #include <linux/io.h> 1814cdbf04SAlexander Shishkin 1914cdbf04SAlexander Shishkin #include "intel_th.h" 2014cdbf04SAlexander Shishkin #include "pti.h" 2114cdbf04SAlexander Shishkin 2214cdbf04SAlexander Shishkin struct pti_device { 2314cdbf04SAlexander Shishkin void __iomem *base; 2414cdbf04SAlexander Shishkin struct intel_th_device *thdev; 2514cdbf04SAlexander Shishkin unsigned int mode; 2614cdbf04SAlexander Shishkin unsigned int freeclk; 2714cdbf04SAlexander Shishkin unsigned int clkdiv; 2814cdbf04SAlexander Shishkin unsigned int patgen; 29f77d22bcSAlexander Shishkin unsigned int lpp_dest_mask; 30f77d22bcSAlexander Shishkin unsigned int lpp_dest; 3114cdbf04SAlexander Shishkin }; 3214cdbf04SAlexander Shishkin 3314cdbf04SAlexander Shishkin /* map PTI widths to MODE settings of PTI_CTL register */ 3414cdbf04SAlexander Shishkin static const unsigned int pti_mode[] = { 3514cdbf04SAlexander Shishkin 0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 3614cdbf04SAlexander Shishkin }; 3714cdbf04SAlexander Shishkin 3814cdbf04SAlexander Shishkin static int pti_width_mode(unsigned int width) 3914cdbf04SAlexander Shishkin { 4014cdbf04SAlexander Shishkin int i; 4114cdbf04SAlexander Shishkin 4214cdbf04SAlexander Shishkin for (i = 0; i < ARRAY_SIZE(pti_mode); i++) 4314cdbf04SAlexander Shishkin if (pti_mode[i] == width) 4414cdbf04SAlexander Shishkin return i; 4514cdbf04SAlexander Shishkin 4614cdbf04SAlexander Shishkin return -EINVAL; 4714cdbf04SAlexander Shishkin } 4814cdbf04SAlexander Shishkin 4914cdbf04SAlexander Shishkin static ssize_t mode_show(struct device *dev, struct device_attribute *attr, 5014cdbf04SAlexander Shishkin char *buf) 5114cdbf04SAlexander Shishkin { 5214cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 5314cdbf04SAlexander Shishkin 5414cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]); 5514cdbf04SAlexander Shishkin } 5614cdbf04SAlexander Shishkin 5714cdbf04SAlexander Shishkin static ssize_t mode_store(struct device *dev, struct device_attribute *attr, 5814cdbf04SAlexander Shishkin const char *buf, size_t size) 5914cdbf04SAlexander Shishkin { 6014cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 6114cdbf04SAlexander Shishkin unsigned long val; 6214cdbf04SAlexander Shishkin int ret; 6314cdbf04SAlexander Shishkin 6414cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val); 6514cdbf04SAlexander Shishkin if (ret) 6614cdbf04SAlexander Shishkin return ret; 6714cdbf04SAlexander Shishkin 6814cdbf04SAlexander Shishkin ret = pti_width_mode(val); 6914cdbf04SAlexander Shishkin if (ret < 0) 7014cdbf04SAlexander Shishkin return ret; 7114cdbf04SAlexander Shishkin 7214cdbf04SAlexander Shishkin pti->mode = ret; 7314cdbf04SAlexander Shishkin 7414cdbf04SAlexander Shishkin return size; 7514cdbf04SAlexander Shishkin } 7614cdbf04SAlexander Shishkin 7714cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(mode); 7814cdbf04SAlexander Shishkin 7914cdbf04SAlexander Shishkin static ssize_t 8014cdbf04SAlexander Shishkin freerunning_clock_show(struct device *dev, struct device_attribute *attr, 8114cdbf04SAlexander Shishkin char *buf) 8214cdbf04SAlexander Shishkin { 8314cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 8414cdbf04SAlexander Shishkin 8514cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk); 8614cdbf04SAlexander Shishkin } 8714cdbf04SAlexander Shishkin 8814cdbf04SAlexander Shishkin static ssize_t 8914cdbf04SAlexander Shishkin freerunning_clock_store(struct device *dev, struct device_attribute *attr, 9014cdbf04SAlexander Shishkin const char *buf, size_t size) 9114cdbf04SAlexander Shishkin { 9214cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 9314cdbf04SAlexander Shishkin unsigned long val; 9414cdbf04SAlexander Shishkin int ret; 9514cdbf04SAlexander Shishkin 9614cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val); 9714cdbf04SAlexander Shishkin if (ret) 9814cdbf04SAlexander Shishkin return ret; 9914cdbf04SAlexander Shishkin 10014cdbf04SAlexander Shishkin pti->freeclk = !!val; 10114cdbf04SAlexander Shishkin 10214cdbf04SAlexander Shishkin return size; 10314cdbf04SAlexander Shishkin } 10414cdbf04SAlexander Shishkin 10514cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(freerunning_clock); 10614cdbf04SAlexander Shishkin 10714cdbf04SAlexander Shishkin static ssize_t 10814cdbf04SAlexander Shishkin clock_divider_show(struct device *dev, struct device_attribute *attr, 10914cdbf04SAlexander Shishkin char *buf) 11014cdbf04SAlexander Shishkin { 11114cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 11214cdbf04SAlexander Shishkin 11314cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv); 11414cdbf04SAlexander Shishkin } 11514cdbf04SAlexander Shishkin 11614cdbf04SAlexander Shishkin static ssize_t 11714cdbf04SAlexander Shishkin clock_divider_store(struct device *dev, struct device_attribute *attr, 11814cdbf04SAlexander Shishkin const char *buf, size_t size) 11914cdbf04SAlexander Shishkin { 12014cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 12114cdbf04SAlexander Shishkin unsigned long val; 12214cdbf04SAlexander Shishkin int ret; 12314cdbf04SAlexander Shishkin 12414cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val); 12514cdbf04SAlexander Shishkin if (ret) 12614cdbf04SAlexander Shishkin return ret; 12714cdbf04SAlexander Shishkin 12814cdbf04SAlexander Shishkin if (!is_power_of_2(val) || val > 8 || !val) 12914cdbf04SAlexander Shishkin return -EINVAL; 13014cdbf04SAlexander Shishkin 13114cdbf04SAlexander Shishkin pti->clkdiv = val; 13214cdbf04SAlexander Shishkin 13314cdbf04SAlexander Shishkin return size; 13414cdbf04SAlexander Shishkin } 13514cdbf04SAlexander Shishkin 13614cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(clock_divider); 13714cdbf04SAlexander Shishkin 13814cdbf04SAlexander Shishkin static struct attribute *pti_output_attrs[] = { 13914cdbf04SAlexander Shishkin &dev_attr_mode.attr, 14014cdbf04SAlexander Shishkin &dev_attr_freerunning_clock.attr, 14114cdbf04SAlexander Shishkin &dev_attr_clock_divider.attr, 14214cdbf04SAlexander Shishkin NULL, 14314cdbf04SAlexander Shishkin }; 14414cdbf04SAlexander Shishkin 145*d9962f6fSRikard Falkeborn static const struct attribute_group pti_output_group = { 14614cdbf04SAlexander Shishkin .attrs = pti_output_attrs, 14714cdbf04SAlexander Shishkin }; 14814cdbf04SAlexander Shishkin 14914cdbf04SAlexander Shishkin static int intel_th_pti_activate(struct intel_th_device *thdev) 15014cdbf04SAlexander Shishkin { 15114cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(&thdev->dev); 15214cdbf04SAlexander Shishkin u32 ctl = PTI_EN; 15314cdbf04SAlexander Shishkin 15414cdbf04SAlexander Shishkin if (pti->patgen) 15514cdbf04SAlexander Shishkin ctl |= pti->patgen << __ffs(PTI_PATGENMODE); 15614cdbf04SAlexander Shishkin if (pti->freeclk) 15714cdbf04SAlexander Shishkin ctl |= PTI_FCEN; 15814cdbf04SAlexander Shishkin ctl |= pti->mode << __ffs(PTI_MODE); 15914cdbf04SAlexander Shishkin ctl |= pti->clkdiv << __ffs(PTI_CLKDIV); 160f77d22bcSAlexander Shishkin ctl |= pti->lpp_dest << __ffs(LPP_DEST); 16114cdbf04SAlexander Shishkin 16214cdbf04SAlexander Shishkin iowrite32(ctl, pti->base + REG_PTI_CTL); 16314cdbf04SAlexander Shishkin 16414cdbf04SAlexander Shishkin intel_th_trace_enable(thdev); 16514cdbf04SAlexander Shishkin 16614cdbf04SAlexander Shishkin return 0; 16714cdbf04SAlexander Shishkin } 16814cdbf04SAlexander Shishkin 16914cdbf04SAlexander Shishkin static void intel_th_pti_deactivate(struct intel_th_device *thdev) 17014cdbf04SAlexander Shishkin { 17114cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(&thdev->dev); 17214cdbf04SAlexander Shishkin 17314cdbf04SAlexander Shishkin intel_th_trace_disable(thdev); 17414cdbf04SAlexander Shishkin 17514cdbf04SAlexander Shishkin iowrite32(0, pti->base + REG_PTI_CTL); 17614cdbf04SAlexander Shishkin } 17714cdbf04SAlexander Shishkin 17814cdbf04SAlexander Shishkin static void read_hw_config(struct pti_device *pti) 17914cdbf04SAlexander Shishkin { 18014cdbf04SAlexander Shishkin u32 ctl = ioread32(pti->base + REG_PTI_CTL); 18114cdbf04SAlexander Shishkin 18214cdbf04SAlexander Shishkin pti->mode = (ctl & PTI_MODE) >> __ffs(PTI_MODE); 18314cdbf04SAlexander Shishkin pti->clkdiv = (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV); 18414cdbf04SAlexander Shishkin pti->freeclk = !!(ctl & PTI_FCEN); 18514cdbf04SAlexander Shishkin 18614cdbf04SAlexander Shishkin if (!pti_mode[pti->mode]) 18714cdbf04SAlexander Shishkin pti->mode = pti_width_mode(4); 18814cdbf04SAlexander Shishkin if (!pti->clkdiv) 18914cdbf04SAlexander Shishkin pti->clkdiv = 1; 190f77d22bcSAlexander Shishkin 191f77d22bcSAlexander Shishkin if (pti->thdev->output.type == GTH_LPP) { 192f77d22bcSAlexander Shishkin if (ctl & LPP_PTIPRESENT) 193f77d22bcSAlexander Shishkin pti->lpp_dest_mask |= LPP_DEST_PTI; 194f77d22bcSAlexander Shishkin if (ctl & LPP_BSSBPRESENT) 195f77d22bcSAlexander Shishkin pti->lpp_dest_mask |= LPP_DEST_EXI; 196f77d22bcSAlexander Shishkin if (ctl & LPP_DEST) 197f77d22bcSAlexander Shishkin pti->lpp_dest = 1; 198f77d22bcSAlexander Shishkin } 19914cdbf04SAlexander Shishkin } 20014cdbf04SAlexander Shishkin 20114cdbf04SAlexander Shishkin static int intel_th_pti_probe(struct intel_th_device *thdev) 20214cdbf04SAlexander Shishkin { 20314cdbf04SAlexander Shishkin struct device *dev = &thdev->dev; 20414cdbf04SAlexander Shishkin struct resource *res; 20514cdbf04SAlexander Shishkin struct pti_device *pti; 20614cdbf04SAlexander Shishkin void __iomem *base; 20714cdbf04SAlexander Shishkin 20814cdbf04SAlexander Shishkin res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0); 20914cdbf04SAlexander Shishkin if (!res) 21014cdbf04SAlexander Shishkin return -ENODEV; 21114cdbf04SAlexander Shishkin 21214cdbf04SAlexander Shishkin base = devm_ioremap(dev, res->start, resource_size(res)); 21373061da0SDan Carpenter if (!base) 21473061da0SDan Carpenter return -ENOMEM; 21514cdbf04SAlexander Shishkin 21614cdbf04SAlexander Shishkin pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL); 21714cdbf04SAlexander Shishkin if (!pti) 21814cdbf04SAlexander Shishkin return -ENOMEM; 21914cdbf04SAlexander Shishkin 22014cdbf04SAlexander Shishkin pti->thdev = thdev; 22114cdbf04SAlexander Shishkin pti->base = base; 22214cdbf04SAlexander Shishkin 22314cdbf04SAlexander Shishkin read_hw_config(pti); 22414cdbf04SAlexander Shishkin 22514cdbf04SAlexander Shishkin dev_set_drvdata(dev, pti); 22614cdbf04SAlexander Shishkin 22714cdbf04SAlexander Shishkin return 0; 22814cdbf04SAlexander Shishkin } 22914cdbf04SAlexander Shishkin 23014cdbf04SAlexander Shishkin static void intel_th_pti_remove(struct intel_th_device *thdev) 23114cdbf04SAlexander Shishkin { 23214cdbf04SAlexander Shishkin } 23314cdbf04SAlexander Shishkin 23414cdbf04SAlexander Shishkin static struct intel_th_driver intel_th_pti_driver = { 23514cdbf04SAlexander Shishkin .probe = intel_th_pti_probe, 23614cdbf04SAlexander Shishkin .remove = intel_th_pti_remove, 23714cdbf04SAlexander Shishkin .activate = intel_th_pti_activate, 23814cdbf04SAlexander Shishkin .deactivate = intel_th_pti_deactivate, 239e8644e4cSAlexander Shishkin .attr_group = &pti_output_group, 24014cdbf04SAlexander Shishkin .driver = { 24114cdbf04SAlexander Shishkin .name = "pti", 24214cdbf04SAlexander Shishkin .owner = THIS_MODULE, 24314cdbf04SAlexander Shishkin }, 24414cdbf04SAlexander Shishkin }; 24514cdbf04SAlexander Shishkin 246f77d22bcSAlexander Shishkin static const char * const lpp_dest_str[] = { "pti", "exi" }; 247f77d22bcSAlexander Shishkin 248f77d22bcSAlexander Shishkin static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr, 249f77d22bcSAlexander Shishkin char *buf) 250f77d22bcSAlexander Shishkin { 251f77d22bcSAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 252f77d22bcSAlexander Shishkin ssize_t ret = 0; 253f77d22bcSAlexander Shishkin int i; 254f77d22bcSAlexander Shishkin 255f77d22bcSAlexander Shishkin for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) { 256f77d22bcSAlexander Shishkin const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s "; 257f77d22bcSAlexander Shishkin 258f77d22bcSAlexander Shishkin if (!(pti->lpp_dest_mask & BIT(i))) 259f77d22bcSAlexander Shishkin continue; 260f77d22bcSAlexander Shishkin 261f77d22bcSAlexander Shishkin ret += scnprintf(buf + ret, PAGE_SIZE - ret, 262f77d22bcSAlexander Shishkin fmt, lpp_dest_str[i]); 263f77d22bcSAlexander Shishkin } 264f77d22bcSAlexander Shishkin 265f77d22bcSAlexander Shishkin if (ret) 266f77d22bcSAlexander Shishkin buf[ret - 1] = '\n'; 267f77d22bcSAlexander Shishkin 268f77d22bcSAlexander Shishkin return ret; 269f77d22bcSAlexander Shishkin } 270f77d22bcSAlexander Shishkin 271f77d22bcSAlexander Shishkin static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr, 272f77d22bcSAlexander Shishkin const char *buf, size_t size) 273f77d22bcSAlexander Shishkin { 274f77d22bcSAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 275f77d22bcSAlexander Shishkin int i; 276f77d22bcSAlexander Shishkin 2771d2ef028SAndy Shevchenko i = sysfs_match_string(lpp_dest_str, buf); 2781d2ef028SAndy Shevchenko if (i < 0) 2791d2ef028SAndy Shevchenko return i; 280f77d22bcSAlexander Shishkin 2811d2ef028SAndy Shevchenko if (!(pti->lpp_dest_mask & BIT(i))) 2821d2ef028SAndy Shevchenko return -EINVAL; 2831d2ef028SAndy Shevchenko 284f77d22bcSAlexander Shishkin pti->lpp_dest = i; 2851d2ef028SAndy Shevchenko return size; 286f77d22bcSAlexander Shishkin } 287f77d22bcSAlexander Shishkin 288f77d22bcSAlexander Shishkin static DEVICE_ATTR_RW(lpp_dest); 289f77d22bcSAlexander Shishkin 290f77d22bcSAlexander Shishkin static struct attribute *lpp_output_attrs[] = { 291f77d22bcSAlexander Shishkin &dev_attr_mode.attr, 292f77d22bcSAlexander Shishkin &dev_attr_freerunning_clock.attr, 293f77d22bcSAlexander Shishkin &dev_attr_clock_divider.attr, 294f77d22bcSAlexander Shishkin &dev_attr_lpp_dest.attr, 295f77d22bcSAlexander Shishkin NULL, 296f77d22bcSAlexander Shishkin }; 297f77d22bcSAlexander Shishkin 298*d9962f6fSRikard Falkeborn static const struct attribute_group lpp_output_group = { 299f77d22bcSAlexander Shishkin .attrs = lpp_output_attrs, 300f77d22bcSAlexander Shishkin }; 301f77d22bcSAlexander Shishkin 302f77d22bcSAlexander Shishkin static struct intel_th_driver intel_th_lpp_driver = { 303f77d22bcSAlexander Shishkin .probe = intel_th_pti_probe, 304f77d22bcSAlexander Shishkin .remove = intel_th_pti_remove, 305f77d22bcSAlexander Shishkin .activate = intel_th_pti_activate, 306f77d22bcSAlexander Shishkin .deactivate = intel_th_pti_deactivate, 307f77d22bcSAlexander Shishkin .attr_group = &lpp_output_group, 308f77d22bcSAlexander Shishkin .driver = { 309f77d22bcSAlexander Shishkin .name = "lpp", 310f77d22bcSAlexander Shishkin .owner = THIS_MODULE, 311f77d22bcSAlexander Shishkin }, 312f77d22bcSAlexander Shishkin }; 313f77d22bcSAlexander Shishkin 314f77d22bcSAlexander Shishkin static int __init intel_th_pti_lpp_init(void) 315f77d22bcSAlexander Shishkin { 316f77d22bcSAlexander Shishkin int err; 317f77d22bcSAlexander Shishkin 318f77d22bcSAlexander Shishkin err = intel_th_driver_register(&intel_th_pti_driver); 319f77d22bcSAlexander Shishkin if (err) 320f77d22bcSAlexander Shishkin return err; 321f77d22bcSAlexander Shishkin 322f77d22bcSAlexander Shishkin err = intel_th_driver_register(&intel_th_lpp_driver); 323f77d22bcSAlexander Shishkin if (err) { 324f77d22bcSAlexander Shishkin intel_th_driver_unregister(&intel_th_pti_driver); 325f77d22bcSAlexander Shishkin return err; 326f77d22bcSAlexander Shishkin } 327f77d22bcSAlexander Shishkin 328f77d22bcSAlexander Shishkin return 0; 329f77d22bcSAlexander Shishkin } 330f77d22bcSAlexander Shishkin 331f77d22bcSAlexander Shishkin module_init(intel_th_pti_lpp_init); 332f77d22bcSAlexander Shishkin 333f77d22bcSAlexander Shishkin static void __exit intel_th_pti_lpp_exit(void) 334f77d22bcSAlexander Shishkin { 335f77d22bcSAlexander Shishkin intel_th_driver_unregister(&intel_th_pti_driver); 336f77d22bcSAlexander Shishkin intel_th_driver_unregister(&intel_th_lpp_driver); 337f77d22bcSAlexander Shishkin } 338f77d22bcSAlexander Shishkin 339f77d22bcSAlexander Shishkin module_exit(intel_th_pti_lpp_exit); 34014cdbf04SAlexander Shishkin 34114cdbf04SAlexander Shishkin MODULE_LICENSE("GPL v2"); 342f77d22bcSAlexander Shishkin MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver"); 34314cdbf04SAlexander Shishkin MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); 344