mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-05-20 08:03:25 +00:00
ptp: ocp: add nvmem interface for accessing eeprom
Add the at24 drivers for the eeprom, and use the accessors via the nvmem API instead of direct i2c accesses. This makes things cleaner. Add an eeprom map table which specifies where the pre-defined information is located. Retrieve the information and and export it via the devlink interface. Signed-off-by: Jonathan Lemon <jonathan.lemon@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
b57b44f749
commit
0cfcdd1ebc
1 changed files with 124 additions and 68 deletions
|
@ -11,12 +11,14 @@
|
||||||
#include <linux/clkdev.h>
|
#include <linux/clkdev.h>
|
||||||
#include <linux/clk-provider.h>
|
#include <linux/clk-provider.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/platform_data/i2c-xiic.h>
|
||||||
#include <linux/ptp_clock_kernel.h>
|
#include <linux/ptp_clock_kernel.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
#include <linux/spi/xilinx_spi.h>
|
#include <linux/spi/xilinx_spi.h>
|
||||||
#include <net/devlink.h>
|
#include <net/devlink.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/mtd/mtd.h>
|
#include <linux/mtd/mtd.h>
|
||||||
|
#include <linux/nvmem-consumer.h>
|
||||||
|
|
||||||
#ifndef PCI_VENDOR_ID_FACEBOOK
|
#ifndef PCI_VENDOR_ID_FACEBOOK
|
||||||
#define PCI_VENDOR_ID_FACEBOOK 0x1d9b
|
#define PCI_VENDOR_ID_FACEBOOK 0x1d9b
|
||||||
|
@ -204,6 +206,9 @@ struct ptp_ocp_ext_src {
|
||||||
int irq_vec;
|
int irq_vec;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define OCP_BOARD_ID_LEN 13
|
||||||
|
#define OCP_SERIAL_LEN 6
|
||||||
|
|
||||||
struct ptp_ocp {
|
struct ptp_ocp {
|
||||||
struct pci_dev *pdev;
|
struct pci_dev *pdev;
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
@ -230,6 +235,7 @@ struct ptp_ocp {
|
||||||
struct platform_device *spi_flash;
|
struct platform_device *spi_flash;
|
||||||
struct clk_hw *i2c_clk;
|
struct clk_hw *i2c_clk;
|
||||||
struct timer_list watchdog;
|
struct timer_list watchdog;
|
||||||
|
const struct ptp_ocp_eeprom_map *eeprom_map;
|
||||||
struct dentry *debug_root;
|
struct dentry *debug_root;
|
||||||
time64_t gnss_lost;
|
time64_t gnss_lost;
|
||||||
int id;
|
int id;
|
||||||
|
@ -238,8 +244,9 @@ struct ptp_ocp {
|
||||||
int gnss2_port;
|
int gnss2_port;
|
||||||
int mac_port; /* miniature atomic clock */
|
int mac_port; /* miniature atomic clock */
|
||||||
int nmea_port;
|
int nmea_port;
|
||||||
u8 serial[6];
|
u8 board_id[OCP_BOARD_ID_LEN];
|
||||||
bool has_serial;
|
u8 serial[OCP_SERIAL_LEN];
|
||||||
|
bool has_eeprom_data;
|
||||||
u32 pps_req_map;
|
u32 pps_req_map;
|
||||||
int flash_start;
|
int flash_start;
|
||||||
u32 utc_tai_offset;
|
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 irqreturn_t ptp_ocp_ts_irq(int irq, void *priv);
|
||||||
static int ptp_ocp_ts_enable(void *priv, u32 req, bool enable);
|
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) ({ \
|
#define bp_assign_entry(bp, res, val) ({ \
|
||||||
uintptr_t addr = (uintptr_t)(bp) + (res)->bp_offset; \
|
uintptr_t addr = (uintptr_t)(bp) + (res)->bp_offset; \
|
||||||
*(typeof(val) *)addr = val; \
|
*(typeof(val) *)addr = val; \
|
||||||
|
@ -396,6 +425,15 @@ static struct ocp_resource ocp_fb_resource[] = {
|
||||||
.extra = &(struct ptp_ocp_i2c_info) {
|
.extra = &(struct ptp_ocp_i2c_info) {
|
||||||
.name = "xiic-i2c",
|
.name = "xiic-i2c",
|
||||||
.fixed_rate = 50000000,
|
.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];
|
return gnss_name[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
struct ptp_ocp_nvmem_match_info {
|
||||||
ptp_ocp_firstchild(struct device *dev, void *data)
|
struct ptp_ocp *bp;
|
||||||
{
|
const void * const tag;
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
ptp_ocp_read_i2c(struct i2c_adapter *adap, u8 addr, u8 reg, u8 sz, u8 *data)
|
|
||||||
{
|
|
||||||
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. */
|
static int
|
||||||
while (sz) {
|
ptp_ocp_nvmem_match(struct device *dev, const void *data)
|
||||||
len = min_t(u8, sz, 2);
|
{
|
||||||
msgs[1].len = len;
|
const struct ptp_ocp_nvmem_match_info *info = data;
|
||||||
err = i2c_transfer(adap, msgs, 2);
|
|
||||||
if (err != msgs[1].len)
|
dev = dev->parent;
|
||||||
return err;
|
if (!i2c_verify_client(dev) || info->tag != dev->platform_data)
|
||||||
msgs[1].buf += len;
|
return 0;
|
||||||
reg += len;
|
|
||||||
sz -= len;
|
while ((dev = dev->parent))
|
||||||
}
|
if (dev->driver && !strcmp(dev->driver->name, KBUILD_MODNAME))
|
||||||
|
return info->bp == dev_get_drvdata(dev);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static inline struct nvmem_device *
|
||||||
ptp_ocp_get_serial_number(struct ptp_ocp *bp)
|
ptp_ocp_nvmem_device_get(struct ptp_ocp *bp, const void * const tag)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *adap;
|
struct ptp_ocp_nvmem_match_info info = { .bp = bp, .tag = tag };
|
||||||
struct device *dev;
|
|
||||||
int err;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ptp_ocp_read_eeprom(struct ptp_ocp *bp)
|
||||||
|
{
|
||||||
|
const struct ptp_ocp_eeprom_map *map;
|
||||||
|
struct nvmem_device *nvmem;
|
||||||
|
const void *tag;
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (!bp->i2c_ctrl)
|
if (!bp->i2c_ctrl)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dev = device_find_child(&bp->i2c_ctrl->dev, NULL, ptp_ocp_firstchild);
|
tag = NULL;
|
||||||
if (!dev) {
|
nvmem = NULL;
|
||||||
dev_err(&bp->pdev->dev, "Can't find I2C adapter\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
adap = i2c_verify_adapter(dev);
|
for (map = bp->eeprom_map; map->len; map++) {
|
||||||
if (!adap) {
|
if (map->tag != tag) {
|
||||||
dev_err(&bp->pdev->dev, "device '%s' isn't an I2C adapter\n",
|
tag = map->tag;
|
||||||
dev_name(dev));
|
ptp_ocp_nvmem_device_put(&nvmem);
|
||||||
|
}
|
||||||
|
if (!nvmem) {
|
||||||
|
nvmem = ptp_ocp_nvmem_device_get(bp, tag);
|
||||||
|
if (!nvmem)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
ret = nvmem_device_read(nvmem, map->off, map->len,
|
||||||
err = ptp_ocp_read_i2c(adap, 0x58, 0x9A, 6, bp->serial);
|
BP_MAP_ENTRY_ADDR(bp, map));
|
||||||
if (err) {
|
if (ret != map->len)
|
||||||
dev_err(&bp->pdev->dev, "could not read eeprom: %d\n", err);
|
goto read_fail;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bp->has_serial = true;
|
bp->has_eeprom_data = true;
|
||||||
|
|
||||||
out:
|
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 *
|
static struct device *
|
||||||
|
@ -1109,15 +1157,22 @@ ptp_ocp_devlink_info_get(struct devlink *devlink, struct devlink_info_req *req,
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bp->has_serial)
|
if (!bp->has_eeprom_data) {
|
||||||
ptp_ocp_get_serial_number(bp);
|
ptp_ocp_read_eeprom(bp);
|
||||||
|
if (!bp->has_eeprom_data)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (bp->has_serial) {
|
|
||||||
sprintf(buf, "%pM", bp->serial);
|
sprintf(buf, "%pM", bp->serial);
|
||||||
err = devlink_info_serial_number_put(req, buf);
|
err = devlink_info_serial_number_put(req, buf);
|
||||||
if (err)
|
if (err)
|
||||||
return 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1412,6 +1467,7 @@ static int
|
||||||
ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
|
ptp_ocp_fb_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
|
||||||
{
|
{
|
||||||
bp->flash_start = 1024 * 4096;
|
bp->flash_start = 1024 * 4096;
|
||||||
|
bp->eeprom_map = fb_eeprom_map;
|
||||||
|
|
||||||
ptp_ocp_tod_init(bp);
|
ptp_ocp_tod_init(bp);
|
||||||
ptp_ocp_nmea_out_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);
|
struct ptp_ocp *bp = dev_get_drvdata(dev);
|
||||||
|
|
||||||
if (!bp->has_serial)
|
if (!bp->has_eeprom_data)
|
||||||
ptp_ocp_get_serial_number(bp);
|
ptp_ocp_read_eeprom(bp);
|
||||||
|
|
||||||
return sysfs_emit(buf, "%pM\n", bp->serial);
|
return sysfs_emit(buf, "%pM\n", bp->serial);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue