162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Intel(R) Trace Hub Software Trace Hub support 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014-2015 Intel Corporation. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/types.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/device.h> 1362306a36Sopenharmony_ci#include <linux/io.h> 1462306a36Sopenharmony_ci#include <linux/mm.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/stm.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "intel_th.h" 1962306a36Sopenharmony_ci#include "sth.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct sth_device { 2262306a36Sopenharmony_ci void __iomem *base; 2362306a36Sopenharmony_ci void __iomem *channels; 2462306a36Sopenharmony_ci phys_addr_t channels_phys; 2562306a36Sopenharmony_ci struct device *dev; 2662306a36Sopenharmony_ci struct stm_data stm; 2762306a36Sopenharmony_ci unsigned int sw_nmasters; 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic struct intel_th_channel __iomem * 3162306a36Sopenharmony_cisth_channel(struct sth_device *sth, unsigned int master, unsigned int channel) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci struct intel_th_channel __iomem *sw_map = sth->channels; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci return &sw_map[(master - sth->stm.sw_start) * sth->stm.sw_nchannels + 3662306a36Sopenharmony_ci channel]; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic void sth_iowrite(void __iomem *dest, const unsigned char *payload, 4062306a36Sopenharmony_ci unsigned int size) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci switch (size) { 4362306a36Sopenharmony_ci#ifdef CONFIG_64BIT 4462306a36Sopenharmony_ci case 8: 4562306a36Sopenharmony_ci writeq_relaxed(*(u64 *)payload, dest); 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci#endif 4862306a36Sopenharmony_ci case 4: 4962306a36Sopenharmony_ci writel_relaxed(*(u32 *)payload, dest); 5062306a36Sopenharmony_ci break; 5162306a36Sopenharmony_ci case 2: 5262306a36Sopenharmony_ci writew_relaxed(*(u16 *)payload, dest); 5362306a36Sopenharmony_ci break; 5462306a36Sopenharmony_ci case 1: 5562306a36Sopenharmony_ci writeb_relaxed(*(u8 *)payload, dest); 5662306a36Sopenharmony_ci break; 5762306a36Sopenharmony_ci default: 5862306a36Sopenharmony_ci break; 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistatic ssize_t notrace sth_stm_packet(struct stm_data *stm_data, 6362306a36Sopenharmony_ci unsigned int master, 6462306a36Sopenharmony_ci unsigned int channel, 6562306a36Sopenharmony_ci unsigned int packet, 6662306a36Sopenharmony_ci unsigned int flags, 6762306a36Sopenharmony_ci unsigned int size, 6862306a36Sopenharmony_ci const unsigned char *payload) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct sth_device *sth = container_of(stm_data, struct sth_device, stm); 7162306a36Sopenharmony_ci struct intel_th_channel __iomem *out = 7262306a36Sopenharmony_ci sth_channel(sth, master, channel); 7362306a36Sopenharmony_ci u64 __iomem *outp = &out->Dn; 7462306a36Sopenharmony_ci unsigned long reg = REG_STH_TRIG; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#ifndef CONFIG_64BIT 7762306a36Sopenharmony_ci if (size > 4) 7862306a36Sopenharmony_ci size = 4; 7962306a36Sopenharmony_ci#endif 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci size = rounddown_pow_of_two(size); 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci switch (packet) { 8462306a36Sopenharmony_ci /* Global packets (GERR, XSYNC, TRIG) are sent with register writes */ 8562306a36Sopenharmony_ci case STP_PACKET_GERR: 8662306a36Sopenharmony_ci reg += 4; 8762306a36Sopenharmony_ci fallthrough; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci case STP_PACKET_XSYNC: 9062306a36Sopenharmony_ci reg += 8; 9162306a36Sopenharmony_ci fallthrough; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci case STP_PACKET_TRIG: 9462306a36Sopenharmony_ci if (flags & STP_PACKET_TIMESTAMPED) 9562306a36Sopenharmony_ci reg += 4; 9662306a36Sopenharmony_ci writeb_relaxed(*payload, sth->base + reg); 9762306a36Sopenharmony_ci break; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci case STP_PACKET_MERR: 10062306a36Sopenharmony_ci if (size > 4) 10162306a36Sopenharmony_ci size = 4; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci sth_iowrite(&out->MERR, payload, size); 10462306a36Sopenharmony_ci break; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci case STP_PACKET_FLAG: 10762306a36Sopenharmony_ci if (flags & STP_PACKET_TIMESTAMPED) 10862306a36Sopenharmony_ci outp = (u64 __iomem *)&out->FLAG_TS; 10962306a36Sopenharmony_ci else 11062306a36Sopenharmony_ci outp = (u64 __iomem *)&out->FLAG; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci size = 0; 11362306a36Sopenharmony_ci writeb_relaxed(0, outp); 11462306a36Sopenharmony_ci break; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci case STP_PACKET_USER: 11762306a36Sopenharmony_ci if (flags & STP_PACKET_TIMESTAMPED) 11862306a36Sopenharmony_ci outp = &out->USER_TS; 11962306a36Sopenharmony_ci else 12062306a36Sopenharmony_ci outp = &out->USER; 12162306a36Sopenharmony_ci sth_iowrite(outp, payload, size); 12262306a36Sopenharmony_ci break; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci case STP_PACKET_DATA: 12562306a36Sopenharmony_ci outp = &out->Dn; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (flags & STP_PACKET_TIMESTAMPED) 12862306a36Sopenharmony_ci outp += 2; 12962306a36Sopenharmony_ci if (flags & STP_PACKET_MARKED) 13062306a36Sopenharmony_ci outp++; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci sth_iowrite(outp, payload, size); 13362306a36Sopenharmony_ci break; 13462306a36Sopenharmony_ci default: 13562306a36Sopenharmony_ci return -ENOTSUPP; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci return size; 13962306a36Sopenharmony_ci} 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_cistatic phys_addr_t 14262306a36Sopenharmony_cisth_stm_mmio_addr(struct stm_data *stm_data, unsigned int master, 14362306a36Sopenharmony_ci unsigned int channel, unsigned int nr_chans) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct sth_device *sth = container_of(stm_data, struct sth_device, stm); 14662306a36Sopenharmony_ci phys_addr_t addr; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci master -= sth->stm.sw_start; 14962306a36Sopenharmony_ci addr = sth->channels_phys + (master * sth->stm.sw_nchannels + channel) * 15062306a36Sopenharmony_ci sizeof(struct intel_th_channel); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (offset_in_page(addr) || 15362306a36Sopenharmony_ci offset_in_page(nr_chans * sizeof(struct intel_th_channel))) 15462306a36Sopenharmony_ci return 0; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return addr; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int sth_stm_link(struct stm_data *stm_data, unsigned int master, 16062306a36Sopenharmony_ci unsigned int channel) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct sth_device *sth = container_of(stm_data, struct sth_device, stm); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return intel_th_set_output(to_intel_th_device(sth->dev), master); 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic int intel_th_sw_init(struct sth_device *sth) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci u32 reg; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci reg = ioread32(sth->base + REG_STH_STHCAP1); 17262306a36Sopenharmony_ci sth->stm.sw_nchannels = reg & 0xff; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci reg = ioread32(sth->base + REG_STH_STHCAP0); 17562306a36Sopenharmony_ci sth->stm.sw_start = reg & 0xffff; 17662306a36Sopenharmony_ci sth->stm.sw_end = reg >> 16; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci sth->sw_nmasters = sth->stm.sw_end - sth->stm.sw_start; 17962306a36Sopenharmony_ci dev_dbg(sth->dev, "sw_start: %x sw_end: %x masters: %x nchannels: %x\n", 18062306a36Sopenharmony_ci sth->stm.sw_start, sth->stm.sw_end, sth->sw_nmasters, 18162306a36Sopenharmony_ci sth->stm.sw_nchannels); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci return 0; 18462306a36Sopenharmony_ci} 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int intel_th_sth_probe(struct intel_th_device *thdev) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct device *dev = &thdev->dev; 18962306a36Sopenharmony_ci struct sth_device *sth; 19062306a36Sopenharmony_ci struct resource *res; 19162306a36Sopenharmony_ci void __iomem *base, *channels; 19262306a36Sopenharmony_ci int err; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0); 19562306a36Sopenharmony_ci if (!res) 19662306a36Sopenharmony_ci return -ENODEV; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci base = devm_ioremap(dev, res->start, resource_size(res)); 19962306a36Sopenharmony_ci if (!base) 20062306a36Sopenharmony_ci return -ENOMEM; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 1); 20362306a36Sopenharmony_ci if (!res) 20462306a36Sopenharmony_ci return -ENODEV; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci channels = devm_ioremap(dev, res->start, resource_size(res)); 20762306a36Sopenharmony_ci if (!channels) 20862306a36Sopenharmony_ci return -ENOMEM; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci sth = devm_kzalloc(dev, sizeof(*sth), GFP_KERNEL); 21162306a36Sopenharmony_ci if (!sth) 21262306a36Sopenharmony_ci return -ENOMEM; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci sth->dev = dev; 21562306a36Sopenharmony_ci sth->base = base; 21662306a36Sopenharmony_ci sth->channels = channels; 21762306a36Sopenharmony_ci sth->channels_phys = res->start; 21862306a36Sopenharmony_ci sth->stm.name = dev_name(dev); 21962306a36Sopenharmony_ci sth->stm.packet = sth_stm_packet; 22062306a36Sopenharmony_ci sth->stm.mmio_addr = sth_stm_mmio_addr; 22162306a36Sopenharmony_ci sth->stm.sw_mmiosz = sizeof(struct intel_th_channel); 22262306a36Sopenharmony_ci sth->stm.link = sth_stm_link; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci err = intel_th_sw_init(sth); 22562306a36Sopenharmony_ci if (err) 22662306a36Sopenharmony_ci return err; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci err = stm_register_device(dev, &sth->stm, THIS_MODULE); 22962306a36Sopenharmony_ci if (err) { 23062306a36Sopenharmony_ci dev_err(dev, "stm_register_device failed\n"); 23162306a36Sopenharmony_ci return err; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci dev_set_drvdata(dev, sth); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void intel_th_sth_remove(struct intel_th_device *thdev) 24062306a36Sopenharmony_ci{ 24162306a36Sopenharmony_ci struct sth_device *sth = dev_get_drvdata(&thdev->dev); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci stm_unregister_device(&sth->stm); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic struct intel_th_driver intel_th_sth_driver = { 24762306a36Sopenharmony_ci .probe = intel_th_sth_probe, 24862306a36Sopenharmony_ci .remove = intel_th_sth_remove, 24962306a36Sopenharmony_ci .driver = { 25062306a36Sopenharmony_ci .name = "sth", 25162306a36Sopenharmony_ci .owner = THIS_MODULE, 25262306a36Sopenharmony_ci }, 25362306a36Sopenharmony_ci}; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cimodule_driver(intel_th_sth_driver, 25662306a36Sopenharmony_ci intel_th_driver_register, 25762306a36Sopenharmony_ci intel_th_driver_unregister); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 26062306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel(R) Trace Hub Software Trace Hub driver"); 26162306a36Sopenharmony_ciMODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@intel.com>"); 262