Commit 9bdf43b3 authored by Yangbo Lu's avatar Yangbo Lu Committed by Greg Kroah-Hartman

staging: fsl-dpaa2/rtc: add rtc driver

This patch is to add driver for the DPAA2 1588 timer module (RTC)
which interfaces to up to an unlimited number of 10/100/1000 or
10G ethernet MACs, providing current time, alarm, and fiper support.
The 1588 IP control block includes these distinctive features.

- External GPIO trigger for time-stamping
- 2 Time-stamp alarms
- 3 FIPER pulse generators
- Phase adjusted output timer clock

Currently this driver only supports basic functions like
settime/gettime/adjtime/adjfreq.
Signed-off-by: default avatarYangbo Lu <yangbo.lu@nxp.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent d08e2f97
......@@ -24,3 +24,11 @@ config FSL_DPAA2_ETHSW
---help---
Driver for Freescale DPAA2 Ethernet Switch. Select
BRIDGE to have support for bridge tools.
config FSL_DPAA2_PTP_CLOCK
tristate "Freescale DPAA2 PTP Clock"
depends on FSL_DPAA2
select PTP_1588_CLOCK
help
This driver adds support for using the DPAA2 1588 timer module
as a PTP clock.
......@@ -4,3 +4,4 @@
obj-$(CONFIG_FSL_DPAA2_ETH) += ethernet/
obj-$(CONFIG_FSL_DPAA2_ETHSW) += ethsw/
obj-$(CONFIG_FSL_DPAA2_PTP_CLOCK) += rtc/
#
# Makefile for the Freescale DPAA2 PTP clock
#
obj-$(CONFIG_FSL_DPAA2_PTP_CLOCK) += dpaa2-rtc.o
dpaa2-rtc-objs := rtc.o dprtc.o
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2013-2016 Freescale Semiconductor Inc.
* Copyright 2016-2018 NXP
*/
#ifndef _FSL_DPRTC_CMD_H
#define _FSL_DPRTC_CMD_H
/* DPRTC Version */
#define DPRTC_VER_MAJOR 2
#define DPRTC_VER_MINOR 0
/* Command versioning */
#define DPRTC_CMD_BASE_VERSION 1
#define DPRTC_CMD_ID_OFFSET 4
#define DPRTC_CMD(id) (((id) << DPRTC_CMD_ID_OFFSET) | DPRTC_CMD_BASE_VERSION)
/* Command IDs */
#define DPRTC_CMDID_CLOSE DPRTC_CMD(0x800)
#define DPRTC_CMDID_OPEN DPRTC_CMD(0x810)
#define DPRTC_CMDID_CREATE DPRTC_CMD(0x910)
#define DPRTC_CMDID_DESTROY DPRTC_CMD(0x990)
#define DPRTC_CMDID_GET_API_VERSION DPRTC_CMD(0xa10)
#define DPRTC_CMDID_ENABLE DPRTC_CMD(0x002)
#define DPRTC_CMDID_DISABLE DPRTC_CMD(0x003)
#define DPRTC_CMDID_GET_ATTR DPRTC_CMD(0x004)
#define DPRTC_CMDID_RESET DPRTC_CMD(0x005)
#define DPRTC_CMDID_IS_ENABLED DPRTC_CMD(0x006)
#define DPRTC_CMDID_SET_IRQ_ENABLE DPRTC_CMD(0x012)
#define DPRTC_CMDID_GET_IRQ_ENABLE DPRTC_CMD(0x013)
#define DPRTC_CMDID_SET_IRQ_MASK DPRTC_CMD(0x014)
#define DPRTC_CMDID_GET_IRQ_MASK DPRTC_CMD(0x015)
#define DPRTC_CMDID_GET_IRQ_STATUS DPRTC_CMD(0x016)
#define DPRTC_CMDID_CLEAR_IRQ_STATUS DPRTC_CMD(0x017)
#define DPRTC_CMDID_SET_CLOCK_OFFSET DPRTC_CMD(0x1d0)
#define DPRTC_CMDID_SET_FREQ_COMPENSATION DPRTC_CMD(0x1d1)
#define DPRTC_CMDID_GET_FREQ_COMPENSATION DPRTC_CMD(0x1d2)
#define DPRTC_CMDID_GET_TIME DPRTC_CMD(0x1d3)
#define DPRTC_CMDID_SET_TIME DPRTC_CMD(0x1d4)
#define DPRTC_CMDID_SET_ALARM DPRTC_CMD(0x1d5)
#define DPRTC_CMDID_SET_PERIODIC_PULSE DPRTC_CMD(0x1d6)
#define DPRTC_CMDID_CLEAR_PERIODIC_PULSE DPRTC_CMD(0x1d7)
#define DPRTC_CMDID_SET_EXT_TRIGGER DPRTC_CMD(0x1d8)
#define DPRTC_CMDID_CLEAR_EXT_TRIGGER DPRTC_CMD(0x1d9)
#define DPRTC_CMDID_GET_EXT_TRIGGER_TIMESTAMP DPRTC_CMD(0x1dA)
/* Macros for accessing command fields smaller than 1byte */
#define DPRTC_MASK(field) \
GENMASK(DPRTC_##field##_SHIFT + DPRTC_##field##_SIZE - 1, \
DPRTC_##field##_SHIFT)
#define dprtc_get_field(var, field) \
(((var) & DPRTC_MASK(field)) >> DPRTC_##field##_SHIFT)
#pragma pack(push, 1)
struct dprtc_cmd_open {
__le32 dprtc_id;
};
struct dprtc_cmd_destroy {
__le32 object_id;
};
#define DPRTC_ENABLE_SHIFT 0
#define DPRTC_ENABLE_SIZE 1
struct dprtc_rsp_is_enabled {
u8 en;
};
struct dprtc_cmd_get_irq {
__le32 pad;
u8 irq_index;
};
struct dprtc_cmd_set_irq_enable {
u8 en;
u8 pad[3];
u8 irq_index;
};
struct dprtc_rsp_get_irq_enable {
u8 en;
};
struct dprtc_cmd_set_irq_mask {
__le32 mask;
u8 irq_index;
};
struct dprtc_rsp_get_irq_mask {
__le32 mask;
};
struct dprtc_cmd_get_irq_status {
__le32 status;
u8 irq_index;
};
struct dprtc_rsp_get_irq_status {
__le32 status;
};
struct dprtc_cmd_clear_irq_status {
__le32 status;
u8 irq_index;
};
struct dprtc_rsp_get_attributes {
__le32 pad;
__le32 id;
};
struct dprtc_cmd_set_clock_offset {
__le64 offset;
};
struct dprtc_get_freq_compensation {
__le32 freq_compensation;
};
struct dprtc_time {
__le64 time;
};
struct dprtc_rsp_get_api_version {
__le16 major;
__le16 minor;
};
#pragma pack(pop)
#endif /* _FSL_DPRTC_CMD_H */
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2013-2016 Freescale Semiconductor Inc.
* Copyright 2016-2018 NXP
*/
#ifndef __FSL_DPRTC_H
#define __FSL_DPRTC_H
/* Data Path Real Time Counter API
* Contains initialization APIs and runtime control APIs for RTC
*/
struct fsl_mc_io;
/**
* Number of irq's
*/
#define DPRTC_MAX_IRQ_NUM 1
#define DPRTC_IRQ_INDEX 0
/**
* Interrupt event masks:
*/
/**
* Interrupt event mask indicating alarm event had occurred
*/
#define DPRTC_EVENT_ALARM 0x40000000
/**
* Interrupt event mask indicating periodic pulse event had occurred
*/
#define DPRTC_EVENT_PPS 0x08000000
int dprtc_open(struct fsl_mc_io *mc_io,
u32 cmd_flags,
int dprtc_id,
u16 *token);
int dprtc_close(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token);
/**
* struct dprtc_cfg - Structure representing DPRTC configuration
* @options: place holder
*/
struct dprtc_cfg {
u32 options;
};
int dprtc_create(struct fsl_mc_io *mc_io,
u16 dprc_token,
u32 cmd_flags,
const struct dprtc_cfg *cfg,
u32 *obj_id);
int dprtc_destroy(struct fsl_mc_io *mc_io,
u16 dprc_token,
u32 cmd_flags,
u32 object_id);
int dprtc_enable(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token);
int dprtc_disable(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token);
int dprtc_is_enabled(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
int *en);
int dprtc_reset(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token);
int dprtc_set_clock_offset(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
int64_t offset);
int dprtc_set_freq_compensation(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
u32 freq_compensation);
int dprtc_get_freq_compensation(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
u32 *freq_compensation);
int dprtc_get_time(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
uint64_t *time);
int dprtc_set_time(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
uint64_t time);
int dprtc_set_alarm(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
uint64_t time);
int dprtc_set_irq_enable(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
u8 irq_index,
u8 en);
int dprtc_get_irq_enable(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
u8 irq_index,
u8 *en);
int dprtc_set_irq_mask(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
u8 irq_index,
u32 mask);
int dprtc_get_irq_mask(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
u8 irq_index,
u32 *mask);
int dprtc_get_irq_status(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
u8 irq_index,
u32 *status);
int dprtc_clear_irq_status(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
u8 irq_index,
u32 status);
/**
* struct dprtc_attr - Structure representing DPRTC attributes
* @id: DPRTC object ID
*/
struct dprtc_attr {
int id;
};
int dprtc_get_attributes(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 token,
struct dprtc_attr *attr);
int dprtc_get_api_version(struct fsl_mc_io *mc_io,
u32 cmd_flags,
u16 *major_ver,
u16 *minor_ver);
#endif /* __FSL_DPRTC_H */
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2013-2016 Freescale Semiconductor Inc.
* Copyright 2016-2018 NXP
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/ptp_clock_kernel.h>
#include <linux/fsl/mc.h>
#include "dprtc.h"
#include "dprtc-cmd.h"
struct ptp_dpaa2_priv {
struct fsl_mc_device *rtc_mc_dev;
struct ptp_clock *clock;
struct ptp_clock_info caps;
int phc_index;
u32 freq_comp;
};
/* PTP clock operations */
static int ptp_dpaa2_adjfreq(struct ptp_clock_info *ptp, s32 ppb)
{
struct ptp_dpaa2_priv *ptp_dpaa2 =
container_of(ptp, struct ptp_dpaa2_priv, caps);
struct fsl_mc_device *mc_dev = ptp_dpaa2->rtc_mc_dev;
struct device *dev = &mc_dev->dev;
u64 adj;
u32 diff, tmr_add;
int neg_adj = 0;
int err = 0;
if (ppb < 0) {
neg_adj = 1;
ppb = -ppb;
}
tmr_add = ptp_dpaa2->freq_comp;
adj = tmr_add;
adj *= ppb;
diff = div_u64(adj, 1000000000ULL);
tmr_add = neg_adj ? tmr_add - diff : tmr_add + diff;
err = dprtc_set_freq_compensation(mc_dev->mc_io, 0,
mc_dev->mc_handle, tmr_add);
if (err)
dev_err(dev, "dprtc_set_freq_compensation err %d\n", err);
return 0;
}
static int ptp_dpaa2_adjtime(struct ptp_clock_info *ptp, s64 delta)
{
struct ptp_dpaa2_priv *ptp_dpaa2 =
container_of(ptp, struct ptp_dpaa2_priv, caps);
struct fsl_mc_device *mc_dev = ptp_dpaa2->rtc_mc_dev;
struct device *dev = &mc_dev->dev;
s64 now;
int err = 0;
err = dprtc_get_time(mc_dev->mc_io, 0, mc_dev->mc_handle, &now);
if (err) {
dev_err(dev, "dprtc_get_time err %d\n", err);
return 0;
}
now += delta;
err = dprtc_set_time(mc_dev->mc_io, 0, mc_dev->mc_handle, now);
if (err) {
dev_err(dev, "dprtc_set_time err %d\n", err);
return 0;
}
return 0;
}
static int ptp_dpaa2_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
{
struct ptp_dpaa2_priv *ptp_dpaa2 =
container_of(ptp, struct ptp_dpaa2_priv, caps);
struct fsl_mc_device *mc_dev = ptp_dpaa2->rtc_mc_dev;
struct device *dev = &mc_dev->dev;
u64 ns;
u32 remainder;
int err = 0;
err = dprtc_get_time(mc_dev->mc_io, 0, mc_dev->mc_handle, &ns);
if (err) {
dev_err(dev, "dprtc_get_time err %d\n", err);
return 0;
}
ts->tv_sec = div_u64_rem(ns, 1000000000, &remainder);
ts->tv_nsec = remainder;
return 0;
}
static int ptp_dpaa2_settime(struct ptp_clock_info *ptp,
const struct timespec64 *ts)
{
struct ptp_dpaa2_priv *ptp_dpaa2 =
container_of(ptp, struct ptp_dpaa2_priv, caps);
struct fsl_mc_device *mc_dev = ptp_dpaa2->rtc_mc_dev;
struct device *dev = &mc_dev->dev;
u64 ns;
int err = 0;
ns = ts->tv_sec * 1000000000ULL;
ns += ts->tv_nsec;
err = dprtc_set_time(mc_dev->mc_io, 0, mc_dev->mc_handle, ns);
if (err)
dev_err(dev, "dprtc_set_time err %d\n", err);
return 0;
}
static struct ptp_clock_info ptp_dpaa2_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,
.adjfreq = ptp_dpaa2_adjfreq,
.adjtime = ptp_dpaa2_adjtime,
.gettime64 = ptp_dpaa2_gettime,
.settime64 = ptp_dpaa2_settime,
};
static int rtc_probe(struct fsl_mc_device *mc_dev)
{
struct device *dev = &mc_dev->dev;
struct ptp_dpaa2_priv *ptp_dpaa2;
u32 tmr_add = 0;
int err;
ptp_dpaa2 = kzalloc(sizeof(*ptp_dpaa2), GFP_KERNEL);
if (!ptp_dpaa2)
return -ENOMEM;
err = fsl_mc_portal_allocate(mc_dev, 0, &mc_dev->mc_io);
if (err) {
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_dpaa2->rtc_mc_dev = mc_dev;
err = dprtc_get_freq_compensation(mc_dev->mc_io, 0,
mc_dev->mc_handle, &tmr_add);
if (err) {
dev_err(dev, "dprtc_get_freq_compensation err %d\n", err);
goto err_close;
}
ptp_dpaa2->freq_comp = tmr_add;
ptp_dpaa2->caps = ptp_dpaa2_caps;
ptp_dpaa2->clock = ptp_clock_register(&ptp_dpaa2->caps, dev);
if (IS_ERR(ptp_dpaa2->clock)) {
err = PTR_ERR(ptp_dpaa2->clock);
goto err_close;
}
ptp_dpaa2->phc_index = ptp_clock_index(ptp_dpaa2->clock);
dev_set_drvdata(dev, ptp_dpaa2);
return 0;
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:
kfree(ptp_dpaa2);
dev_set_drvdata(dev, NULL);
return err;
}
static int rtc_remove(struct fsl_mc_device *mc_dev)
{
struct ptp_dpaa2_priv *ptp_dpaa2;
struct device *dev = &mc_dev->dev;
ptp_dpaa2 = dev_get_drvdata(dev);
ptp_clock_unregister(ptp_dpaa2->clock);
dprtc_close(mc_dev->mc_io, 0, mc_dev->mc_handle);
fsl_mc_portal_free(mc_dev->mc_io);
kfree(ptp_dpaa2);
dev_set_drvdata(dev, NULL);
return 0;
}
static const struct fsl_mc_device_id rtc_match_id_table[] = {
{
.vendor = FSL_MC_VENDOR_FREESCALE,
.obj_type = "dprtc",
},
{}
};
MODULE_DEVICE_TABLE(fslmc, rtc_match_id_table);
static struct fsl_mc_driver rtc_drv = {
.driver = {
.name = KBUILD_MODNAME,
.owner = THIS_MODULE,
},
.probe = rtc_probe,
.remove = rtc_remove,
.match_id_table = rtc_match_id_table,
};
module_fsl_mc_driver(rtc_drv);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("DPAA2 PTP Clock Driver");
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