// SPDX-License-Identifier: GPL-2.0
/*
* Intel(R) Trace Hub Global Trace Hub
*
* Copyright (C) 2014-2015 Intel Corporation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/types.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/bitmap.h>
#include <linux/pm_runtime.h>
#include "intel_th.h"
#include "gth.h"
struct gth_device;
/**
* struct gth_output - GTH view on an output port
* @gth: backlink to the GTH device
* @output: link to output device's output descriptor
* @index: output port number
* @port_type: one of GTH_* port type values
* @master: bitmap of masters configured for this output
*/
struct gth_output {
struct gth_device *gth;
struct intel_th_output *output;
unsigned int index;
unsigned int port_type;
DECLARE_BITMAP(master, TH_CONFIGURABLE_MASTERS + 1);
};
/**
* struct gth_device - GTH device
* @dev: driver core's device
* @base: register window base address
* @output_group: attributes describing output ports
* @master_group: attributes describing master assignments
* @output: output ports
* @master: master/output port assignments
* @gth_lock: serializes accesses to GTH bits
*/
struct gth_device {
struct device *dev;
void __iomem *base;
struct attribute_group output_group;
struct attribute_group master_group;
struct gth_output output[TH_POSSIBLE_OUTPUTS];
signed char master[TH_CONFIGURABLE_MASTERS + 1];
spinlock_t gth_lock;
};
static void gth_output_set(struct gth_device *gth, int port,
unsigned int config)
{
unsigned long reg = port & 4 ? REG_GTH_GTHOPT1 : REG_GTH_GTHOPT0;
u32 val;
int shift = (port & 3) * 8;
val = ioread32(gth->base + reg);
val &= ~(0xff << shift);
val |= config << shift;
iowrite32(val, gth->base + reg);
}
static unsigned int gth_output_get(struct gth_device *gth, int port)
{
unsigned long reg = port & 4 ? REG_GTH_GTHOPT1 : REG_GTH_GTHOPT0;
u32 val;
int shift = (port & 3) * 8;
val = ioread32(gth->base + reg);
val &= 0xff << shift;
val >>= shift;
return val;
}
static void gth_smcfreq_set(struct gth_device *gth, int port,
unsigned int freq)
{
unsigned long reg = REG_GTH_SMCR0 + ((port / 2) * 4);
int shift = (port & 1) * 16;
u32 val;
val = ioread32(gth->base + reg);
val &= ~(0xffff << shift);
val |= freq << shift;
iowrite32(val, gth->base + reg);
}
static unsigned int gth_smcfreq_get(struct gth_device *gth, int port)
{
unsigned long reg = REG_GTH_SMCR0 + ((port / 2) * 4);
int shift = (port & 1) * 16;
u32 val;
val = ioread32(gth->base + reg);
val &= 0xffff << shift;
val >>= shift;
return val;
}
/*
* "masters" attribute group
*/
struct master_attribute {
struct device_attribute attr;
struct gth_device *gth;
unsigned int master;
};
static void
gth_master_set(struct gth_device *gth, unsigned int master, int port)
{
unsigned int reg = REG_GTH_SWDEST0 + ((master >> 1) & ~3u);
unsigned int shift = (master & 0x7) * 4;
u32 val;
if (master >= 256) {
reg = REG_GTH_GSWTDEST;
shift = 0;
}
val = ioread32(gth->base + reg);
val &= ~(0xf << shift);
if (port >= 0)
val |= (0x8 | port) << shift;
iowrite32(val, gth->base + reg);
}
static ssize_t master_attr_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct master_attribute *ma =
container_of(attr, struct master_attribute, attr);
struct gth_device *gth = ma->gth;
size_t count;
int port;
spin_lock(>h->gth_lock);
port = gth->master[ma->master];
spin_unlock(>h->gth_lock);
if (port >= 0)
count = snprintf(buf, PAGE_SIZE, "%x\n", port);
else
count = snprintf(buf, PAGE_SIZE, "disabled\n");
return count;
}
static ssize_t master_attr_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t