Merge branch 'net-devlink-return-the-driver-name-in-devlink_nl_info_fill'
[linux-2.6-microblaze.git] / drivers / ptp / ptp_ocp.c
index 1ce0f29..4bbaccd 100644 (file)
 #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",
@@ -203,6 +208,11 @@ struct frequency_reg {
        u32     ctrl;
        u32     status;
 };
+
+struct board_config_reg {
+       u32 mro50_serial_activate;
+};
+
 #define FREQ_STATUS_VALID      BIT(31)
 #define FREQ_STATUS_ERROR      BIT(30)
 #define FREQ_STATUS_OVERRUN    BIT(29)
@@ -294,6 +304,7 @@ struct ptp_ocp {
        struct tod_reg __iomem  *tod;
        struct pps_reg __iomem  *pps_to_ext;
        struct pps_reg __iomem  *pps_to_clk;
+       struct board_config_reg __iomem *board_config;
        struct gpio_reg __iomem *pps_select;
        struct gpio_reg __iomem *sma_map1;
        struct gpio_reg __iomem *sma_map2;
@@ -310,6 +321,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 +382,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 +410,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 +457,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 +690,141 @@ static struct ocp_resource ocp_fb_resource[] = {
        { }
 };
 
+#define OCP_ART_CONFIG_SIZE            144
+#define OCP_ART_TEMP_TABLE_SIZE                368
+
+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),
+                               },
+                       },
+               },
+       },
+       {
+               OCP_SERIAL_RESOURCE(mac_port),
+               .offset = 0x00190000, .irq_vec = 7,
+               .extra = &(struct ptp_ocp_serial_port) {
+                       .baud = 9600,
+               },
+       },
+       {
+               OCP_MEM_RESOURCE(board_config),
+               .offset = 0x210000, .size = 0x1000,
+       },
+       {
+               .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 +889,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);
@@ -1356,11 +1530,9 @@ ptp_ocp_devlink_fw_image(struct devlink *devlink, const struct firmware *fw,
        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",
+                       "No firmware header found, cancel firmware upgrade",
                        NULL, 0, 0);
-               offset = 0;
-               length = fw->size;
-               goto out;
+               return -EINVAL;
        }
 
        if (be16_to_cpu(hdr->pci_vendor_id) != bp->pdev->vendor ||
@@ -1388,7 +1560,6 @@ ptp_ocp_devlink_fw_image(struct devlink *devlink, const struct firmware *fw,
                return -EINVAL;
        }
 
-out:
        *data = &fw->data[offset];
        *size = length;
 
@@ -1476,10 +1647,6 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
        char buf[32];
        int err;
 
-       err = devlink_info_driver_name_put(req, KBUILD_MODNAME);
-       if (err)
-               return err;
-
        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);
@@ -2275,6 +2442,121 @@ 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;
+
+       /* Enable MAC serial port during initialisation */
+       iowrite32(1, &bp->board_config->mro50_serial_activate);
+
+       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)
@@ -3048,6 +3330,130 @@ DEVICE_FREQ_GROUP(freq2, 1);
 DEVICE_FREQ_GROUP(freq3, 2);
 DEVICE_FREQ_GROUP(freq4, 3);
 
