114cdbf04SAlexander Shishkin /* 214cdbf04SAlexander Shishkin * Intel(R) Trace Hub PTI output driver 314cdbf04SAlexander Shishkin * 414cdbf04SAlexander Shishkin * Copyright (C) 2014-2015 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; 3714cdbf04SAlexander Shishkin }; 3814cdbf04SAlexander Shishkin 3914cdbf04SAlexander Shishkin /* map PTI widths to MODE settings of PTI_CTL register */ 4014cdbf04SAlexander Shishkin static const unsigned int pti_mode[] = { 4114cdbf04SAlexander Shishkin 0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 4214cdbf04SAlexander Shishkin }; 4314cdbf04SAlexander Shishkin 4414cdbf04SAlexander Shishkin static int pti_width_mode(unsigned int width) 4514cdbf04SAlexander Shishkin { 4614cdbf04SAlexander Shishkin int i; 4714cdbf04SAlexander Shishkin 4814cdbf04SAlexander Shishkin for (i = 0; i < ARRAY_SIZE(pti_mode); i++) 4914cdbf04SAlexander Shishkin if (pti_mode[i] == width) 5014cdbf04SAlexander Shishkin return i; 5114cdbf04SAlexander Shishkin 5214cdbf04SAlexander Shishkin return -EINVAL; 5314cdbf04SAlexander Shishkin } 5414cdbf04SAlexander Shishkin 5514cdbf04SAlexander Shishkin static ssize_t mode_show(struct device *dev, struct device_attribute *attr, 5614cdbf04SAlexander Shishkin char *buf) 5714cdbf04SAlexander Shishkin { 5814cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 5914cdbf04SAlexander Shishkin 6014cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]); 6114cdbf04SAlexander Shishkin } 6214cdbf04SAlexander Shishkin 6314cdbf04SAlexander Shishkin static ssize_t mode_store(struct device *dev, struct device_attribute *attr, 6414cdbf04SAlexander Shishkin const char *buf, size_t size) 6514cdbf04SAlexander Shishkin { 6614cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 6714cdbf04SAlexander Shishkin unsigned long val; 6814cdbf04SAlexander Shishkin int ret; 6914cdbf04SAlexander Shishkin 7014cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val); 7114cdbf04SAlexander Shishkin if (ret) 7214cdbf04SAlexander Shishkin return ret; 7314cdbf04SAlexander Shishkin 7414cdbf04SAlexander Shishkin ret = pti_width_mode(val); 7514cdbf04SAlexander Shishkin if (ret < 0) 7614cdbf04SAlexander Shishkin return ret; 7714cdbf04SAlexander Shishkin 7814cdbf04SAlexander Shishkin pti->mode = ret; 7914cdbf04SAlexander Shishkin 8014cdbf04SAlexander Shishkin return size; 8114cdbf04SAlexander Shishkin } 8214cdbf04SAlexander Shishkin 8314cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(mode); 8414cdbf04SAlexander Shishkin 8514cdbf04SAlexander Shishkin static ssize_t 8614cdbf04SAlexander Shishkin freerunning_clock_show(struct device *dev, struct device_attribute *attr, 8714cdbf04SAlexander Shishkin char *buf) 8814cdbf04SAlexander Shishkin { 8914cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 9014cdbf04SAlexander Shishkin 9114cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk); 9214cdbf04SAlexander Shishkin } 9314cdbf04SAlexander Shishkin 9414cdbf04SAlexander Shishkin static ssize_t 9514cdbf04SAlexander Shishkin freerunning_clock_store(struct device *dev, struct device_attribute *attr, 9614cdbf04SAlexander Shishkin const char *buf, size_t size) 9714cdbf04SAlexander Shishkin { 9814cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 9914cdbf04SAlexander Shishkin unsigned long val; 10014cdbf04SAlexander Shishkin int ret; 10114cdbf04SAlexander Shishkin 10214cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val); 10314cdbf04SAlexander Shishkin if (ret) 10414cdbf04SAlexander Shishkin return ret; 10514cdbf04SAlexander Shishkin 10614cdbf04SAlexander Shishkin pti->freeclk = !!val; 10714cdbf04SAlexander Shishkin 10814cdbf04SAlexander Shishkin return size; 10914cdbf04SAlexander Shishkin } 11014cdbf04SAlexander Shishkin 11114cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(freerunning_clock); 11214cdbf04SAlexander Shishkin 11314cdbf04SAlexander Shishkin static ssize_t 11414cdbf04SAlexander Shishkin clock_divider_show(struct device *dev, struct device_attribute *attr, 11514cdbf04SAlexander Shishkin char *buf) 11614cdbf04SAlexander Shishkin { 11714cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 11814cdbf04SAlexander Shishkin 11914cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv); 12014cdbf04SAlexander Shishkin } 12114cdbf04SAlexander Shishkin 12214cdbf04SAlexander Shishkin static ssize_t 12314cdbf04SAlexander Shishkin clock_divider_store(struct device *dev, struct device_attribute *attr, 12414cdbf04SAlexander Shishkin const char *buf, size_t size) 12514cdbf04SAlexander Shishkin { 12614cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 12714cdbf04SAlexander Shishkin unsigned long val; 12814cdbf04SAlexander Shishkin int ret; 12914cdbf04SAlexander Shishkin 13014cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val); 13114cdbf04SAlexander Shishkin if (ret) 13214cdbf04SAlexander Shishkin return ret; 13314cdbf04SAlexander Shishkin 13414cdbf04SAlexander Shishkin if (!is_power_of_2(val) || val > 8 || !val) 13514cdbf04SAlexander Shishkin return -EINVAL; 13614cdbf04SAlexander Shishkin 13714cdbf04SAlexander Shishkin pti->clkdiv = val; 13814cdbf04SAlexander Shishkin 13914cdbf04SAlexander Shishkin return size; 14014cdbf04SAlexander Shishkin } 14114cdbf04SAlexander Shishkin 14214cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(clock_divider); 14314cdbf04SAlexander Shishkin 14414cdbf04SAlexander Shishkin static struct attribute *pti_output_attrs[] = { 14514cdbf04SAlexander Shishkin &dev_attr_mode.attr, 14614cdbf04SAlexander Shishkin &dev_attr_freerunning_clock.attr, 14714cdbf04SAlexander Shishkin &dev_attr_clock_divider.attr, 14814cdbf04SAlexander Shishkin NULL, 14914cdbf04SAlexander Shishkin }; 15014cdbf04SAlexander Shishkin 15114cdbf04SAlexander Shishkin static struct attribute_group pti_output_group = { 15214cdbf04SAlexander Shishkin .attrs = pti_output_attrs, 15314cdbf04SAlexander Shishkin }; 15414cdbf04SAlexander Shishkin 15514cdbf04SAlexander Shishkin static int intel_th_pti_activate(struct intel_th_device *thdev) 15614cdbf04SAlexander Shishkin { 15714cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(&thdev->dev); 15814cdbf04SAlexander Shishkin u32 ctl = PTI_EN; 15914cdbf04SAlexander Shishkin 16014cdbf04SAlexander Shishkin if (pti->patgen) 16114cdbf04SAlexander Shishkin ctl |= pti->patgen << __ffs(PTI_PATGENMODE); 16214cdbf04SAlexander Shishkin if (pti->freeclk) 16314cdbf04SAlexander Shishkin ctl |= PTI_FCEN; 16414cdbf04SAlexander Shishkin ctl |= pti->mode << __ffs(PTI_MODE); 16514cdbf04SAlexander Shishkin ctl |= pti->clkdiv << __ffs(PTI_CLKDIV); 16614cdbf04SAlexander Shishkin 16714cdbf04SAlexander Shishkin iowrite32(ctl, pti->base + REG_PTI_CTL); 16814cdbf04SAlexander Shishkin 16914cdbf04SAlexander Shishkin intel_th_trace_enable(thdev); 17014cdbf04SAlexander Shishkin 17114cdbf04SAlexander Shishkin return 0; 17214cdbf04SAlexander Shishkin } 17314cdbf04SAlexander Shishkin 17414cdbf04SAlexander Shishkin static void intel_th_pti_deactivate(struct intel_th_device *thdev) 17514cdbf04SAlexander Shishkin { 17614cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(&thdev->dev); 17714cdbf04SAlexander Shishkin 17814cdbf04SAlexander Shishkin intel_th_trace_disable(thdev); 17914cdbf04SAlexander Shishkin 18014cdbf04SAlexander Shishkin iowrite32(0, pti->base + REG_PTI_CTL); 18114cdbf04SAlexander Shishkin } 18214cdbf04SAlexander Shishkin 18314cdbf04SAlexander Shishkin static void read_hw_config(struct pti_device *pti) 18414cdbf04SAlexander Shishkin { 18514cdbf04SAlexander Shishkin u32 ctl = ioread32(pti->base + REG_PTI_CTL); 18614cdbf04SAlexander Shishkin 18714cdbf04SAlexander Shishkin pti->mode = (ctl & PTI_MODE) >> __ffs(PTI_MODE); 18814cdbf04SAlexander Shishkin pti->clkdiv = (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV); 18914cdbf04SAlexander Shishkin pti->freeclk = !!(ctl & PTI_FCEN); 19014cdbf04SAlexander Shishkin 19114cdbf04SAlexander Shishkin if (!pti_mode[pti->mode]) 19214cdbf04SAlexander Shishkin pti->mode = pti_width_mode(4); 19314cdbf04SAlexander Shishkin if (!pti->clkdiv) 19414cdbf04SAlexander Shishkin pti->clkdiv = 1; 19514cdbf04SAlexander Shishkin } 19614cdbf04SAlexander Shishkin 19714cdbf04SAlexander Shishkin static int intel_th_pti_probe(struct intel_th_device *thdev) 19814cdbf04SAlexander Shishkin { 19914cdbf04SAlexander Shishkin struct device *dev = &thdev->dev; 20014cdbf04SAlexander Shishkin struct resource *res; 20114cdbf04SAlexander Shishkin struct pti_device *pti; 20214cdbf04SAlexander Shishkin void __iomem *base; 20314cdbf04SAlexander Shishkin int ret; 20414cdbf04SAlexander Shishkin 20514cdbf04SAlexander Shishkin res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0); 20614cdbf04SAlexander Shishkin if (!res) 20714cdbf04SAlexander Shishkin return -ENODEV; 20814cdbf04SAlexander Shishkin 20914cdbf04SAlexander Shishkin base = devm_ioremap(dev, res->start, resource_size(res)); 210*73061da0SDan Carpenter if (!base) 211*73061da0SDan Carpenter return -ENOMEM; 21214cdbf04SAlexander Shishkin 21314cdbf04SAlexander Shishkin pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL); 21414cdbf04SAlexander Shishkin if (!pti) 21514cdbf04SAlexander Shishkin return -ENOMEM; 21614cdbf04SAlexander Shishkin 21714cdbf04SAlexander Shishkin pti->thdev = thdev; 21814cdbf04SAlexander Shishkin pti->base = base; 21914cdbf04SAlexander Shishkin 22014cdbf04SAlexander Shishkin read_hw_config(pti); 22114cdbf04SAlexander Shishkin 22214cdbf04SAlexander Shishkin ret = sysfs_create_group(&dev->kobj, &pti_output_group); 22314cdbf04SAlexander Shishkin if (ret) 22414cdbf04SAlexander Shishkin return ret; 22514cdbf04SAlexander Shishkin 22614cdbf04SAlexander Shishkin dev_set_drvdata(dev, pti); 22714cdbf04SAlexander Shishkin 22814cdbf04SAlexander Shishkin return 0; 22914cdbf04SAlexander Shishkin } 23014cdbf04SAlexander Shishkin 23114cdbf04SAlexander Shishkin static void intel_th_pti_remove(struct intel_th_device *thdev) 23214cdbf04SAlexander Shishkin { 23314cdbf04SAlexander Shishkin } 23414cdbf04SAlexander Shishkin 23514cdbf04SAlexander Shishkin static struct intel_th_driver intel_th_pti_driver = { 23614cdbf04SAlexander Shishkin .probe = intel_th_pti_probe, 23714cdbf04SAlexander Shishkin .remove = intel_th_pti_remove, 23814cdbf04SAlexander Shishkin .activate = intel_th_pti_activate, 23914cdbf04SAlexander Shishkin .deactivate = intel_th_pti_deactivate, 24014cdbf04SAlexander Shishkin .driver = { 24114cdbf04SAlexander Shishkin .name = "pti", 24214cdbf04SAlexander Shishkin .owner = THIS_MODULE, 24314cdbf04SAlexander Shishkin }, 24414cdbf04SAlexander Shishkin }; 24514cdbf04SAlexander Shishkin 24614cdbf04SAlexander Shishkin module_driver(intel_th_pti_driver, 24714cdbf04SAlexander Shishkin intel_th_driver_register, 24814cdbf04SAlexander Shishkin intel_th_driver_unregister); 24914cdbf04SAlexander Shishkin 25014cdbf04SAlexander Shishkin MODULE_LICENSE("GPL v2"); 25114cdbf04SAlexander Shishkin MODULE_DESCRIPTION("Intel(R) Trace Hub PTI output driver"); 25214cdbf04SAlexander Shishkin MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); 253