344 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Intel(R) Trace Hub PTI output driver4 *5 * Copyright (C) 2014-2016 Intel Corporation.6 */7 8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt9 10#include <linux/types.h>11#include <linux/module.h>12#include <linux/device.h>13#include <linux/sizes.h>14#include <linux/printk.h>15#include <linux/slab.h>16#include <linux/mm.h>17#include <linux/io.h>18 19#include "intel_th.h"20#include "pti.h"21 22struct pti_device {23 void __iomem *base;24 struct intel_th_device *thdev;25 unsigned int mode;26 unsigned int freeclk;27 unsigned int clkdiv;28 unsigned int patgen;29 unsigned int lpp_dest_mask;30 unsigned int lpp_dest;31};32 33/* map PTI widths to MODE settings of PTI_CTL register */34static const unsigned int pti_mode[] = {35 0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,36};37 38static int pti_width_mode(unsigned int width)39{40 int i;41 42 for (i = 0; i < ARRAY_SIZE(pti_mode); i++)43 if (pti_mode[i] == width)44 return i;45 46 return -EINVAL;47}48 49static ssize_t mode_show(struct device *dev, struct device_attribute *attr,50 char *buf)51{52 struct pti_device *pti = dev_get_drvdata(dev);53 54 return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]);55}56 57static ssize_t mode_store(struct device *dev, struct device_attribute *attr,58 const char *buf, size_t size)59{60 struct pti_device *pti = dev_get_drvdata(dev);61 unsigned long val;62 int ret;63 64 ret = kstrtoul(buf, 10, &val);65 if (ret)66 return ret;67 68 ret = pti_width_mode(val);69 if (ret < 0)70 return ret;71 72 pti->mode = ret;73 74 return size;75}76 77static DEVICE_ATTR_RW(mode);78 79static ssize_t80freerunning_clock_show(struct device *dev, struct device_attribute *attr,81 char *buf)82{83 struct pti_device *pti = dev_get_drvdata(dev);84 85 return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk);86}87 88static ssize_t89freerunning_clock_store(struct device *dev, struct device_attribute *attr,90 const char *buf, size_t size)91{92 struct pti_device *pti = dev_get_drvdata(dev);93 unsigned long val;94 int ret;95 96 ret = kstrtoul(buf, 10, &val);97 if (ret)98 return ret;99 100 pti->freeclk = !!val;101 102 return size;103}104 105static DEVICE_ATTR_RW(freerunning_clock);106 107static ssize_t108clock_divider_show(struct device *dev, struct device_attribute *attr,109 char *buf)110{111 struct pti_device *pti = dev_get_drvdata(dev);112 113 return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv);114}115 116static ssize_t117clock_divider_store(struct device *dev, struct device_attribute *attr,118 const char *buf, size_t size)119{120 struct pti_device *pti = dev_get_drvdata(dev);121 unsigned long val;122 int ret;123 124 ret = kstrtoul(buf, 10, &val);125 if (ret)126 return ret;127 128 if (!is_power_of_2(val) || val > 8 || !val)129 return -EINVAL;130 131 pti->clkdiv = val;132 133 return size;134}135 136static DEVICE_ATTR_RW(clock_divider);137 138static struct attribute *pti_output_attrs[] = {139 &dev_attr_mode.attr,140 &dev_attr_freerunning_clock.attr,141 &dev_attr_clock_divider.attr,142 NULL,143};144 145static const struct attribute_group pti_output_group = {146 .attrs = pti_output_attrs,147};148 149static int intel_th_pti_activate(struct intel_th_device *thdev)150{151 struct pti_device *pti = dev_get_drvdata(&thdev->dev);152 u32 ctl = PTI_EN;153 154 if (pti->patgen)155 ctl |= pti->patgen << __ffs(PTI_PATGENMODE);156 if (pti->freeclk)157 ctl |= PTI_FCEN;158 ctl |= pti->mode << __ffs(PTI_MODE);159 ctl |= pti->clkdiv << __ffs(PTI_CLKDIV);160 ctl |= pti->lpp_dest << __ffs(LPP_DEST);161 162 iowrite32(ctl, pti->base + REG_PTI_CTL);163 164 intel_th_trace_enable(thdev);165 166 return 0;167}168 169static void intel_th_pti_deactivate(struct intel_th_device *thdev)170{171 struct pti_device *pti = dev_get_drvdata(&thdev->dev);172 173 intel_th_trace_disable(thdev);174 175 iowrite32(0, pti->base + REG_PTI_CTL);176}177 178static void read_hw_config(struct pti_device *pti)179{180 u32 ctl = ioread32(pti->base + REG_PTI_CTL);181 182 pti->mode = (ctl & PTI_MODE) >> __ffs(PTI_MODE);183 pti->clkdiv = (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV);184 pti->freeclk = !!(ctl & PTI_FCEN);185 186 if (!pti_mode[pti->mode])187 pti->mode = pti_width_mode(4);188 if (!pti->clkdiv)189 pti->clkdiv = 1;190 191 if (pti->thdev->output.type == GTH_LPP) {192 if (ctl & LPP_PTIPRESENT)193 pti->lpp_dest_mask |= LPP_DEST_PTI;194 if (ctl & LPP_BSSBPRESENT)195 pti->lpp_dest_mask |= LPP_DEST_EXI;196 if (ctl & LPP_DEST)197 pti->lpp_dest = 1;198 }199}200 201static int intel_th_pti_probe(struct intel_th_device *thdev)202{203 struct device *dev = &thdev->dev;204 struct resource *res;205 struct pti_device *pti;206 void __iomem *base;207 208 res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);209 if (!res)210 return -ENODEV;211 212 base = devm_ioremap(dev, res->start, resource_size(res));213 if (!base)214 return -ENOMEM;215 216 pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL);217 if (!pti)218 return -ENOMEM;219 220 pti->thdev = thdev;221 pti->base = base;222 223 read_hw_config(pti);224 225 dev_set_drvdata(dev, pti);226 227 return 0;228}229 230static void intel_th_pti_remove(struct intel_th_device *thdev)231{232}233 234static struct intel_th_driver intel_th_pti_driver = {235 .probe = intel_th_pti_probe,236 .remove = intel_th_pti_remove,237 .activate = intel_th_pti_activate,238 .deactivate = intel_th_pti_deactivate,239 .attr_group = &pti_output_group,240 .driver = {241 .name = "pti",242 .owner = THIS_MODULE,243 },244};245 246static const char * const lpp_dest_str[] = { "pti", "exi" };247 248static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr,249 char *buf)250{251 struct pti_device *pti = dev_get_drvdata(dev);252 ssize_t ret = 0;253 int i;254 255 for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) {256 const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s ";257 258 if (!(pti->lpp_dest_mask & BIT(i)))259 continue;260 261 ret += scnprintf(buf + ret, PAGE_SIZE - ret,262 fmt, lpp_dest_str[i]);263 }264 265 if (ret)266 buf[ret - 1] = '\n';267 268 return ret;269}270 271static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr,272 const char *buf, size_t size)273{274 struct pti_device *pti = dev_get_drvdata(dev);275 int i;276 277 i = sysfs_match_string(lpp_dest_str, buf);278 if (i < 0)279 return i;280 281 if (!(pti->lpp_dest_mask & BIT(i)))282 return -EINVAL;283 284 pti->lpp_dest = i;285 return size;286}287 288static DEVICE_ATTR_RW(lpp_dest);289 290static struct attribute *lpp_output_attrs[] = {291 &dev_attr_mode.attr,292 &dev_attr_freerunning_clock.attr,293 &dev_attr_clock_divider.attr,294 &dev_attr_lpp_dest.attr,295 NULL,296};297 298static const struct attribute_group lpp_output_group = {299 .attrs = lpp_output_attrs,300};301 302static struct intel_th_driver intel_th_lpp_driver = {303 .probe = intel_th_pti_probe,304 .remove = intel_th_pti_remove,305 .activate = intel_th_pti_activate,306 .deactivate = intel_th_pti_deactivate,307 .attr_group = &lpp_output_group,308 .driver = {309 .name = "lpp",310 .owner = THIS_MODULE,311 },312};313 314static int __init intel_th_pti_lpp_init(void)315{316 int err;317 318 err = intel_th_driver_register(&intel_th_pti_driver);319 if (err)320 return err;321 322 err = intel_th_driver_register(&intel_th_lpp_driver);323 if (err) {324 intel_th_driver_unregister(&intel_th_pti_driver);325 return err;326 }327 328 return 0;329}330 331module_init(intel_th_pti_lpp_init);332 333static void __exit intel_th_pti_lpp_exit(void)334{335 intel_th_driver_unregister(&intel_th_pti_driver);336 intel_th_driver_unregister(&intel_th_lpp_driver);337}338 339module_exit(intel_th_pti_lpp_exit);340 341MODULE_LICENSE("GPL v2");342MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver");343MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");344