+static ssize_t
+disciplining_config_read(struct file *filp, struct kobject *kobj,
+                        struct bin_attribute *bin_attr, char *buf,
+                        loff_t off, size_t count)
+{
+       struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
+       size_t size = OCP_ART_CONFIG_SIZE;
+       struct nvmem_device *nvmem;
+       ssize_t err;
+
+       nvmem = ptp_ocp_nvmem_device_get(bp, NULL);
+       if (IS_ERR(nvmem))
+               return PTR_ERR(nvmem);
+
+       if (off > size) {
+               err = 0;
+               goto out;
+       }
+
+       if (off + count > size)
+               count = size - off;
+
+       // the configuration is in the very beginning of the EEPROM
+       err = nvmem_device_read(nvmem, off, count, buf);
+       if (err != count) {
+               err = -EFAULT;
+               goto out;
+       }
+
+out:
+       ptp_ocp_nvmem_device_put(&nvmem);
+
+       return err;
+}
+
+static ssize_t
+disciplining_config_write(struct file *filp, struct kobject *kobj,
+                         struct bin_attribute *bin_attr, char *buf,
+                         loff_t off, size_t count)
+{
+       struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
+       struct nvmem_device *nvmem;
+       ssize_t err;
+
+       /* Allow write of the whole area only */
+       if (off || count != OCP_ART_CONFIG_SIZE)
+               return -EFAULT;
+
+       nvmem = ptp_ocp_nvmem_device_get(bp, NULL);
+       if (IS_ERR(nvmem))
+               return PTR_ERR(nvmem);
+
+       err = nvmem_device_write(nvmem, 0x00, count, buf);
+       if (err != count)
+               err = -EFAULT;
+
+       ptp_ocp_nvmem_device_put(&nvmem);
+
+       return err;
+}
+static BIN_ATTR_RW(disciplining_config, OCP_ART_CONFIG_SIZE);
+
+static ssize_t
+temperature_table_read(struct file *filp, struct kobject *kobj,
+                      struct bin_attribute *bin_attr, char *buf,
+                      loff_t off, size_t count)
+{
+       struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
+       size_t size = OCP_ART_TEMP_TABLE_SIZE;
+       struct nvmem_device *nvmem;
+       ssize_t err;
+
+       nvmem = ptp_ocp_nvmem_device_get(bp, NULL);
+       if (IS_ERR(nvmem))
+               return PTR_ERR(nvmem);
+
+       if (off > size) {
+               err = 0;
+               goto out;
+       }
+
+       if (off + count > size)
+               count = size - off;
+
+       // the configuration is in the very beginning of the EEPROM
+       err = nvmem_device_read(nvmem, 0x90 + off, count, buf);
+       if (err != count) {
+               err = -EFAULT;
+               goto out;
+       }
+
+out:
+       ptp_ocp_nvmem_device_put(&nvmem);
+
+       return err;
+}
+
+static ssize_t
+temperature_table_write(struct file *filp, struct kobject *kobj,
+                       struct bin_attribute *bin_attr, char *buf,
+                       loff_t off, size_t count)
+{
+       struct ptp_ocp *bp = dev_get_drvdata(kobj_to_dev(kobj));
+       struct nvmem_device *nvmem;
+       ssize_t err;
+
+       /* Allow write of the whole area only */
+       if (off || count != OCP_ART_TEMP_TABLE_SIZE)
+               return -EFAULT;
+
+       nvmem = ptp_ocp_nvmem_device_get(bp, NULL);
+       if (IS_ERR(nvmem))
+               return PTR_ERR(nvmem);
+
+       err = nvmem_device_write(nvmem, 0x90, count, buf);
+       if (err != count)
+               err = -EFAULT;
+
+       ptp_ocp_nvmem_device_put(&nvmem);
+
+       return err;
+}
+static BIN_ATTR_RW(temperature_table, OCP_ART_TEMP_TABLE_SIZE);
+
 static struct attribute *fb_timecard_attrs[] = {
        &dev_attr_serialnum.attr,
        &dev_attr_gnss_sync.attr,
@@ -3067,9 +3473,11 @@ static struct attribute *fb_timecard_attrs[] = {
        &dev_attr_tod_correction.attr,
        NULL,
 };
+
 static const struct attribute_group fb_timecard_group = {
        .attrs = fb_timecard_attrs,
 };
+
 static const struct ocp_attr_group fb_timecard_groups[] = {
        { .cap = OCP_CAP_BASIC,     .group = &fb_timecard_group },
        { .cap = OCP_CAP_SIGNAL,    .group = &fb_timecard_signal0_group },
@@ -3083,6 +3491,37 @@ 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 struct bin_attribute *bin_art_timecard_attrs[] = {
+       &bin_attr_disciplining_config,
+       &bin_attr_temperature_table,
+       NULL,
+};
+
+static const struct attribute_group art_timecard_group = {
+       .attrs = art_timecard_attrs,
+       .bin_attrs = bin_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)