Skip to content

Commit

Permalink
ptp: ocp: add Orolia timecard support
Browse files Browse the repository at this point in the history
This brings in the Orolia timecard support from the GitHub repository.
The card uses different drivers to provide access to i2c EEPROM and
firmware SPI flash. And it also has a bit different EEPROM map, but
other parts of the code are the same and could be reused.

Co-developed-by: Charles Parent <charles.parent@orolia2s.com>
Acked-by: Jonathan Lemon <jonathan.lemon@gmail.com>
Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com>
Signed-off-by: Vadim Fedorenko <vadfed@fb.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Vadim Fedorenko authored and David S. Miller committed Oct 24, 2022
1 parent 895ac5a commit 69dbe10
Showing 1 changed file with 290 additions and 0 deletions.
290 changes: 290 additions & 0 deletions drivers/ptp/ptp_ocp.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@
#include <linux/clk-provider.h>
#include <linux/platform_device.h>
#include <linux/platform_data/i2c-xiic.h>
#include <linux/platform_data/i2c-ocores.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/spi/spi.h>
#include <linux/spi/xilinx_spi.h>
#include <linux/spi/altera.h>
#include <net/devlink.h>
#include <linux/i2c.h>
#include <linux/mtd/mtd.h>
Expand All @@ -28,6 +30,9 @@
#define PCI_VENDOR_ID_CELESTICA 0x18d4
#define PCI_DEVICE_ID_CELESTICA_TIMECARD 0x1008

#define PCI_VENDOR_ID_OROLIA 0x1ad7
#define PCI_DEVICE_ID_OROLIA_ARTCARD 0xa000

static struct class timecard_class = {
.owner = THIS_MODULE,
.name = "timecard",
Expand Down Expand Up @@ -310,6 +315,7 @@ struct ptp_ocp {
struct ptp_ocp_ext_src *ts2;
struct ptp_ocp_ext_src *ts3;
struct ptp_ocp_ext_src *ts4;
struct ocp_art_gpio_reg __iomem *art_sma;
struct img_reg __iomem *image;
struct ptp_clock *ptp;
struct ptp_clock_info ptp_info;
Expand Down Expand Up @@ -370,8 +376,12 @@ static int ptp_ocp_signal_from_perout(struct ptp_ocp *bp, int gen,
static int ptp_ocp_signal_enable(void *priv, u32 req, bool enable);
static int ptp_ocp_sma_store(struct ptp_ocp *bp, const char *buf, int sma_nr);

static int ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r);

static const struct ocp_attr_group fb_timecard_groups[];

static const struct ocp_attr_group art_timecard_groups[];

struct ptp_ocp_eeprom_map {
u16 off;
u16 len;
Expand All @@ -394,6 +404,12 @@ static struct ptp_ocp_eeprom_map fb_eeprom_map[] = {
{ }
};

static struct ptp_ocp_eeprom_map art_eeprom_map[] = {
{ EEPROM_ENTRY(0x200 + 0x43, board_id) },
{ EEPROM_ENTRY(0x200 + 0x63, serial) },
{ }
};

#define bp_assign_entry(bp, res, val) ({ \
uintptr_t addr = (uintptr_t)(bp) + (res)->bp_offset; \
*(typeof(val) *)addr = val; \
Expand Down Expand Up @@ -435,6 +451,13 @@ static struct ptp_ocp_eeprom_map fb_eeprom_map[] = {
* 14: Signal Generator 4
* 15: TS3
* 16: TS4
--
* 8: Orolia TS1
* 10: Orolia TS2
* 11: Orolia TS0 (GNSS)
* 12: Orolia PPS
* 14: Orolia TS3
* 15: Orolia TS4
*/

static struct ocp_resource ocp_fb_resource[] = {
Expand Down Expand Up @@ -661,9 +684,127 @@ static struct ocp_resource ocp_fb_resource[] = {
{ }
};

struct ocp_art_gpio_reg {
struct {
u32 gpio;
u32 __pad[3];
} map[4];
};

static struct ocp_resource ocp_art_resource[] = {
{
OCP_MEM_RESOURCE(reg),
.offset = 0x01000000, .size = 0x10000,
},
{
OCP_SERIAL_RESOURCE(gnss_port),
.offset = 0x00160000 + 0x1000, .irq_vec = 3,
.extra = &(struct ptp_ocp_serial_port) {
.baud = 115200,
},
},
{
OCP_MEM_RESOURCE(art_sma),
.offset = 0x003C0000, .size = 0x1000,
},
/* Timestamp associated with GNSS1 receiver PPS */
{
OCP_EXT_RESOURCE(ts0),
.offset = 0x360000, .size = 0x20, .irq_vec = 12,
.extra = &(struct ptp_ocp_ext_info) {
.index = 0,
.irq_fcn = ptp_ocp_ts_irq,
.enable = ptp_ocp_ts_enable,
},
},
{
OCP_EXT_RESOURCE(ts1),
.offset = 0x380000, .size = 0x20, .irq_vec = 8,
.extra = &(struct ptp_ocp_ext_info) {
.index = 1,
.irq_fcn = ptp_ocp_ts_irq,
.enable = ptp_ocp_ts_enable,
},
},
{
OCP_EXT_RESOURCE(ts2),
.offset = 0x390000, .size = 0x20, .irq_vec = 10,
.extra = &(struct ptp_ocp_ext_info) {
.index = 2,
.irq_fcn = ptp_ocp_ts_irq,
.enable = ptp_ocp_ts_enable,
},
},
{
OCP_EXT_RESOURCE(ts3),
.offset = 0x3A0000, .size = 0x20, .irq_vec = 14,
.extra = &(struct ptp_ocp_ext_info) {
.index = 3,
.irq_fcn = ptp_ocp_ts_irq,
.enable = ptp_ocp_ts_enable,
},
},
{
OCP_EXT_RESOURCE(ts4),
.offset = 0x3B0000, .size = 0x20, .irq_vec = 15,
.extra = &(struct ptp_ocp_ext_info) {
.index = 4,
.irq_fcn = ptp_ocp_ts_irq,
.enable = ptp_ocp_ts_enable,
},
},
/* Timestamp associated with Internal PPS of the card */
{
OCP_EXT_RESOURCE(pps),
.offset = 0x00330000, .size = 0x20, .irq_vec = 11,
.extra = &(struct ptp_ocp_ext_info) {
.index = 5,
.irq_fcn = ptp_ocp_ts_irq,
.enable = ptp_ocp_ts_enable,
},
},
{
OCP_SPI_RESOURCE(spi_flash),
.offset = 0x00310000, .size = 0x10000, .irq_vec = 9,
.extra = &(struct ptp_ocp_flash_info) {
.name = "spi_altera", .pci_offset = 0,
.data_size = sizeof(struct altera_spi_platform_data),
.data = &(struct altera_spi_platform_data) {
.num_chipselect = 1,
.num_devices = 1,
.devices = &(struct spi_board_info) {
.modalias = "spi-nor",
},
},
},
},
{
OCP_I2C_RESOURCE(i2c_ctrl),
.offset = 0x350000, .size = 0x100, .irq_vec = 4,
.extra = &(struct ptp_ocp_i2c_info) {
.name = "ocores-i2c",
.fixed_rate = 400000,
.data_size = sizeof(struct ocores_i2c_platform_data),
.data = &(struct ocores_i2c_platform_data) {
.clock_khz = 125000,
.bus_khz = 400,
.num_devices = 1,
.devices = &(struct i2c_board_info) {
I2C_BOARD_INFO("24c08", 0x50),
},
},
},
},
{
.setup = ptp_ocp_art_board_init,
},
{ }
};

static const struct pci_device_id ptp_ocp_pcidev_id[] = {
{ PCI_DEVICE_DATA(FACEBOOK, TIMECARD, &ocp_fb_resource) },
{ PCI_DEVICE_DATA(CELESTICA, TIMECARD, &ocp_fb_resource) },
{ PCI_DEVICE_DATA(OROLIA, ARTCARD, &ocp_art_resource) },
{ }
};
MODULE_DEVICE_TABLE(pci, ptp_ocp_pcidev_id);
Expand Down Expand Up @@ -728,6 +869,19 @@ static const struct ocp_selector ptp_ocp_sma_out[] = {
{ }
};

static const struct ocp_selector ptp_ocp_art_sma_in[] = {
{ .name = "PPS1", .value = 0x0001 },
{ .name = "10Mhz", .value = 0x0008 },
{ }
};

static const struct ocp_selector ptp_ocp_art_sma_out[] = {
{ .name = "PHC", .value = 0x0002 },
{ .name = "GNSS", .value = 0x0004 },
{ .name = "10Mhz", .value = 0x0010 },
{ }
};

struct ocp_sma_op {
const struct ocp_selector *tbl[2];
void (*init)(struct ptp_ocp *bp);
Expand Down Expand Up @@ -2275,6 +2429,118 @@ ptp_ocp_register_resources(struct ptp_ocp *bp, kernel_ulong_t driver_data)
return err;
}

static void
ptp_ocp_art_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;

bp->sma[0].default_fcn = 0x08; /* IN: 10Mhz */
bp->sma[1].default_fcn = 0x01; /* IN: PPS1 */
bp->sma[2].default_fcn = 0x10; /* OUT: 10Mhz */
bp->sma[3].default_fcn = 0x02; /* OUT: PHC */

/* If no SMA map, the pin functions and directions are fixed. */
if (!bp->art_sma) {
for (i = 0; i < 4; i++) {
bp->sma[i].fixed_fcn = true;
bp->sma[i].fixed_dir = true;
}
return;
}

for (i = 0; i < 4; i++) {
reg = ioread32(&bp->art_sma->map[i].gpio);

switch (reg & 0xff) {
case 0:
bp->sma[i].fixed_fcn = true;
bp->sma[i].fixed_dir = true;
break;
case 1:
case 8:
bp->sma[i].mode = SMA_MODE_IN;
break;
default:
bp->sma[i].mode = SMA_MODE_OUT;
break;
}
}
}

static u32
ptp_ocp_art_sma_get(struct ptp_ocp *bp, int sma_nr)
{
if (bp->sma[sma_nr - 1].fixed_fcn)
return bp->sma[sma_nr - 1].default_fcn;

return ioread32(&bp->art_sma->map[sma_nr - 1].gpio) & 0xff;
}

/* note: store 0 is considered invalid. */
static int
ptp_ocp_art_sma_set(struct ptp_ocp *bp, int sma_nr, u32 val)
{
unsigned long flags;
u32 __iomem *gpio;
int err = 0;
u32 reg;

val &= SMA_SELECT_MASK;
if (hweight32(val) > 1)
return -EINVAL;

gpio = &bp->art_sma->map[sma_nr - 1].gpio;

spin_lock_irqsave(&bp->lock, flags);
reg = ioread32(gpio);
if (((reg >> 16) & val) == 0) {
err = -EOPNOTSUPP;
} else {
reg = (reg & 0xff00) | (val & 0xff);
iowrite32(reg, gpio);
}
spin_unlock_irqrestore(&bp->lock, flags);

return err;
}

static const struct ocp_sma_op ocp_art_sma_op = {
.tbl = { ptp_ocp_art_sma_in, ptp_ocp_art_sma_out },
.init = ptp_ocp_art_sma_init,
.get = ptp_ocp_art_sma_get,
.set_inputs = ptp_ocp_art_sma_set,
.set_output = ptp_ocp_art_sma_set,
};

/* ART specific board initializers; last "resource" registered. */
static int
ptp_ocp_art_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
{
int err;

bp->flash_start = 0x1000000;
bp->eeprom_map = art_eeprom_map;
bp->fw_cap = OCP_CAP_BASIC;
bp->fw_version = ioread32(&bp->reg->version);
bp->fw_tag = 2;
bp->sma_op = &ocp_art_sma_op;

ptp_ocp_sma_init(bp);

err = ptp_ocp_attr_group_add(bp, art_timecard_groups);
if (err)
return err;

return ptp_ocp_init_clock(bp);
}

static ssize_t
ptp_ocp_show_output(const struct ocp_selector *tbl, u32 val, char *buf,
int def_val)
Expand Down Expand Up @@ -3083,6 +3349,30 @@ static const struct ocp_attr_group fb_timecard_groups[] = {
{ },
};

static struct attribute *art_timecard_attrs[] = {
&dev_attr_serialnum.attr,
&dev_attr_clock_source.attr,
&dev_attr_available_clock_sources.attr,
&dev_attr_utc_tai_offset.attr,
&dev_attr_ts_window_adjust.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,
};

static const struct attribute_group art_timecard_group = {
.attrs = art_timecard_attrs,
};

static const struct ocp_attr_group art_timecard_groups[] = {
{ .cap = OCP_CAP_BASIC, .group = &art_timecard_group },
{ },
};

static void
gpio_input_map(char *buf, struct ptp_ocp *bp, u16 map[][2], u16 bit,
const char *def)
Expand Down

0 comments on commit 69dbe10

Please sign in to comment.