mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-11-01 17:08:10 +00:00
Merge branch 'ptp-ocp-various-updates'
Jonathan Lemon says: ==================== ptp: ocp: various updates Collection of cleanups and updates to the timecard. ==================== Link: https://lore.kernel.org/r/20220519212153.450437-1-jonathan.lemon@gmail.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
57d7becda9
1 changed files with 407 additions and 270 deletions
|
@ -19,14 +19,13 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/crc16.h>
|
||||
|
||||
#ifndef PCI_VENDOR_ID_FACEBOOK
|
||||
#define PCI_VENDOR_ID_FACEBOOK 0x1d9b
|
||||
#endif
|
||||
#define PCI_VENDOR_ID_FACEBOOK 0x1d9b
|
||||
#define PCI_DEVICE_ID_FACEBOOK_TIMECARD 0x0400
|
||||
|
||||
#ifndef PCI_DEVICE_ID_FACEBOOK_TIMECARD
|
||||
#define PCI_DEVICE_ID_FACEBOOK_TIMECARD 0x0400
|
||||
#endif
|
||||
#define PCI_VENDOR_ID_CELESTICA 0x18d4
|
||||
#define PCI_DEVICE_ID_CELESTICA_TIMECARD 0x1008
|
||||
|
||||
static struct class timecard_class = {
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -215,6 +214,17 @@ struct ptp_ocp_flash_info {
|
|||
void *data;
|
||||
};
|
||||
|
||||
struct ptp_ocp_firmware_header {
|
||||
char magic[4];
|
||||
__be16 pci_vendor_id;
|
||||
__be16 pci_device_id;
|
||||
__be32 image_size;
|
||||
__be16 hw_revision;
|
||||
__be16 crc;
|
||||
};
|
||||
|
||||
#define OCP_FIRMWARE_MAGIC_HEADER "OCPC"
|
||||
|
||||
struct ptp_ocp_i2c_info {
|
||||
const char *name;
|
||||
unsigned long fixed_rate;
|
||||
|
@ -245,6 +255,7 @@ struct ptp_ocp_sma_connector {
|
|||
bool fixed_fcn;
|
||||
bool fixed_dir;
|
||||
bool disabled;
|
||||
u8 default_fcn;
|
||||
};
|
||||
|
||||
struct ocp_attr_group {
|
||||
|
@ -310,7 +321,9 @@ struct ptp_ocp {
|
|||
int gnss2_port;
|
||||
int mac_port; /* miniature atomic clock */
|
||||
int nmea_port;
|
||||
u32 fw_version;
|
||||
bool fw_loader;
|
||||
u8 fw_tag;
|
||||
u16 fw_version;
|
||||
u8 board_id[OCP_BOARD_ID_LEN];
|
||||
u8 serial[OCP_SERIAL_LEN];
|
||||
bool has_eeprom_data;
|
||||
|
@ -321,6 +334,7 @@ struct ptp_ocp {
|
|||
u64 fw_cap;
|
||||
struct ptp_ocp_signal signal[4];
|
||||
struct ptp_ocp_sma_connector sma[4];
|
||||
const struct ocp_sma_op *sma_op;
|
||||
};
|
||||
|
||||
#define OCP_REQ_TIMESTAMP BIT(0)
|
||||
|
@ -634,7 +648,8 @@ static struct ocp_resource ocp_fb_resource[] = {
|
|||
|
||||
static const struct pci_device_id ptp_ocp_pcidev_id[] = {
|
||||
{ PCI_DEVICE_DATA(FACEBOOK, TIMECARD, &ocp_fb_resource) },
|
||||
{ 0 }
|
||||
{ PCI_DEVICE_DATA(CELESTICA, TIMECARD, &ocp_fb_resource) },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id);
|
||||
|
||||
|
@ -646,7 +661,7 @@ struct ocp_selector {
|
|||
int value;
|
||||
};
|
||||
|
||||
static struct ocp_selector ptp_ocp_clock[] = {
|
||||
static const struct ocp_selector ptp_ocp_clock[] = {
|
||||
{ .name = "NONE", .value = 0 },
|
||||
{ .name = "TOD", .value = 1 },
|
||||
{ .name = "IRIG", .value = 2 },
|
||||
|
@ -663,7 +678,7 @@ static struct ocp_selector ptp_ocp_clock[] = {
|
|||
#define SMA_SELECT_MASK ((1U << 15) - 1)
|
||||
#define SMA_DISABLE 0x10000
|
||||
|
||||
static struct ocp_selector ptp_ocp_sma_in[] = {
|
||||
static const struct ocp_selector ptp_ocp_sma_in[] = {
|
||||
{ .name = "10Mhz", .value = 0x0000 },
|
||||
{ .name = "PPS1", .value = 0x0001 },
|
||||
{ .name = "PPS2", .value = 0x0002 },
|
||||
|
@ -681,7 +696,7 @@ static struct ocp_selector ptp_ocp_sma_in[] = {
|
|||
{ }
|
||||
};
|
||||
|
||||
static struct ocp_selector ptp_ocp_sma_out[] = {
|
||||
static const struct ocp_selector ptp_ocp_sma_out[] = {
|
||||
{ .name = "10Mhz", .value = 0x0000 },
|
||||
{ .name = "PHC", .value = 0x0001 },
|
||||
{ .name = "MAC", .value = 0x0002 },
|
||||
|
@ -698,8 +713,40 @@ static struct ocp_selector ptp_ocp_sma_out[] = {
|
|||
{ }
|
||||
};
|
||||
|
||||
struct ocp_sma_op {
|
||||
const struct ocp_selector *tbl[2];
|
||||
void (*init)(struct ptp_ocp *bp);
|
||||
u32 (*get)(struct ptp_ocp *bp, int sma_nr);
|
||||
int (*set_inputs)(struct ptp_ocp *bp, int sma_nr, u32 val);
|
||||
int (*set_output)(struct ptp_ocp *bp, int sma_nr, u32 val);
|
||||
};
|
||||
|
||||
static void
|
||||
ptp_ocp_sma_init(struct ptp_ocp *bp)
|
||||
{
|
||||
return bp->sma_op->init(bp);
|
||||
}
|
||||
|
||||
static u32
|
||||
ptp_ocp_sma_get(struct ptp_ocp *bp, int sma_nr)
|
||||
{
|
||||
return bp->sma_op->get(bp, sma_nr);
|
||||
}
|
||||
|
||||
static int
|
||||
ptp_ocp_sma_set_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
|
||||
{
|
||||
return bp->sma_op->set_inputs(bp, sma_nr, val);
|
||||
}
|
||||
|
||||
static int
|
||||
ptp_ocp_sma_set_output(struct ptp_ocp *bp, int sma_nr, u32 val)
|
||||
{
|
||||
return bp->sma_op->set_output(bp, sma_nr, val);
|
||||
}
|
||||
|
||||
static const char *
|
||||
ptp_ocp_select_name_from_val(struct ocp_selector *tbl, int val)
|
||||
ptp_ocp_select_name_from_val(const struct ocp_selector *tbl, int val)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -710,7 +757,7 @@ ptp_ocp_select_name_from_val(struct ocp_selector *tbl, int val)
|
|||
}
|
||||
|
||||
static int
|
||||
ptp_ocp_select_val_from_name(struct ocp_selector *tbl, const char *name)
|
||||
ptp_ocp_select_val_from_name(const struct ocp_selector *tbl, const char *name)
|
||||
{
|
||||
const char *select;
|
||||
int i;
|
||||
|
@ -724,7 +771,7 @@ ptp_ocp_select_val_from_name(struct ocp_selector *tbl, const char *name)
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
ptp_ocp_select_table_show(struct ocp_selector *tbl, char *buf)
|
||||
ptp_ocp_select_table_show(const struct ocp_selector *tbl, char *buf)
|
||||
{
|
||||
ssize_t count;
|
||||
int i;
|
||||
|
@ -1288,25 +1335,81 @@ ptp_ocp_find_flash(struct ptp_ocp *bp)
|
|||
return dev;
|
||||
}
|
||||
|
||||
static int
|
||||
ptp_ocp_devlink_fw_image(struct devlink *devlink, const struct firmware *fw,
|
||||
const u8 **data, size_t *size)
|
||||
{
|
||||
struct ptp_ocp *bp = devlink_priv(devlink);
|
||||
const struct ptp_ocp_firmware_header *hdr;
|
||||
size_t offset, length;
|
||||
u16 crc;
|
||||
|
||||
hdr = (const struct ptp_ocp_firmware_header *)fw->data;
|
||||
if (memcmp(hdr->magic, OCP_FIRMWARE_MAGIC_HEADER, 4)) {
|
||||
devlink_flash_update_status_notify(devlink,
|
||||
"No firmware header found, flashing raw image",
|
||||
NULL, 0, 0);
|
||||
offset = 0;
|
||||
length = fw->size;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (be16_to_cpu(hdr->pci_vendor_id) != bp->pdev->vendor ||
|
||||
be16_to_cpu(hdr->pci_device_id) != bp->pdev->device) {
|
||||
devlink_flash_update_status_notify(devlink,
|
||||
"Firmware image compatibility check failed",
|
||||
NULL, 0, 0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
offset = sizeof(*hdr);
|
||||
length = be32_to_cpu(hdr->image_size);
|
||||
if (length != (fw->size - offset)) {
|
||||
devlink_flash_update_status_notify(devlink,
|
||||
"Firmware image size check failed",
|
||||
NULL, 0, 0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
crc = crc16(0xffff, &fw->data[offset], length);
|
||||
if (be16_to_cpu(hdr->crc) != crc) {
|
||||
devlink_flash_update_status_notify(devlink,
|
||||
"Firmware image CRC check failed",
|
||||
NULL, 0, 0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
*data = &fw->data[offset];
|
||||
*size = length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ptp_ocp_devlink_flash(struct devlink *devlink, struct device *dev,
|
||||
const struct firmware *fw)
|
||||
{
|
||||
struct mtd_info *mtd = dev_get_drvdata(dev);
|
||||
struct ptp_ocp *bp = devlink_priv(devlink);
|
||||
size_t off, len, resid, wrote;
|
||||
size_t off, len, size, resid, wrote;
|
||||
struct erase_info erase;
|
||||
size_t base, blksz;
|
||||
int err = 0;
|
||||
const u8 *data;
|
||||
int err;
|
||||
|
||||
err = ptp_ocp_devlink_fw_image(devlink, fw, &data, &size);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
off = 0;
|
||||
base = bp->flash_start;
|
||||
blksz = 4096;
|
||||
resid = fw->size;
|
||||
resid = size;
|
||||
|
||||
while (resid) {
|
||||
devlink_flash_update_status_notify(devlink, "Flashing",
|
||||
NULL, off, fw->size);
|
||||
NULL, off, size);
|
||||
|
||||
len = min_t(size_t, resid, blksz);
|
||||
erase.addr = base + off;
|
||||
|
@ -1316,7 +1419,7 @@ ptp_ocp_devlink_flash(struct devlink *devlink, struct device *dev,
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
err = mtd_write(mtd, base + off, len, &wrote, &fw->data[off]);
|
||||
err = mtd_write(mtd, base + off, len, &wrote, data + off);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
|
@ -1360,6 +1463,7 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
|
|||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct ptp_ocp *bp = devlink_priv(devlink);
|
||||
const char *fw_image;
|
||||
char buf[32];
|
||||
int err;
|
||||
|
||||
|
@ -1367,13 +1471,9 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
if (bp->fw_version & 0xffff) {
|
||||
sprintf(buf, "%d", bp->fw_version);
|
||||
err = devlink_info_version_running_put(req, "fw", buf);
|
||||
} else {
|
||||
sprintf(buf, "%d", bp->fw_version >> 16);
|
||||
err = devlink_info_version_running_put(req, "loader", buf);
|
||||
}
|
||||
fw_image = bp->fw_loader ? "loader" : "fw";
|
||||
sprintf(buf, "%d.%d", bp->fw_tag, bp->fw_version);
|
||||
err = devlink_info_version_running_put(req, fw_image, buf);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -1403,7 +1503,7 @@ static const struct devlink_ops ptp_ocp_devlink_ops = {
|
|||
};
|
||||
|
||||
static void __iomem *
|
||||
__ptp_ocp_get_mem(struct ptp_ocp *bp, unsigned long start, int size)
|
||||
__ptp_ocp_get_mem(struct ptp_ocp *bp, resource_size_t start, int size)
|
||||
{
|
||||
struct resource res = DEFINE_RES_MEM_NAMED(start, size, "ptp_ocp");
|
||||
|
||||
|
@ -1413,7 +1513,7 @@ __ptp_ocp_get_mem(struct ptp_ocp *bp, unsigned long start, int size)
|
|||
static void __iomem *
|
||||
ptp_ocp_get_mem(struct ptp_ocp *bp, struct ocp_resource *r)
|
||||
{
|
||||
unsigned long start;
|
||||
resource_size_t start;
|
||||
|
||||
start = pci_resource_start(bp->pdev, 0) + r->offset;
|
||||
return __ptp_ocp_get_mem(bp, start, r->size);
|
||||
|
@ -1427,7 +1527,7 @@ ptp_ocp_set_irq_resource(struct resource *res, int irq)
|
|||
}
|
||||
|
||||
static void
|
||||
ptp_ocp_set_mem_resource(struct resource *res, unsigned long start, int size)
|
||||
ptp_ocp_set_mem_resource(struct resource *res, resource_size_t start, int size)
|
||||
{
|
||||
struct resource r = DEFINE_RES_MEM(start, size);
|
||||
*res = r;
|
||||
|
@ -1440,7 +1540,7 @@ ptp_ocp_register_spi(struct ptp_ocp *bp, struct ocp_resource *r)
|
|||
struct pci_dev *pdev = bp->pdev;
|
||||
struct platform_device *p;
|
||||
struct resource res[2];
|
||||
unsigned long start;
|
||||
resource_size_t start;
|
||||
int id;
|
||||
|
||||
start = pci_resource_start(pdev, 0) + r->offset;
|
||||
|
@ -1467,7 +1567,7 @@ ptp_ocp_i2c_bus(struct pci_dev *pdev, struct ocp_resource *r, int id)
|
|||
{
|
||||
struct ptp_ocp_i2c_info *info;
|
||||
struct resource res[2];
|
||||
unsigned long start;
|
||||
resource_size_t start;
|
||||
|
||||
info = r->extra;
|
||||
start = pci_resource_start(pdev, 0) + r->offset;
|
||||
|
@ -1872,131 +1972,6 @@ ptp_ocp_attr_group_add(struct ptp_ocp *bp,
|
|||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
ptp_ocp_sma_init(struct ptp_ocp *bp)
|
||||
{
|
||||
u32 reg;
|
||||
int i;
|
||||
|
||||
/* defaults */
|
||||
bp->sma[0].mode = SMA_MODE_IN;
|
||||
bp->sma[1].mode = SMA_MODE_IN;
|
||||
bp->sma[2].mode = SMA_MODE_OUT;
|
||||
bp->sma[3].mode = SMA_MODE_OUT;
|
||||
|
||||
/* If no SMA1 map, the pin functions and directions are fixed. */
|
||||
if (!bp->sma_map1) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
bp->sma[i].fixed_fcn = true;
|
||||
bp->sma[i].fixed_dir = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* If SMA2 GPIO output map is all 1, it is not present.
|
||||
* This indicates the firmware has fixed direction SMA pins.
|
||||
*/
|
||||
reg = ioread32(&bp->sma_map2->gpio2);
|
||||
if (reg == 0xffffffff) {
|
||||
for (i = 0; i < 4; i++)
|
||||
bp->sma[i].fixed_dir = true;
|
||||
} else {
|
||||
reg = ioread32(&bp->sma_map1->gpio1);
|
||||
bp->sma[0].mode = reg & BIT(15) ? SMA_MODE_IN : SMA_MODE_OUT;
|
||||
bp->sma[1].mode = reg & BIT(31) ? SMA_MODE_IN : SMA_MODE_OUT;
|
||||
|
||||
reg = ioread32(&bp->sma_map1->gpio2);
|
||||
bp->sma[2].mode = reg & BIT(15) ? SMA_MODE_OUT : SMA_MODE_IN;
|
||||
bp->sma[3].mode = reg & BIT(31) ? SMA_MODE_OUT : SMA_MODE_IN;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ptp_ocp_fb_set_pins(struct ptp_ocp *bp)
|
||||
{
|
||||
struct ptp_pin_desc *config;
|
||||
int i;
|
||||
|
||||
config = kzalloc(sizeof(*config) * 4, GFP_KERNEL);
|
||||
if (!config)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
sprintf(config[i].name, "sma%d", i + 1);
|
||||
config[i].index = i;
|
||||
}
|
||||
|
||||
bp->ptp_info.n_pins = 4;
|
||||
bp->ptp_info.pin_config = config;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FB specific board initializers; last "resource" registered. */
|
||||
static int
|
||||
ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
|
||||
{
|
||||
int ver, err;
|
||||
|
||||
bp->flash_start = 1024 * 4096;
|
||||
bp->eeprom_map = fb_eeprom_map;
|
||||
bp->fw_version = ioread32(&bp->image->version);
|
||||
bp->fw_cap = OCP_CAP_BASIC;
|
||||
|
||||
ver = bp->fw_version & 0xffff;
|
||||
if (ver >= 19)
|
||||
bp->fw_cap |= OCP_CAP_SIGNAL;
|
||||
if (ver >= 20)
|
||||
bp->fw_cap |= OCP_CAP_FREQ;
|
||||
|
||||
ptp_ocp_tod_init(bp);
|
||||
ptp_ocp_nmea_out_init(bp);
|
||||
ptp_ocp_sma_init(bp);
|
||||
ptp_ocp_signal_init(bp);
|
||||
|
||||
err = ptp_ocp_attr_group_add(bp, fb_timecard_groups);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ptp_ocp_fb_set_pins(bp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return ptp_ocp_init_clock(bp);
|
||||
}
|
||||
|
||||
static bool
|
||||
ptp_ocp_allow_irq(struct ptp_ocp *bp, struct ocp_resource *r)
|
||||
{
|
||||
bool allow = !r->irq_vec || r->irq_vec < bp->n_irqs;
|
||||
|
||||
if (!allow)
|
||||
dev_err(&bp->pdev->dev, "irq %d out of range, skipping %s\n",
|
||||
r->irq_vec, r->name);
|
||||
return allow;
|
||||
}
|
||||
|
||||
static int
|
||||
ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
|
||||
{
|
||||
struct ocp_resource *r, *table;
|
||||
int err = 0;
|
||||
|
||||
table = (struct ocp_resource *)driver_data;
|
||||
for (r = table; r->setup; r++) {
|
||||
if (!ptp_ocp_allow_irq(bp, r))
|
||||
continue;
|
||||
err = r->setup(bp, r);
|
||||
if (err) {
|
||||
dev_err(&bp->pdev->dev,
|
||||
"Could not register %s: err %d\n",
|
||||
r->name, err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static void
|
||||
ptp_ocp_enable_fpga(u32 __iomem *reg, u32 bit, bool enable)
|
||||
{
|
||||
|
@ -2054,44 +2029,271 @@ __handle_signal_inputs(struct ptp_ocp *bp, u32 val)
|
|||
ptp_ocp_dcf_in(bp, val & 0x00200020);
|
||||
}
|
||||
|
||||
/*
|
||||
* ANT0 == gps (in)
|
||||
* ANT1 == sma1 (in)
|
||||
* ANT2 == sma2 (in)
|
||||
* ANT3 == sma3 (out)
|
||||
* ANT4 == sma4 (out)
|
||||
*/
|
||||
static u32
|
||||
ptp_ocp_sma_fb_get(struct ptp_ocp *bp, int sma_nr)
|
||||
{
|
||||
u32 __iomem *gpio;
|
||||
u32 shift;
|
||||
|
||||
if (bp->sma[sma_nr - 1].fixed_fcn)
|
||||
return (sma_nr - 1) & 1;
|
||||
|
||||
if (bp->sma[sma_nr - 1].mode == SMA_MODE_IN)
|
||||
gpio = sma_nr > 2 ? &bp->sma_map2->gpio1 : &bp->sma_map1->gpio1;
|
||||
else
|
||||
gpio = sma_nr > 2 ? &bp->sma_map1->gpio2 : &bp->sma_map2->gpio2;
|
||||
shift = sma_nr & 1 ? 0 : 16;
|
||||
|
||||
return (ioread32(gpio) >> shift) & 0xffff;
|
||||
}
|
||||
|
||||
static int
|
||||
ptp_ocp_sma_fb_set_output(struct ptp_ocp *bp, int sma_nr, u32 val)
|
||||
{
|
||||
u32 reg, mask, shift;
|
||||
unsigned long flags;
|
||||
u32 __iomem *gpio;
|
||||
|
||||
gpio = sma_nr > 2 ? &bp->sma_map1->gpio2 : &bp->sma_map2->gpio2;
|
||||
shift = sma_nr & 1 ? 0 : 16;
|
||||
|
||||
mask = 0xffff << (16 - shift);
|
||||
|
||||
spin_lock_irqsave(&bp->lock, flags);
|
||||
|
||||
reg = ioread32(gpio);
|
||||
reg = (reg & mask) | (val << shift);
|
||||
|
||||
__handle_signal_outputs(bp, reg);
|
||||
|
||||
iowrite32(reg, gpio);
|
||||
|
||||
spin_unlock_irqrestore(&bp->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ptp_ocp_sma_fb_set_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
|
||||
{
|
||||
u32 reg, mask, shift;
|
||||
unsigned long flags;
|
||||
u32 __iomem *gpio;
|
||||
|
||||
gpio = sma_nr > 2 ? &bp->sma_map2->gpio1 : &bp->sma_map1->gpio1;
|
||||
shift = sma_nr & 1 ? 0 : 16;
|
||||
|
||||
mask = 0xffff << (16 - shift);
|
||||
|
||||
spin_lock_irqsave(&bp->lock, flags);
|
||||
|
||||
reg = ioread32(gpio);
|
||||
reg = (reg & mask) | (val << shift);
|
||||
|
||||
__handle_signal_inputs(bp, reg);
|
||||
|
||||
iowrite32(reg, gpio);
|
||||
|
||||
spin_unlock_irqrestore(&bp->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ptp_ocp_sma_fb_init(struct ptp_ocp *bp)
|
||||
{
|
||||
u32 reg;
|
||||
int i;
|
||||
|
||||
/* defaults */
|
||||
bp->sma[0].mode = SMA_MODE_IN;
|
||||
bp->sma[1].mode = SMA_MODE_IN;
|
||||
bp->sma[2].mode = SMA_MODE_OUT;
|
||||
bp->sma[3].mode = SMA_MODE_OUT;
|
||||
for (i = 0; i < 4; i++)
|
||||
bp->sma[i].default_fcn = i & 1;
|
||||
|
||||
/* If no SMA1 map, the pin functions and directions are fixed. */
|
||||
if (!bp->sma_map1) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
bp->sma[i].fixed_fcn = true;
|
||||
bp->sma[i].fixed_dir = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* If SMA2 GPIO output map is all 1, it is not present.
|
||||
* This indicates the firmware has fixed direction SMA pins.
|
||||
*/
|
||||
reg = ioread32(&bp->sma_map2->gpio2);
|
||||
if (reg == 0xffffffff) {
|
||||
for (i = 0; i < 4; i++)
|
||||
bp->sma[i].fixed_dir = true;
|
||||
} else {
|
||||
reg = ioread32(&bp->sma_map1->gpio1);
|
||||
bp->sma[0].mode = reg & BIT(15) ? SMA_MODE_IN : SMA_MODE_OUT;
|
||||
bp->sma[1].mode = reg & BIT(31) ? SMA_MODE_IN : SMA_MODE_OUT;
|
||||
|
||||
reg = ioread32(&bp->sma_map1->gpio2);
|
||||
bp->sma[2].mode = reg & BIT(15) ? SMA_MODE_OUT : SMA_MODE_IN;
|
||||
bp->sma[3].mode = reg & BIT(31) ? SMA_MODE_OUT : SMA_MODE_IN;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ocp_sma_op ocp_fb_sma_op = {
|
||||
.tbl = { ptp_ocp_sma_in, ptp_ocp_sma_out },
|
||||
.init = ptp_ocp_sma_fb_init,
|
||||
.get = ptp_ocp_sma_fb_get,
|
||||
.set_inputs = ptp_ocp_sma_fb_set_inputs,
|
||||
.set_output = ptp_ocp_sma_fb_set_output,
|
||||
};
|
||||
|
||||
static int
|
||||
ptp_ocp_fb_set_pins(struct ptp_ocp *bp)
|
||||
{
|
||||
struct ptp_pin_desc *config;
|
||||
int i;
|
||||
|
||||
config = kzalloc(sizeof(*config) * 4, GFP_KERNEL);
|
||||
if (!config)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
sprintf(config[i].name, "sma%d", i + 1);
|
||||
config[i].index = i;
|
||||
}
|
||||
|
||||
bp->ptp_info.n_pins = 4;
|
||||
bp->ptp_info.pin_config = config;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
ptp_ocp_fb_set_version(struct ptp_ocp *bp)
|
||||
{
|
||||
u64 cap = OCP_CAP_BASIC;
|
||||
u32 version;
|
||||
|
||||
version = ioread32(&bp->image->version);
|
||||
|
||||
/* if lower 16 bits are empty, this is the fw loader. */
|
||||
if ((version & 0xffff) == 0) {
|
||||
version = version >> 16;
|
||||
bp->fw_loader = true;
|
||||
}
|
||||
|
||||
bp->fw_tag = version >> 15;
|
||||
bp->fw_version = version & 0x7fff;
|
||||
|
||||
if (bp->fw_tag) {
|
||||
/* FPGA firmware */
|
||||
if (version >= 5)
|
||||
cap |= OCP_CAP_SIGNAL | OCP_CAP_FREQ;
|
||||
} else {
|
||||
/* SOM firmware */
|
||||
if (version >= 19)
|
||||
cap |= OCP_CAP_SIGNAL;
|
||||
if (version >= 20)
|
||||
cap |= OCP_CAP_FREQ;
|
||||
}
|
||||
|
||||
bp->fw_cap = cap;
|
||||
}
|
||||
|
||||
/* FB specific board initializers; last "resource" registered. */
|
||||
static int
|
||||
ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
|
||||
{
|
||||
int err;
|
||||
|
||||
bp->flash_start = 1024 * 4096;
|
||||
bp->eeprom_map = fb_eeprom_map;
|
||||
bp->fw_version = ioread32(&bp->image->version);
|
||||
bp->sma_op = &ocp_fb_sma_op;
|
||||
|
||||
ptp_ocp_fb_set_version(bp);
|
||||
|
||||
ptp_ocp_tod_init(bp);
|
||||
ptp_ocp_nmea_out_init(bp);
|
||||
ptp_ocp_sma_init(bp);
|
||||
ptp_ocp_signal_init(bp);
|
||||
|
||||
err = ptp_ocp_attr_group_add(bp, fb_timecard_groups);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = ptp_ocp_fb_set_pins(bp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
return ptp_ocp_init_clock(bp);
|
||||
}
|
||||
|
||||
static bool
|
||||
ptp_ocp_allow_irq(struct ptp_ocp *bp, struct ocp_resource *r)
|
||||
{
|
||||
bool allow = !r->irq_vec || r->irq_vec < bp->n_irqs;
|
||||
|
||||
if (!allow)
|
||||
dev_err(&bp->pdev->dev, "irq %d out of range, skipping %s\n",
|
||||
r->irq_vec, r->name);
|
||||
return allow;
|
||||
}
|
||||
|
||||
static int
|
||||
ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
|
||||
{
|
||||
struct ocp_resource *r, *table;
|
||||
int err = 0;
|
||||
|
||||
table = (struct ocp_resource *)driver_data;
|
||||
for (r = table; r->setup; r++) {
|
||||
if (!ptp_ocp_allow_irq(bp, r))
|
||||
continue;
|
||||
err = r->setup(bp, r);
|
||||
if (err) {
|
||||
dev_err(&bp->pdev->dev,
|
||||
"Could not register %s: err %d\n",
|
||||
r->name, err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ptp_ocp_show_output(u32 val, char *buf, int def_val)
|
||||
ptp_ocp_show_output(const struct ocp_selector *tbl, u32 val, char *buf,
|
||||
int def_val)
|
||||
{
|
||||
const char *name;
|
||||
ssize_t count;
|
||||
|
||||
count = sysfs_emit(buf, "OUT: ");
|
||||
name = ptp_ocp_select_name_from_val(ptp_ocp_sma_out, val);
|
||||
name = ptp_ocp_select_name_from_val(tbl, val);
|
||||
if (!name)
|
||||
name = ptp_ocp_select_name_from_val(ptp_ocp_sma_out, def_val);
|
||||
name = ptp_ocp_select_name_from_val(tbl, def_val);
|
||||
count += sysfs_emit_at(buf, count, "%s\n", name);
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ptp_ocp_show_inputs(u32 val, char *buf, int def_val)
|
||||
ptp_ocp_show_inputs(const struct ocp_selector *tbl, u32 val, char *buf,
|
||||
int def_val)
|
||||
{
|
||||
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;
|
||||
for (i = 0; tbl[i].name; i++) {
|
||||
if (val & tbl[i].value) {
|
||||
name = tbl[i].name;
|
||||
count += sysfs_emit_at(buf, count, "%s ", name);
|
||||
}
|
||||
}
|
||||
if (!val && def_val >= 0) {
|
||||
name = ptp_ocp_select_name_from_val(ptp_ocp_sma_in, def_val);
|
||||
name = ptp_ocp_select_name_from_val(tbl, def_val);
|
||||
count += sysfs_emit_at(buf, count, "%s ", name);
|
||||
}
|
||||
if (count)
|
||||
|
@ -2101,9 +2303,9 @@ ptp_ocp_show_inputs(u32 val, char *buf, int def_val)
|
|||
}
|
||||
|
||||
static int
|
||||
sma_parse_inputs(const char *buf, enum ptp_ocp_sma_mode *mode)
|
||||
sma_parse_inputs(const struct ocp_selector * const tbl[], 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;
|
||||
|
@ -2139,40 +2341,24 @@ sma_parse_inputs(const char *buf, enum ptp_ocp_sma_mode *mode)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static u32
|
||||
ptp_ocp_sma_get(struct ptp_ocp *bp, int sma_nr, enum ptp_ocp_sma_mode mode)
|
||||
{
|
||||
u32 __iomem *gpio;
|
||||
u32 shift;
|
||||
|
||||
if (bp->sma[sma_nr - 1].fixed_fcn)
|
||||
return (sma_nr - 1) & 1;
|
||||
|
||||
if (mode == SMA_MODE_IN)
|
||||
gpio = sma_nr > 2 ? &bp->sma_map2->gpio1 : &bp->sma_map1->gpio1;
|
||||
else
|
||||
gpio = sma_nr > 2 ? &bp->sma_map1->gpio2 : &bp->sma_map2->gpio2;
|
||||
shift = sma_nr & 1 ? 0 : 16;
|
||||
|
||||
return (ioread32(gpio) >> shift) & 0xffff;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ptp_ocp_sma_show(struct ptp_ocp *bp, int sma_nr, char *buf,
|
||||
int default_in_val, int default_out_val)
|
||||
{
|
||||
struct ptp_ocp_sma_connector *sma = &bp->sma[sma_nr - 1];
|
||||
const struct ocp_selector * const *tbl;
|
||||
u32 val;
|
||||
|
||||
val = ptp_ocp_sma_get(bp, sma_nr, sma->mode) & SMA_SELECT_MASK;
|
||||
tbl = bp->sma_op->tbl;
|
||||
val = ptp_ocp_sma_get(bp, sma_nr) & SMA_SELECT_MASK;
|
||||
|
||||
if (sma->mode == SMA_MODE_IN) {
|
||||
if (sma->disabled)
|
||||
val = SMA_DISABLE;
|
||||
return ptp_ocp_show_inputs(val, buf, default_in_val);
|
||||
return ptp_ocp_show_inputs(tbl[0], val, buf, default_in_val);
|
||||
}
|
||||
|
||||
return ptp_ocp_show_output(val, buf, default_out_val);
|
||||
return ptp_ocp_show_output(tbl[1], val, buf, default_out_val);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
|
@ -2207,54 +2393,6 @@ sma4_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
return ptp_ocp_sma_show(bp, 4, buf, -1, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
ptp_ocp_sma_store_output(struct ptp_ocp *bp, int sma_nr, u32 val)
|
||||
{
|
||||
u32 reg, mask, shift;
|
||||
unsigned long flags;
|
||||
u32 __iomem *gpio;
|
||||
|
||||
gpio = sma_nr > 2 ? &bp->sma_map1->gpio2 : &bp->sma_map2->gpio2;
|
||||
shift = sma_nr & 1 ? 0 : 16;
|
||||
|
||||
mask = 0xffff << (16 - shift);
|
||||
|
||||
spin_lock_irqsave(&bp->lock, flags);
|
||||
|
||||
reg = ioread32(gpio);
|
||||
reg = (reg & mask) | (val << shift);
|
||||
|
||||
__handle_signal_outputs(bp, reg);
|
||||
|
||||
iowrite32(reg, gpio);
|
||||
|
||||
spin_unlock_irqrestore(&bp->lock, flags);
|
||||
}
|
||||
|
||||
static void
|
||||
ptp_ocp_sma_store_inputs(struct ptp_ocp *bp, int sma_nr, u32 val)
|
||||
{
|
||||
u32 reg, mask, shift;
|
||||
unsigned long flags;
|
||||
u32 __iomem *gpio;
|
||||
|
||||
gpio = sma_nr > 2 ? &bp->sma_map2->gpio1 : &bp->sma_map1->gpio1;
|
||||
shift = sma_nr & 1 ? 0 : 16;
|
||||
|
||||
mask = 0xffff << (16 - shift);
|
||||
|
||||
spin_lock_irqsave(&bp->lock, flags);
|
||||
|
||||
reg = ioread32(gpio);
|
||||
reg = (reg & mask) | (val << shift);
|
||||
|
||||
__handle_signal_inputs(bp, reg);
|
||||
|
||||
iowrite32(reg, gpio);
|
||||
|
||||
spin_unlock_irqrestore(&bp->lock, flags);
|
||||
}
|
||||
|
||||
static int
|
||||
ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
|
||||
{
|
||||
|
@ -2263,7 +2401,7 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
|
|||
int val;
|
||||
|
||||
mode = sma->mode;
|
||||
val = sma_parse_inputs(buf, &mode);
|
||||
val = sma_parse_inputs(bp->sma_op->tbl, buf, &mode);
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
|
@ -2271,7 +2409,7 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
|
|||
return -EOPNOTSUPP;
|
||||
|
||||
if (sma->fixed_fcn) {
|
||||
if (val != ((sma_nr - 1) & 1))
|
||||
if (val != sma->default_fcn)
|
||||
return -EOPNOTSUPP;
|
||||
return 0;
|
||||
}
|
||||
|
@ -2280,9 +2418,9 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
|
|||
|
||||
if (mode != sma->mode) {
|
||||
if (mode == SMA_MODE_IN)
|
||||
ptp_ocp_sma_store_output(bp, sma_nr, 0);
|
||||
ptp_ocp_sma_set_output(bp, sma_nr, 0);
|
||||
else
|
||||
ptp_ocp_sma_store_inputs(bp, sma_nr, 0);
|
||||
ptp_ocp_sma_set_inputs(bp, sma_nr, 0);
|
||||
sma->mode = mode;
|
||||
}
|
||||
|
||||
|
@ -2293,11 +2431,11 @@ ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr)
|
|||
val = 0;
|
||||
|
||||
if (mode == SMA_MODE_IN)
|
||||
ptp_ocp_sma_store_inputs(bp, sma_nr, val);
|
||||
val = ptp_ocp_sma_set_inputs(bp, sma_nr, val);
|
||||
else
|
||||
ptp_ocp_sma_store_output(bp, sma_nr, val);
|
||||
val = ptp_ocp_sma_set_output(bp, sma_nr, val);
|
||||
|
||||
return 0;
|
||||
return val;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
|
@ -2352,7 +2490,9 @@ 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);
|
||||
struct ptp_ocp *bp = dev_get_drvdata(dev);
|
||||
|
||||
return ptp_ocp_select_table_show(bp->sma_op->tbl[0], buf);
|
||||
}
|
||||
static DEVICE_ATTR_RO(available_sma_inputs);
|
||||
|
||||
|
@ -2360,7 +2500,9 @@ 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);
|
||||
struct ptp_ocp *bp = dev_get_drvdata(dev);
|
||||
|
||||
return ptp_ocp_select_table_show(bp->sma_op->tbl[1], buf);
|
||||
}
|
||||
static DEVICE_ATTR_RO(available_sma_outputs);
|
||||
|
||||
|
@ -3025,10 +3167,10 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
|
|||
struct device *dev = s->private;
|
||||
struct ptp_system_timestamp sts;
|
||||
struct ts_reg __iomem *ts_reg;
|
||||
char *buf, *src, *mac_src;
|
||||
struct timespec64 ts;
|
||||
struct ptp_ocp *bp;
|
||||
u16 sma_val[4][2];
|
||||
char *src, *buf;
|
||||
u32 ctrl, val;
|
||||
bool on, map;
|
||||
int i;
|
||||
|
@ -3191,17 +3333,26 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
|
|||
if (bp->pps_select) {
|
||||
val = ioread32(&bp->pps_select->gpio1);
|
||||
src = &buf[80];
|
||||
if (val & 0x01)
|
||||
mac_src = "GNSS1";
|
||||
if (val & 0x01) {
|
||||
gpio_input_map(src, bp, sma_val, 0, NULL);
|
||||
else if (val & 0x02)
|
||||
mac_src = src;
|
||||
} else if (val & 0x02) {
|
||||
src = "MAC";
|
||||
else if (val & 0x04)
|
||||
} else if (val & 0x04) {
|
||||
src = "GNSS1";
|
||||
else
|
||||
} else {
|
||||
src = "----";
|
||||
mac_src = src;
|
||||
}
|
||||
} else {
|
||||
src = "?";
|
||||
mac_src = src;
|
||||
}
|
||||
seq_printf(s, "MAC PPS1 src: %s\n", mac_src);
|
||||
|
||||
gpio_input_map(buf, bp, sma_val, 1, "GNSS2");
|
||||
seq_printf(s, "MAC PPS2 src: %s\n", buf);
|
||||
|
||||
/* assumes automatic switchover/selection */
|
||||
val = ioread32(&bp->reg->select);
|
||||
|
@ -3226,12 +3377,6 @@ ptp_ocp_summary_show(struct seq_file *s, void *data)
|
|||
seq_printf(s, "%7s: %s, state: %s\n", "PHC src", buf,
|
||||
val & OCP_STATUS_IN_SYNC ? "sync" : "unsynced");
|
||||
|
||||
/* reuses PPS1 src from earlier */
|
||||
seq_printf(s, "MAC PPS1 src: %s\n", src);
|
||||
|
||||
gpio_input_map(buf, bp, sma_val, 1, "GNSS2");
|
||||
seq_printf(s, "MAC PPS2 src: %s\n", buf);
|
||||
|
||||
if (!ptp_ocp_gettimex(&bp->ptp_info, &ts, &sts)) {
|
||||
struct timespec64 sys_ts;
|
||||
s64 pre_ns, post_ns, ns;
|
||||
|
@ -3498,14 +3643,6 @@ ptp_ocp_info(struct ptp_ocp *bp)
|
|||
|
||||
ptp_ocp_phc_info(bp);
|
||||
|
||||
dev_info(dev, "version %x\n", bp->fw_version);
|
||||
if (bp->fw_version & 0xffff)
|
||||
dev_info(dev, "regular image, version %d\n",
|
||||
bp->fw_version & 0xffff);
|
||||
else
|
||||
dev_info(dev, "golden image, version %d\n",
|
||||
bp->fw_version >> 16);
|
||||
|
||||
ptp_ocp_serial_info(dev, "GNSS", bp->gnss_port, 115200);
|
||||
ptp_ocp_serial_info(dev, "GNSS2", bp->gnss2_port, 115200);
|
||||
ptp_ocp_serial_info(dev, "MAC", bp->mac_port, 57600);
|
||||
|
|
Loading…
Reference in a new issue