Commit 69dbe107 authored by Vadim Fedorenko's avatar Vadim Fedorenko Committed by David S. Miller

ptp: ocp: add Orolia timecard support

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: default avatarCharles Parent <charles.parent@orolia2s.com>
Acked-by: default avatarJonathan Lemon <jonathan.lemon@gmail.com>
Signed-off-by: default avatarJonathan Lemon <jonathan.lemon@gmail.com>
Signed-off-by: default avatarVadim Fedorenko <vadfed@fb.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 895ac5a5
......@@ -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>
......@@ -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",
......@@ -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;
......@@ -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;
......@@ -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; \
......@@ -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[] = {
......@@ -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);
......@@ -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);
......@@ -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)
......@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment