diff options
Diffstat (limited to 'drivers/ptp')
-rw-r--r-- | drivers/ptp/ptp_ocp.c | 186 |
1 files changed, 121 insertions, 65 deletions
diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index d2d663e439dc..433bd9d5d75c 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -11,12 +11,14 @@ #include <linux/clkdev.h> #include <linux/clk-provider.h> #include <linux/platform_device.h> +#include <linux/platform_data/i2c-xiic.h> #include <linux/ptp_clock_kernel.h> #include <linux/spi/spi.h> #include <linux/spi/xilinx_spi.h> #include <net/devlink.h> #include <linux/i2c.h> #include <linux/mtd/mtd.h> +#include <linux/nvmem-consumer.h> #ifndef PCI_VENDOR_ID_FACEBOOK #define PCI_VENDOR_ID_FACEBOOK 0x1d9b @@ -204,6 +206,9 @@ struct ptp_ocp_ext_src { int irq_vec; }; +#define OCP_BOARD_ID_LEN 13 +#define OCP_SERIAL_LEN 6 + struct ptp_ocp { struct pci_dev *pdev; struct device dev; @@ -230,6 +235,7 @@ struct ptp_ocp { struct platform_device *spi_flash; struct clk_hw *i2c_clk; struct timer_list watchdog; + const struct ptp_ocp_eeprom_map *eeprom_map; struct dentry *debug_root; time64_t gnss_lost; int id; @@ -238,8 +244,9 @@ struct ptp_ocp { int gnss2_port; int mac_port; /* miniature atomic clock */ int nmea_port; - u8 serial[6]; - bool has_serial; + u8 board_id[OCP_BOARD_ID_LEN]; + u8 serial[OCP_SERIAL_LEN]; + bool has_eeprom_data; u32 pps_req_map; int flash_start; u32 utc_tai_offset; @@ -268,6 +275,28 @@ static int ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r); static irqreturn_t ptp_ocp_ts_irq(int irq, void *priv); static int ptp_ocp_ts_enable(void *priv, u32 req, bool enable); +struct ptp_ocp_eeprom_map { + u16 off; + u16 len; + u32 bp_offset; + const void * const tag; +}; + +#define EEPROM_ENTRY(addr, member) \ + .off = addr, \ + .len = sizeof_field(struct ptp_ocp, member), \ + .bp_offset = offsetof(struct ptp_ocp, member) + +#define BP_MAP_ENTRY_ADDR(bp, map) ({ \ + (void *)((uintptr_t)(bp) + (map)->bp_offset); \ +}) + +static struct ptp_ocp_eeprom_map fb_eeprom_map[] = { + { EEPROM_ENTRY(0x43, board_id) }, + { EEPROM_ENTRY(0x00, serial), .tag = "mac" }, + { } +}; + #define bp_assign_entry(bp, res, val) ({ \ uintptr_t addr = (uintptr_t)(bp) + (res)->bp_offset; \ *(typeof(val) *)addr = val; \ @@ -396,6 +425,15 @@ static struct ocp_resource ocp_fb_resource[] = { .extra = &(struct ptp_ocp_i2c_info) { .name = "xiic-i2c", .fixed_rate = 50000000, + .data_size = sizeof(struct xiic_i2c_platform_data), + .data = &(struct xiic_i2c_platform_data) { + .num_devices = 2, + .devices = (struct i2c_board_info[]) { + { I2C_BOARD_INFO("24c02", 0x50) }, + { I2C_BOARD_INFO("24mac402", 0x58), + .platform_data = "mac" }, + }, + }, }, }, { @@ -919,78 +957,88 @@ ptp_ocp_tod_gnss_name(int idx) return gnss_name[idx]; } +struct ptp_ocp_nvmem_match_info { + struct ptp_ocp *bp; + const void * const tag; +}; + static int -ptp_ocp_firstchild(struct device *dev, void *data) +ptp_ocp_nvmem_match(struct device *dev, const void *data) { - return 1; + const struct ptp_ocp_nvmem_match_info *info = data; + + dev = dev->parent; + if (!i2c_verify_client(dev) || info->tag != dev->platform_data) + return 0; + + while ((dev = dev->parent)) + if (dev->driver && !strcmp(dev->driver->name, KBUILD_MODNAME)) + return info->bp == dev_get_drvdata(dev); + return 0; } -static int -ptp_ocp_read_i2c(struct i2c_adapter *adap, u8 addr, u8 reg, u8 sz, u8 *data) +static inline struct nvmem_device * +ptp_ocp_nvmem_device_get(struct ptp_ocp *bp, const void * const tag) { - struct i2c_msg msgs[2] = { - { - .addr = addr, - .len = 1, - .buf = ®, - }, - { - .addr = addr, - .flags = I2C_M_RD, - .len = 2, - .buf = data, - }, - }; - int err; - u8 len; - - /* xiic-i2c for some stupid reason only does 2 byte reads. */ - while (sz) { - len = min_t(u8, sz, 2); - msgs[1].len = len; - err = i2c_transfer(adap, msgs, 2); - if (err != msgs[1].len) - return err; - msgs[1].buf += len; - reg += len; - sz -= len; + struct ptp_ocp_nvmem_match_info info = { .bp = bp, .tag = tag }; + + return nvmem_device_find(&info, ptp_ocp_nvmem_match); +} + +static inline void +ptp_ocp_nvmem_device_put(struct nvmem_device **nvmemp) +{ + if (*nvmemp != NULL) { + nvmem_device_put(*nvmemp); + *nvmemp = NULL; } - return 0; } static void -ptp_ocp_get_serial_number(struct ptp_ocp *bp) +ptp_ocp_read_eeprom(struct ptp_ocp *bp) { - struct i2c_adapter *adap; - struct device *dev; - int err; + const struct ptp_ocp_eeprom_map *map; + struct nvmem_device *nvmem; + const void *tag; + int ret; if (!bp->i2c_ctrl) return; - dev = device_find_child(&bp->i2c_ctrl->dev, NULL, ptp_ocp_firstchild); - if (!dev) { - dev_err(&bp->pdev->dev, "Can't find I2C adapter\n"); - return; - } - - adap = i2c_verify_adapter(dev); - if (!adap) { - dev_err(&bp->pdev->dev, "device '%s' isn't an I2C adapter\n", - dev_name(dev)); - goto out; - } + tag = NULL; + nvmem = NULL; - err = ptp_ocp_read_i2c(adap, 0x58, 0x9A, 6, bp->serial); - if (err) { - dev_err(&bp->pdev->dev, "could not read eeprom: %d\n", err); - goto out; + for (map = bp->eeprom_map; map->len; map++) { + if (map->tag != tag) { + tag = map->tag; + ptp_ocp_nvmem_device_put(&nvmem); + } + if (!nvmem) { + nvmem = ptp_ocp_nvmem_device_get(bp, tag); + if (!nvmem) + goto out; + } + ret = nvmem_device_read(nvmem, map->off, map->len, + BP_MAP_ENTRY_ADDR(bp, map)); + if (ret != map->len) + goto read_fail; } - bp->has_serial = true; + bp->has_eeprom_data = true; out: - put_device(dev); + ptp_ocp_nvmem_device_put(&nvmem); + return; + +read_fail: + dev_err(&bp->pdev->dev, "could not read eeprom: %d\n", ret); + goto out; +} + +static int +ptp_ocp_firstchild(struct device *dev, void *data) +{ + return 1; } static struct device * @@ -1109,16 +1157,23 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req, return err; } - if (!bp->has_serial) - ptp_ocp_get_serial_number(bp); - - if (bp->has_serial) { - sprintf(buf, "%pM", bp->serial); - err = devlink_info_serial_number_put(req, buf); - if (err) - return err; + if (!bp->has_eeprom_data) { + ptp_ocp_read_eeprom(bp); + if (!bp->has_eeprom_data) + return 0; } + sprintf(buf, "%pM", bp->serial); + err = devlink_info_serial_number_put(req, buf); + if (err) + return err; + + err = devlink_info_version_fixed_put(req, + DEVLINK_INFO_VERSION_GENERIC_BOARD_ID, + bp->board_id); + if (err) + return err; + return 0; } @@ -1412,6 +1467,7 @@ static int ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r) { bp->flash_start = 1024 * 4096; + bp->eeprom_map = fb_eeprom_map; ptp_ocp_tod_init(bp); ptp_ocp_nmea_out_init(bp); @@ -1810,8 +1866,8 @@ serialnum_show(struct device *dev, struct device_attribute *attr, char *buf) { struct ptp_ocp *bp = dev_get_drvdata(dev); - if (!bp->has_serial) - ptp_ocp_get_serial_number(bp); + if (!bp->has_eeprom_data) + ptp_ocp_read_eeprom(bp); return sysfs_emit(buf, "%pM\n", bp->serial); } |