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