ptp: ocp: Add SMA selector and controls
The latest firmware for the TimeCard adds selectable signals for the SMA input/outputs. Add support for SMA selectors, and the GPIO controls needed for steering signals. Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
dcf614692c
Коммит
e1daf0ec73
|
@ -124,6 +124,13 @@ struct img_reg {
|
|||
u32 version;
|
||||
};
|
||||
|
||||
struct gpio_reg {
|
||||
u32 gpio1;
|
||||
u32 __pad0;
|
||||
u32 gpio2;
|
||||
u32 __pad1;
|
||||
};
|
||||
|
||||
struct ptp_ocp_flash_info {
|
||||
const char *name;
|
||||
int pci_offset;
|
||||
|
@ -159,6 +166,7 @@ struct ptp_ocp {
|
|||
struct tod_reg __iomem *tod;
|
||||
struct pps_reg __iomem *pps_to_ext;
|
||||
struct pps_reg __iomem *pps_to_clk;
|
||||
struct gpio_reg __iomem *sma;
|
||||
struct ptp_ocp_ext_src *pps;
|
||||
struct ptp_ocp_ext_src *ts0;
|
||||
struct ptp_ocp_ext_src *ts1;
|
||||
|
@ -283,6 +291,10 @@ static struct ocp_resource ocp_fb_resource[] = {
|
|||
OCP_MEM_RESOURCE(image),
|
||||
.offset = 0x00020000, .size = 0x1000,
|
||||
},
|
||||
{
|
||||
OCP_MEM_RESOURCE(sma),
|
||||
.offset = 0x00140000, .size = 0x1000,
|
||||
},
|
||||
{
|
||||
OCP_I2C_RESOURCE(i2c_ctrl),
|
||||
.offset = 0x00150000, .size = 0x10000, .irq_vec = 7,
|
||||
|
@ -330,10 +342,12 @@ MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id);
|
|||
static DEFINE_MUTEX(ptp_ocp_lock);
|
||||
static DEFINE_IDR(ptp_ocp_idr);
|
||||
|
||||
static struct {
|
||||
struct ocp_selector {
|
||||
const char *name;
|
||||
int value;
|
||||
} ptp_ocp_clock[] = {
|
||||
};
|
||||
|
||||
static struct ocp_selector ptp_ocp_clock[] = {
|
||||
{ .name = "NONE", .value = 0 },
|
||||
{ .name = "TOD", .value = 1 },
|
||||
{ .name = "IRIG", .value = 2 },
|
||||
|
@ -343,33 +357,67 @@ static struct {
|
|||
{ .name = "DCF", .value = 6 },
|
||||
{ .name = "REGS", .value = 0xfe },
|
||||
{ .name = "EXT", .value = 0xff },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct ocp_selector ptp_ocp_sma_in[] = {
|
||||
{ .name = "10Mhz", .value = 0x00 },
|
||||
{ .name = "PPS1", .value = 0x01 },
|
||||
{ .name = "PPS2", .value = 0x02 },
|
||||
{ .name = "TS1", .value = 0x04 },
|
||||
{ .name = "TS2", .value = 0x08 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct ocp_selector ptp_ocp_sma_out[] = {
|
||||
{ .name = "10Mhz", .value = 0x00 },
|
||||
{ .name = "PHC", .value = 0x01 },
|
||||
{ .name = "MAC", .value = 0x02 },
|
||||
{ .name = "GNSS", .value = 0x04 },
|
||||
{ .name = "GNSS2", .value = 0x08 },
|
||||
{ }
|
||||
};
|
||||
|
||||
static const char *
|
||||
ptp_ocp_clock_name_from_val(int val)
|
||||
ptp_ocp_select_name_from_val(struct ocp_selector *tbl, int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ptp_ocp_clock); i++)
|
||||
if (ptp_ocp_clock[i].value == val)
|
||||
return ptp_ocp_clock[i].name;
|
||||
for (i = 0; tbl[i].name; i++)
|
||||
if (tbl[i].value == val)
|
||||
return tbl[i].name;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
ptp_ocp_clock_val_from_name(const char *name)
|
||||
ptp_ocp_select_val_from_name(struct ocp_selector *tbl, const char *name)
|
||||
{
|
||||
const char *clk;
|
||||
const char *select;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ptp_ocp_clock); i++) {
|
||||
clk = ptp_ocp_clock[i].name;
|
||||
if (!strncasecmp(name, clk, strlen(clk)))
|
||||
return ptp_ocp_clock[i].value;
|
||||
for (i = 0; tbl[i].name; i++) {
|
||||
select = tbl[i].name;
|
||||
if (!strncasecmp(name, select, strlen(select)))
|
||||
return tbl[i].value;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ptp_ocp_select_table_show(struct ocp_selector *tbl, char *buf)
|
||||
{
|
||||
ssize_t count;
|
||||
int i;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; tbl[i].name; i++)
|
||||
count += sysfs_emit_at(buf, count, "%s ", tbl[i].name);
|
||||
if (count)
|
||||
count--;
|
||||
count += sysfs_emit_at(buf, count, "\n");
|
||||
return count;
|
||||
}
|
||||
|
||||
static int
|
||||
__ptp_ocp_gettime_locked(struct ptp_ocp *bp, struct timespec64 *ts,
|
||||
struct ptp_system_timestamp *sts)
|
||||
|
@ -756,7 +804,7 @@ ptp_ocp_info(struct ptp_ocp *bp)
|
|||
select = ioread32(&bp->reg->select);
|
||||
dev_info(&bp->pdev->dev, "Version %d.%d.%d, clock %s, device ptp%d\n",
|
||||
version >> 24, (version >> 16) & 0xff, version & 0xffff,
|
||||
ptp_ocp_clock_name_from_val(select >> 16),
|
||||
ptp_ocp_select_name_from_val(ptp_ocp_clock, select >> 16),
|
||||
ptp_clock_index(bp->ptp));
|
||||
|
||||
if (bp->tod)
|
||||
|
@ -1181,6 +1229,297 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
|
|||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* ANT0 == gps (in)
|
||||
* ANT1 == sma1 (in)
|
||||
* ANT2 == sma2 (in)
|
||||
* ANT3 == sma3 (out)
|
||||
* ANT4 == sma4 (out)
|
||||
*/
|
||||
|
||||
enum ptp_ocp_sma_mode {
|
||||
SMA_MODE_IN,
|
||||
SMA_MODE_OUT,
|
||||
};
|
||||
|
||||
static struct ptp_ocp_sma_connector {
|
||||
enum ptp_ocp_sma_mode mode;
|
||||
bool fixed_mode;
|
||||
u16 default_out_idx;
|
||||
} ptp_ocp_sma_map[4] = {
|
||||
{
|
||||
.mode = SMA_MODE_IN,
|
||||
.fixed_mode = true,
|
||||
},
|
||||
{
|
||||
.mode = SMA_MODE_IN,
|
||||
.fixed_mode = true,
|
||||
},
|
||||
{
|
||||
.mode = SMA_MODE_OUT,
|
||||
.fixed_mode = true,
|
||||
.default_out_idx = 0, /* 10Mhz */
|
||||
},
|
||||
{
|
||||
.mode = SMA_MODE_OUT,
|
||||
.fixed_mode = true,
|
||||
.default_out_idx = 1, /* PHC */
|
||||
},
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
ptp_ocp_show_output(u32 val, char *buf, int default_idx)
|
||||
{
|
||||
const char *name;
|
||||
ssize_t count;
|
||||
|
||||
count = sysfs_emit(buf, "OUT: ");
|
||||
name = ptp_ocp_select_name_from_val(ptp_ocp_sma_out, val);
|
||||
if (!name)
|
||||
name = ptp_ocp_sma_out[default_idx].name;
|
||||
count += sysfs_emit_at(buf, count, "%s\n", name);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ptp_ocp_show_inputs(u32 val, char *buf, const char *zero_in)
|
||||
{
|
||||
const char *name;
|
||||
ssize_t count;
|
||||
int i;
|
||||
|
||||
count = sysfs_emit(buf, "IN: ");
|
||||
for (i = 0; i < ARRAY_SIZE(ptp_ocp_sma_in); i++) {
|
||||
if (val & ptp_ocp_sma_in[i].value) {
|
||||
name = ptp_ocp_sma_in[i].name;
|
||||
count += sysfs_emit_at(buf, count, "%s ", name);
|
||||
}
|
||||
}
|
||||
if (!val && zero_in)
|
||||
count += sysfs_emit_at(buf, count, "%s ", zero_in);
|
||||
if (count)
|
||||
count--;
|
||||
count += sysfs_emit_at(buf, count, "\n");
|
||||
return count;
|
||||
}
|
||||
|
||||
static int
|
||||
sma_parse_inputs(const char *buf, enum ptp_ocp_sma_mode *mode)
|
||||
{
|
||||
struct ocp_selector *tbl[] = { ptp_ocp_sma_in, ptp_ocp_sma_out };
|
||||
int idx, count, dir;
|
||||
char **argv;
|
||||
int ret;
|
||||
|
||||
argv = argv_split(GFP_KERNEL, buf, &count);
|
||||
if (!argv)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (!count)
|
||||
goto out;
|
||||
|
||||
idx = 0;
|
||||
dir = *mode == SMA_MODE_IN ? 0 : 1;
|
||||
if (!strcasecmp("IN:", argv[idx])) {
|
||||
dir = 0;
|
||||
idx++;
|
||||
}
|
||||
if (!strcasecmp("OUT:", argv[0])) {
|
||||
dir = 1;
|
||||
idx++;
|
||||
}
|
||||
*mode = dir == 0 ? SMA_MODE_IN : SMA_MODE_OUT;
|
||||
|
||||
ret = 0;
|
||||
for (; idx < count; idx++)
|
||||
ret |= ptp_ocp_select_val_from_name(tbl[dir], argv[idx]);
|
||||
if (ret < 0)
|
||||
ret = -EINVAL;
|
||||
|
||||
out:
|
||||
argv_free(argv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ptp_ocp_sma_show(struct ptp_ocp *bp, int sma_nr, u32 val, char *buf,
|
||||
const char *zero_in)
|
||||
{
|
||||
struct ptp_ocp_sma_connector *sma = &ptp_ocp_sma_map[sma_nr - 1];
|
||||
|
||||
if (sma->mode == SMA_MODE_IN)
|
||||
return ptp_ocp_show_inputs(val, buf, zero_in);
|
||||
|
||||
return ptp_ocp_show_output(val, buf, sma->default_out_idx);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
sma1_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ptp_ocp *bp = dev_get_drvdata(dev);
|
||||
u32 val;
|
||||
|
||||
val = ioread32(&bp->sma->gpio1) & 0x3f;
|
||||
return ptp_ocp_sma_show(bp, 1, val, buf, ptp_ocp_sma_in[0].name);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
sma2_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ptp_ocp *bp = dev_get_drvdata(dev);
|
||||
u32 val;
|
||||
|
||||
val = (ioread32(&bp->sma->gpio1) >> 16) & 0x3f;
|
||||
return ptp_ocp_sma_show(bp, 2, val, buf, NULL);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
sma3_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ptp_ocp *bp = dev_get_drvdata(dev);
|
||||
u32 val;
|
||||
|
||||
val = ioread32(&bp->sma->gpio2) & 0x3f;
|
||||
return ptp_ocp_sma_show(bp, 3, val, buf, NULL);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct ptp_ocp *bp = dev_get_drvdata(dev);
|
||||
u32 val;
|
||||
|
||||
val = (ioread32(&bp->sma->gpio2) >> 16) & 0x3f;
|
||||
return ptp_ocp_sma_show(bp, 4, val, buf, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
ptp_ocp_sma_store_output(struct ptp_ocp *bp, u32 val, u32 shift)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 gpio, mask;
|
||||
|
||||
mask = 0xffff << (16 - shift);
|
||||
|
||||
spin_lock_irqsave(&bp->lock, flags);
|
||||
|
||||
gpio = ioread32(&bp->sma->gpio2);
|
||||
gpio = (gpio & mask) | (val << shift);
|
||||
iowrite32(gpio, &bp->sma->gpio2);
|
||||
|
||||
spin_unlock_irqrestore(&bp->lock, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
ptp_ocp_sma_store_inputs(struct ptp_ocp *bp, u32 val, u32 shift)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 gpio, mask;
|
||||
|
||||
mask = 0xffff << (16 - shift);
|
||||
|
||||
spin_lock_irqsave(&bp->lock, flags);
|
||||
|
||||
gpio = ioread32(&bp->sma->gpio1);
|
||||
gpio = (gpio & mask) | (val << shift);
|
||||
iowrite32(gpio, &bp->sma->gpio1);
|
||||
|
||||
spin_unlock_irqrestore(&bp->lock, flags);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr, u32 shift)
|
||||
{
|
||||
struct ptp_ocp_sma_connector *sma = &ptp_ocp_sma_map[sma_nr - 1];
|
||||
enum ptp_ocp_sma_mode mode;
|
||||
int val;
|
||||
|
||||
mode = sma->mode;
|
||||
val = sma_parse_inputs(buf, &mode);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
if (mode != sma->mode && sma->fixed_mode)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (mode != sma->mode) {
|
||||
pr_err("Mode changes not supported yet.\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (sma->mode == SMA_MODE_IN)
|
||||
ptp_ocp_sma_store_inputs(bp, val, shift);
|
||||
else
|
||||
ptp_ocp_sma_store_output(bp, val, shift);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
sma1_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ptp_ocp *bp = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = ptp_ocp_sma_store(bp, buf, 1, 0);
|
||||
return err ? err : count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
sma2_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ptp_ocp *bp = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = ptp_ocp_sma_store(bp, buf, 2, 16);
|
||||
return err ? err : count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
sma3_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ptp_ocp *bp = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = ptp_ocp_sma_store(bp, buf, 3, 0);
|
||||
return err ? err : count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
sma4_store(struct device *dev, struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct ptp_ocp *bp = dev_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
err = ptp_ocp_sma_store(bp, buf, 4, 16);
|
||||
return err ? err : count;
|
||||
}
|
||||
static DEVICE_ATTR_RW(sma1);
|
||||
static DEVICE_ATTR_RW(sma2);
|
||||
static DEVICE_ATTR_RW(sma3);
|
||||
static DEVICE_ATTR_RW(sma4);
|
||||
|
||||
static ssize_t
|
||||
available_sma_inputs_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return ptp_ocp_select_table_show(ptp_ocp_sma_in, buf);
|
||||
}
|
||||
static DEVICE_ATTR_RO(available_sma_inputs);
|
||||
|
||||
static ssize_t
|
||||
available_sma_outputs_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
return ptp_ocp_select_table_show(ptp_ocp_sma_out, buf);
|
||||
}
|
||||
static DEVICE_ATTR_RO(available_sma_outputs);
|
||||
|
||||
static ssize_t
|
||||
serialnum_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
|
@ -1216,7 +1555,7 @@ clock_source_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
u32 select;
|
||||
|
||||
select = ioread32(&bp->reg->select);
|
||||
p = ptp_ocp_clock_name_from_val(select >> 16);
|
||||
p = ptp_ocp_select_name_from_val(ptp_ocp_clock, select >> 16);
|
||||
|
||||
return sysfs_emit(buf, "%s\n", p);
|
||||
}
|
||||
|
@ -1229,7 +1568,7 @@ clock_source_store(struct device *dev, struct device_attribute *attr,
|
|||
unsigned long flags;
|
||||
int val;
|
||||
|
||||
val = ptp_ocp_clock_val_from_name(buf);
|
||||
val = ptp_ocp_select_val_from_name(ptp_ocp_clock, buf);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
|
@ -1245,19 +1584,7 @@ static ssize_t
|
|||
available_clock_sources_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
const char *clk;
|
||||
ssize_t count;
|
||||
int i;
|
||||
|
||||
count = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(ptp_ocp_clock); i++) {
|
||||
clk = ptp_ocp_clock[i].name;
|
||||
count += sysfs_emit_at(buf, count, "%s ", clk);
|
||||
}
|
||||
if (count)
|
||||
count--;
|
||||
count += sysfs_emit_at(buf, count, "\n");
|
||||
return count;
|
||||
return ptp_ocp_select_table_show(ptp_ocp_clock, buf);
|
||||
}
|
||||
static DEVICE_ATTR_RO(available_clock_sources);
|
||||
|
||||
|
@ -1266,6 +1593,12 @@ static struct attribute *timecard_attrs[] = {
|
|||
&dev_attr_gnss_sync.attr,
|
||||
&dev_attr_clock_source.attr,
|
||||
&dev_attr_available_clock_sources.attr,
|
||||
&dev_attr_sma1.attr,
|
||||
&dev_attr_sma2.attr,
|
||||
&dev_attr_sma3.attr,
|
||||
&dev_attr_sma4.attr,
|
||||
&dev_attr_available_sma_inputs.attr,
|
||||
&dev_attr_available_sma_outputs.attr,
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(timecard);
|
||||
|
|
Загрузка…
Ссылка в новой задаче