mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-05 18:14:07 +08:00
d21c784c78
Define a global ptp_qoriq structure pointer, and export to use. The ptp clock operations will be used in dpaa2-eth driver. For example, supporting one step timestamping needs to write current time to hardware frame annotation before sending and then hardware inserts the delay time on frame during sending. So in driver, at least clock gettime operation will be needed to make sure right time is written to hardware frame annotation for one step timestamping. Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
263 lines
5.5 KiB
C
263 lines
5.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright 2013-2016 Freescale Semiconductor Inc.
|
|
* Copyright 2016-2018 NXP
|
|
* Copyright 2020 NXP
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_address.h>
|
|
#include <linux/msi.h>
|
|
#include <linux/fsl/mc.h>
|
|
|
|
#include "dpaa2-ptp.h"
|
|
|
|
static int dpaa2_ptp_enable(struct ptp_clock_info *ptp,
|
|
struct ptp_clock_request *rq, int on)
|
|
{
|
|
struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps);
|
|
struct fsl_mc_device *mc_dev;
|
|
struct device *dev;
|
|
u32 mask = 0;
|
|
u32 bit;
|
|
int err;
|
|
|
|
dev = ptp_qoriq->dev;
|
|
mc_dev = to_fsl_mc_device(dev);
|
|
|
|
switch (rq->type) {
|
|
case PTP_CLK_REQ_EXTTS:
|
|
switch (rq->extts.index) {
|
|
case 0:
|
|
bit = DPRTC_EVENT_ETS1;
|
|
break;
|
|
case 1:
|
|
bit = DPRTC_EVENT_ETS2;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
if (on)
|
|
extts_clean_up(ptp_qoriq, rq->extts.index, false);
|
|
break;
|
|
case PTP_CLK_REQ_PPS:
|
|
bit = DPRTC_EVENT_PPS;
|
|
break;
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
err = dprtc_get_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle,
|
|
DPRTC_IRQ_INDEX, &mask);
|
|
if (err < 0) {
|
|
dev_err(dev, "dprtc_get_irq_mask(): %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
if (on)
|
|
mask |= bit;
|
|
else
|
|
mask &= ~bit;
|
|
|
|
err = dprtc_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle,
|
|
DPRTC_IRQ_INDEX, mask);
|
|
if (err < 0) {
|
|
dev_err(dev, "dprtc_set_irq_mask(): %d\n", err);
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct ptp_clock_info dpaa2_ptp_caps = {
|
|
.owner = THIS_MODULE,
|
|
.name = "DPAA2 PTP Clock",
|
|
.max_adj = 512000,
|
|
.n_alarm = 2,
|
|
.n_ext_ts = 2,
|
|
.n_per_out = 3,
|
|
.n_pins = 0,
|
|
.pps = 1,
|
|
.adjfine = ptp_qoriq_adjfine,
|
|
.adjtime = ptp_qoriq_adjtime,
|
|
.gettime64 = ptp_qoriq_gettime,
|
|
.settime64 = ptp_qoriq_settime,
|
|
.enable = dpaa2_ptp_enable,
|
|
};
|
|
|
|
static irqreturn_t dpaa2_ptp_irq_handler_thread(int irq, void *priv)
|
|
{
|
|
struct ptp_qoriq *ptp_qoriq = priv;
|
|
struct ptp_clock_event event;
|
|
struct fsl_mc_device *mc_dev;
|
|
struct device *dev;
|
|
u32 status = 0;
|
|
int err;
|
|
|
|
dev = ptp_qoriq->dev;
|
|
mc_dev = to_fsl_mc_device(dev);
|
|
|
|
err = dprtc_get_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle,
|
|
DPRTC_IRQ_INDEX, &status);
|
|
if (unlikely(err)) {
|
|
dev_err(dev, "dprtc_get_irq_status err %d\n", err);
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
if (status & DPRTC_EVENT_PPS) {
|
|
event.type = PTP_CLOCK_PPS;
|
|
ptp_clock_event(ptp_qoriq->clock, &event);
|
|
}
|
|
|
|
if (status & DPRTC_EVENT_ETS1)
|
|
extts_clean_up(ptp_qoriq, 0, true);
|
|
|
|
if (status & DPRTC_EVENT_ETS2)
|
|
extts_clean_up(ptp_qoriq, 1, true);
|
|
|
|
err = dprtc_clear_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle,
|
|
DPRTC_IRQ_INDEX, status);
|
|
if (unlikely(err)) {
|
|
dev_err(dev, "dprtc_clear_irq_status err %d\n", err);
|
|
return IRQ_NONE;
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int dpaa2_ptp_probe(struct fsl_mc_device *mc_dev)
|
|
{
|
|
struct device *dev = &mc_dev->dev;
|
|
struct fsl_mc_device_irq *irq;
|
|
struct ptp_qoriq *ptp_qoriq;
|
|
struct device_node *node;
|
|
void __iomem *base;
|
|
int err;
|
|
|
|
ptp_qoriq = devm_kzalloc(dev, sizeof(*ptp_qoriq), GFP_KERNEL);
|
|
if (!ptp_qoriq)
|
|
return -ENOMEM;
|
|
|
|
err = fsl_mc_portal_allocate(mc_dev, 0, &mc_dev->mc_io);
|
|
if (err) {
|
|
if (err == -ENXIO)
|
|
err = -EPROBE_DEFER;
|
|
else
|
|
dev_err(dev, "fsl_mc_portal_allocate err %d\n", err);
|
|
goto err_exit;
|
|
}
|
|
|
|
err = dprtc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id,
|
|
&mc_dev->mc_handle);
|
|
if (err) {
|
|
dev_err(dev, "dprtc_open err %d\n", err);
|
|
goto err_free_mcp;
|
|
}
|
|
|
|
ptp_qoriq->dev = dev;
|
|
|
|
node = of_find_compatible_node(NULL, NULL, "fsl,dpaa2-ptp");
|
|
if (!node) {
|
|
err = -ENODEV;
|
|
goto err_close;
|
|
}
|
|
|
|
dev->of_node = node;
|
|
|
|
base = of_iomap(node, 0);
|
|
if (!base) {
|
|
err = -ENOMEM;
|
|
goto err_close;
|
|
}
|
|
|
|
err = fsl_mc_allocate_irqs(mc_dev);
|
|
if (err) {
|
|
dev_err(dev, "MC irqs allocation failed\n");
|
|
goto err_unmap;
|
|
}
|
|
|
|
irq = mc_dev->irqs[0];
|
|
ptp_qoriq->irq = irq->msi_desc->irq;
|
|
|
|
err = request_threaded_irq(ptp_qoriq->irq, NULL,
|
|
dpaa2_ptp_irq_handler_thread,
|
|
IRQF_NO_SUSPEND | IRQF_ONESHOT,
|
|
dev_name(dev), ptp_qoriq);
|
|
if (err < 0) {
|
|
dev_err(dev, "devm_request_threaded_irq(): %d\n", err);
|
|
goto err_free_mc_irq;
|
|
}
|
|
|
|
err = dprtc_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle,
|
|
DPRTC_IRQ_INDEX, 1);
|
|
if (err < 0) {
|
|
dev_err(dev, "dprtc_set_irq_enable(): %d\n", err);
|
|
goto err_free_threaded_irq;
|
|
}
|
|
|
|
err = ptp_qoriq_init(ptp_qoriq, base, &dpaa2_ptp_caps);
|
|
if (err)
|
|
goto err_free_threaded_irq;
|
|
|
|
dpaa2_phc_index = ptp_qoriq->phc_index;
|
|
dpaa2_ptp = ptp_qoriq;
|
|
dev_set_drvdata(dev, ptp_qoriq);
|
|
|
|
return 0;
|
|
|
|
err_free_threaded_irq:
|
|
free_irq(ptp_qoriq->irq, ptp_qoriq);
|
|
err_free_mc_irq:
|
|
fsl_mc_free_irqs(mc_dev);
|
|
err_unmap:
|
|
iounmap(base);
|
|
err_close:
|
|
dprtc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
|
|
err_free_mcp:
|
|
fsl_mc_portal_free(mc_dev->mc_io);
|
|
err_exit:
|
|
return err;
|
|
}
|
|
|
|
static int dpaa2_ptp_remove(struct fsl_mc_device *mc_dev)
|
|
{
|
|
struct device *dev = &mc_dev->dev;
|
|
struct ptp_qoriq *ptp_qoriq;
|
|
|
|
ptp_qoriq = dev_get_drvdata(dev);
|
|
|
|
dpaa2_phc_index = -1;
|
|
ptp_qoriq_free(ptp_qoriq);
|
|
|
|
fsl_mc_free_irqs(mc_dev);
|
|
dprtc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
|
|
fsl_mc_portal_free(mc_dev->mc_io);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct fsl_mc_device_id dpaa2_ptp_match_id_table[] = {
|
|
{
|
|
.vendor = FSL_MC_VENDOR_FREESCALE,
|
|
.obj_type = "dprtc",
|
|
},
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(fslmc, dpaa2_ptp_match_id_table);
|
|
|
|
static struct fsl_mc_driver dpaa2_ptp_drv = {
|
|
.driver = {
|
|
.name = KBUILD_MODNAME,
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.probe = dpaa2_ptp_probe,
|
|
.remove = dpaa2_ptp_remove,
|
|
.match_id_table = dpaa2_ptp_match_id_table,
|
|
};
|
|
|
|
module_fsl_mc_driver(dpaa2_ptp_drv);
|
|
|
|
MODULE_LICENSE("GPL v2");
|
|
MODULE_DESCRIPTION("DPAA2 PTP Clock Driver");
|