1*14cdbf04SAlexander Shishkin /* 2*14cdbf04SAlexander Shishkin * Intel(R) Trace Hub PTI output driver 3*14cdbf04SAlexander Shishkin * 4*14cdbf04SAlexander Shishkin * Copyright (C) 2014-2015 Intel Corporation. 5*14cdbf04SAlexander Shishkin * 6*14cdbf04SAlexander Shishkin * This program is free software; you can redistribute it and/or modify it 7*14cdbf04SAlexander Shishkin * under the terms and conditions of the GNU General Public License, 8*14cdbf04SAlexander Shishkin * version 2, as published by the Free Software Foundation. 9*14cdbf04SAlexander Shishkin * 10*14cdbf04SAlexander Shishkin * This program is distributed in the hope it will be useful, but WITHOUT 11*14cdbf04SAlexander Shishkin * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12*14cdbf04SAlexander Shishkin * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13*14cdbf04SAlexander Shishkin * more details. 14*14cdbf04SAlexander Shishkin */ 15*14cdbf04SAlexander Shishkin 16*14cdbf04SAlexander Shishkin #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 17*14cdbf04SAlexander Shishkin 18*14cdbf04SAlexander Shishkin #include <linux/types.h> 19*14cdbf04SAlexander Shishkin #include <linux/module.h> 20*14cdbf04SAlexander Shishkin #include <linux/device.h> 21*14cdbf04SAlexander Shishkin #include <linux/sizes.h> 22*14cdbf04SAlexander Shishkin #include <linux/printk.h> 23*14cdbf04SAlexander Shishkin #include <linux/slab.h> 24*14cdbf04SAlexander Shishkin #include <linux/mm.h> 25*14cdbf04SAlexander Shishkin #include <linux/io.h> 26*14cdbf04SAlexander Shishkin 27*14cdbf04SAlexander Shishkin #include "intel_th.h" 28*14cdbf04SAlexander Shishkin #include "pti.h" 29*14cdbf04SAlexander Shishkin 30*14cdbf04SAlexander Shishkin struct pti_device { 31*14cdbf04SAlexander Shishkin void __iomem *base; 32*14cdbf04SAlexander Shishkin struct intel_th_device *thdev; 33*14cdbf04SAlexander Shishkin unsigned int mode; 34*14cdbf04SAlexander Shishkin unsigned int freeclk; 35*14cdbf04SAlexander Shishkin unsigned int clkdiv; 36*14cdbf04SAlexander Shishkin unsigned int patgen; 37*14cdbf04SAlexander Shishkin }; 38*14cdbf04SAlexander Shishkin 39*14cdbf04SAlexander Shishkin /* map PTI widths to MODE settings of PTI_CTL register */ 40*14cdbf04SAlexander Shishkin static const unsigned int pti_mode[] = { 41*14cdbf04SAlexander Shishkin 0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 42*14cdbf04SAlexander Shishkin }; 43*14cdbf04SAlexander Shishkin 44*14cdbf04SAlexander Shishkin static int pti_width_mode(unsigned int width) 45*14cdbf04SAlexander Shishkin { 46*14cdbf04SAlexander Shishkin int i; 47*14cdbf04SAlexander Shishkin 48*14cdbf04SAlexander Shishkin for (i = 0; i < ARRAY_SIZE(pti_mode); i++) 49*14cdbf04SAlexander Shishkin if (pti_mode[i] == width) 50*14cdbf04SAlexander Shishkin return i; 51*14cdbf04SAlexander Shishkin 52*14cdbf04SAlexander Shishkin return -EINVAL; 53*14cdbf04SAlexander Shishkin } 54*14cdbf04SAlexander Shishkin 55*14cdbf04SAlexander Shishkin static ssize_t mode_show(struct device *dev, struct device_attribute *attr, 56*14cdbf04SAlexander Shishkin char *buf) 57*14cdbf04SAlexander Shishkin { 58*14cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 59*14cdbf04SAlexander Shishkin 60*14cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]); 61*14cdbf04SAlexander Shishkin } 62*14cdbf04SAlexander Shishkin 63*14cdbf04SAlexander Shishkin static ssize_t mode_store(struct device *dev, struct device_attribute *attr, 64*14cdbf04SAlexander Shishkin const char *buf, size_t size) 65*14cdbf04SAlexander Shishkin { 66*14cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 67*14cdbf04SAlexander Shishkin unsigned long val; 68*14cdbf04SAlexander Shishkin int ret; 69*14cdbf04SAlexander Shishkin 70*14cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val); 71*14cdbf04SAlexander Shishkin if (ret) 72*14cdbf04SAlexander Shishkin return ret; 73*14cdbf04SAlexander Shishkin 74*14cdbf04SAlexander Shishkin ret = pti_width_mode(val); 75*14cdbf04SAlexander Shishkin if (ret < 0) 76*14cdbf04SAlexander Shishkin return ret; 77*14cdbf04SAlexander Shishkin 78*14cdbf04SAlexander Shishkin pti->mode = ret; 79*14cdbf04SAlexander Shishkin 80*14cdbf04SAlexander Shishkin return size; 81*14cdbf04SAlexander Shishkin } 82*14cdbf04SAlexander Shishkin 83*14cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(mode); 84*14cdbf04SAlexander Shishkin 85*14cdbf04SAlexander Shishkin static ssize_t 86*14cdbf04SAlexander Shishkin freerunning_clock_show(struct device *dev, struct device_attribute *attr, 87*14cdbf04SAlexander Shishkin char *buf) 88*14cdbf04SAlexander Shishkin { 89*14cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 90*14cdbf04SAlexander Shishkin 91*14cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk); 92*14cdbf04SAlexander Shishkin } 93*14cdbf04SAlexander Shishkin 94*14cdbf04SAlexander Shishkin static ssize_t 95*14cdbf04SAlexander Shishkin freerunning_clock_store(struct device *dev, struct device_attribute *attr, 96*14cdbf04SAlexander Shishkin const char *buf, size_t size) 97*14cdbf04SAlexander Shishkin { 98*14cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 99*14cdbf04SAlexander Shishkin unsigned long val; 100*14cdbf04SAlexander Shishkin int ret; 101*14cdbf04SAlexander Shishkin 102*14cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val); 103*14cdbf04SAlexander Shishkin if (ret) 104*14cdbf04SAlexander Shishkin return ret; 105*14cdbf04SAlexander Shishkin 106*14cdbf04SAlexander Shishkin pti->freeclk = !!val; 107*14cdbf04SAlexander Shishkin 108*14cdbf04SAlexander Shishkin return size; 109*14cdbf04SAlexander Shishkin } 110*14cdbf04SAlexander Shishkin 111*14cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(freerunning_clock); 112*14cdbf04SAlexander Shishkin 113*14cdbf04SAlexander Shishkin static ssize_t 114*14cdbf04SAlexander Shishkin clock_divider_show(struct device *dev, struct device_attribute *attr, 115*14cdbf04SAlexander Shishkin char *buf) 116*14cdbf04SAlexander Shishkin { 117*14cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 118*14cdbf04SAlexander Shishkin 119*14cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv); 120*14cdbf04SAlexander Shishkin } 121*14cdbf04SAlexander Shishkin 122*14cdbf04SAlexander Shishkin static ssize_t 123*14cdbf04SAlexander Shishkin clock_divider_store(struct device *dev, struct device_attribute *attr, 124*14cdbf04SAlexander Shishkin const char *buf, size_t size) 125*14cdbf04SAlexander Shishkin { 126*14cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev); 127*14cdbf04SAlexander Shishkin unsigned long val; 128*14cdbf04SAlexander Shishkin int ret; 129*14cdbf04SAlexander Shishkin 130*14cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val); 131*14cdbf04SAlexander Shishkin if (ret) 132*14cdbf04SAlexander Shishkin return ret; 133*14cdbf04SAlexander Shishkin 134*14cdbf04SAlexander Shishkin if (!is_power_of_2(val) || val > 8 || !val) 135*14cdbf04SAlexander Shishkin return -EINVAL; 136*14cdbf04SAlexander Shishkin 137*14cdbf04SAlexander Shishkin pti->clkdiv = val; 138*14cdbf04SAlexander Shishkin 139*14cdbf04SAlexander Shishkin return size; 140*14cdbf04SAlexander Shishkin } 141*14cdbf04SAlexander Shishkin 142*14cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(clock_divider); 143*14cdbf04SAlexander Shishkin 144*14cdbf04SAlexander Shishkin static struct attribute *pti_output_attrs[] = { 145*14cdbf04SAlexander Shishkin &dev_attr_mode.attr, 146*14cdbf04SAlexander Shishkin &dev_attr_freerunning_clock.attr, 147*14cdbf04SAlexander Shishkin &dev_attr_clock_divider.attr, 148*14cdbf04SAlexander Shishkin NULL, 149*14cdbf04SAlexander Shishkin }; 150*14cdbf04SAlexander Shishkin 151*14cdbf04SAlexander Shishkin static struct attribute_group pti_output_group = { 152*14cdbf04SAlexander Shishkin .attrs = pti_output_attrs, 153*14cdbf04SAlexander Shishkin }; 154*14cdbf04SAlexander Shishkin 155*14cdbf04SAlexander Shishkin static int intel_th_pti_activate(struct intel_th_device *thdev) 156*14cdbf04SAlexander Shishkin { 157*14cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(&thdev->dev); 158*14cdbf04SAlexander Shishkin u32 ctl = PTI_EN; 159*14cdbf04SAlexander Shishkin 160*14cdbf04SAlexander Shishkin if (pti->patgen) 161*14cdbf04SAlexander Shishkin ctl |= pti->patgen << __ffs(PTI_PATGENMODE); 162*14cdbf04SAlexander Shishkin if (pti->freeclk) 163*14cdbf04SAlexander Shishkin ctl |= PTI_FCEN; 164*14cdbf04SAlexander Shishkin ctl |= pti->mode << __ffs(PTI_MODE); 165*14cdbf04SAlexander Shishkin ctl |= pti->clkdiv << __ffs(PTI_CLKDIV); 166*14cdbf04SAlexander Shishkin 167*14cdbf04SAlexander Shishkin iowrite32(ctl, pti->base + REG_PTI_CTL); 168*14cdbf04SAlexander Shishkin 169*14cdbf04SAlexander Shishkin intel_th_trace_enable(thdev); 170*14cdbf04SAlexander Shishkin 171*14cdbf04SAlexander Shishkin return 0; 172*14cdbf04SAlexander Shishkin } 173*14cdbf04SAlexander Shishkin 174*14cdbf04SAlexander Shishkin static void intel_th_pti_deactivate(struct intel_th_device *thdev) 175*14cdbf04SAlexander Shishkin { 176*14cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(&thdev->dev); 177*14cdbf04SAlexander Shishkin 178*14cdbf04SAlexander Shishkin intel_th_trace_disable(thdev); 179*14cdbf04SAlexander Shishkin 180*14cdbf04SAlexander Shishkin iowrite32(0, pti->base + REG_PTI_CTL); 181*14cdbf04SAlexander Shishkin } 182*14cdbf04SAlexander Shishkin 183*14cdbf04SAlexander Shishkin static void read_hw_config(struct pti_device *pti) 184*14cdbf04SAlexander Shishkin { 185*14cdbf04SAlexander Shishkin u32 ctl = ioread32(pti->base + REG_PTI_CTL); 186*14cdbf04SAlexander Shishkin 187*14cdbf04SAlexander Shishkin pti->mode = (ctl & PTI_MODE) >> __ffs(PTI_MODE); 188*14cdbf04SAlexander Shishkin pti->clkdiv = (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV); 189*14cdbf04SAlexander Shishkin pti->freeclk = !!(ctl & PTI_FCEN); 190*14cdbf04SAlexander Shishkin 191*14cdbf04SAlexander Shishkin if (!pti_mode[pti->mode]) 192*14cdbf04SAlexander Shishkin pti->mode = pti_width_mode(4); 193*14cdbf04SAlexander Shishkin if (!pti->clkdiv) 194*14cdbf04SAlexander Shishkin pti->clkdiv = 1; 195*14cdbf04SAlexander Shishkin } 196*14cdbf04SAlexander Shishkin 197*14cdbf04SAlexander Shishkin static int intel_th_pti_probe(struct intel_th_device *thdev) 198*14cdbf04SAlexander Shishkin { 199*14cdbf04SAlexander Shishkin struct device *dev = &thdev->dev; 200*14cdbf04SAlexander Shishkin struct resource *res; 201*14cdbf04SAlexander Shishkin struct pti_device *pti; 202*14cdbf04SAlexander Shishkin void __iomem *base; 203*14cdbf04SAlexander Shishkin int ret; 204*14cdbf04SAlexander Shishkin 205*14cdbf04SAlexander Shishkin res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0); 206*14cdbf04SAlexander Shishkin if (!res) 207*14cdbf04SAlexander Shishkin return -ENODEV; 208*14cdbf04SAlexander Shishkin 209*14cdbf04SAlexander Shishkin base = devm_ioremap(dev, res->start, resource_size(res)); 210*14cdbf04SAlexander Shishkin if (IS_ERR(base)) 211*14cdbf04SAlexander Shishkin return PTR_ERR(base); 212*14cdbf04SAlexander Shishkin 213*14cdbf04SAlexander Shishkin pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL); 214*14cdbf04SAlexander Shishkin if (!pti) 215*14cdbf04SAlexander Shishkin return -ENOMEM; 216*14cdbf04SAlexander Shishkin 217*14cdbf04SAlexander Shishkin pti->thdev = thdev; 218*14cdbf04SAlexander Shishkin pti->base = base; 219*14cdbf04SAlexander Shishkin 220*14cdbf04SAlexander Shishkin read_hw_config(pti); 221*14cdbf04SAlexander Shishkin 222*14cdbf04SAlexander Shishkin ret = sysfs_create_group(&dev->kobj, &pti_output_group); 223*14cdbf04SAlexander Shishkin if (ret) 224*14cdbf04SAlexander Shishkin return ret; 225*14cdbf04SAlexander Shishkin 226*14cdbf04SAlexander Shishkin dev_set_drvdata(dev, pti); 227*14cdbf04SAlexander Shishkin 228*14cdbf04SAlexander Shishkin return 0; 229*14cdbf04SAlexander Shishkin } 230*14cdbf04SAlexander Shishkin 231*14cdbf04SAlexander Shishkin static void intel_th_pti_remove(struct intel_th_device *thdev) 232*14cdbf04SAlexander Shishkin { 233*14cdbf04SAlexander Shishkin } 234*14cdbf04SAlexander Shishkin 235*14cdbf04SAlexander Shishkin static struct intel_th_driver intel_th_pti_driver = { 236*14cdbf04SAlexander Shishkin .probe = intel_th_pti_probe, 237*14cdbf04SAlexander Shishkin .remove = intel_th_pti_remove, 238*14cdbf04SAlexander Shishkin .activate = intel_th_pti_activate, 239*14cdbf04SAlexander Shishkin .deactivate = intel_th_pti_deactivate, 240*14cdbf04SAlexander Shishkin .driver = { 241*14cdbf04SAlexander Shishkin .name = "pti", 242*14cdbf04SAlexander Shishkin .owner = THIS_MODULE, 243*14cdbf04SAlexander Shishkin }, 244*14cdbf04SAlexander Shishkin }; 245*14cdbf04SAlexander Shishkin 246*14cdbf04SAlexander Shishkin module_driver(intel_th_pti_driver, 247*14cdbf04SAlexander Shishkin intel_th_driver_register, 248*14cdbf04SAlexander Shishkin intel_th_driver_unregister); 249*14cdbf04SAlexander Shishkin 250*14cdbf04SAlexander Shishkin MODULE_LICENSE("GPL v2"); 251*14cdbf04SAlexander Shishkin MODULE_DESCRIPTION("Intel(R) Trace Hub PTI output driver"); 252*14cdbf04SAlexander Shishkin MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); 253