drivers: ptp_clock: Add NXP ENET PTP Clock Driver
Add Driver for NXP ENET PTP Clock device Signed-off-by: Declan Snyder <declan.snyder@nxp.com>
This commit is contained in:
parent
cc5bd51177
commit
d85171fe6f
5 changed files with 299 additions and 0 deletions
|
|
@ -5,3 +5,4 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/ptp_clock.h)
|
|||
zephyr_library()
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_PTP_CLOCK ptp_clock.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_PTP_CLOCK_NXP_ENET ptp_clock_nxp_enet.c)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ config PTP_CLOCK
|
|||
|
||||
if PTP_CLOCK
|
||||
|
||||
source "drivers/ptp_clock/Kconfig.nxp_enet"
|
||||
|
||||
config PTP_CLOCK_INIT_PRIORITY
|
||||
int "Init priority"
|
||||
default 75
|
||||
|
|
|
|||
9
drivers/ptp_clock/Kconfig.nxp_enet
Normal file
9
drivers/ptp_clock/Kconfig.nxp_enet
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Copyright 2023 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config PTP_CLOCK_NXP_ENET
|
||||
bool "NXP ENET PTP Clock driver"
|
||||
default y if DT_HAS_NXP_ENET_PTP_CLOCK_ENABLED && \
|
||||
(PTP_CLOCK || NET_L2_PTP)
|
||||
help
|
||||
Enable NXP ENET PTP clock support.
|
||||
268
drivers/ptp_clock/ptp_clock_nxp_enet.c
Normal file
268
drivers/ptp_clock/ptp_clock_nxp_enet.c
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* Copyright 2023 NXP
|
||||
*
|
||||
* Based on a commit to drivers/ethernet/eth_mcux.c which was:
|
||||
* Copyright (c) 2018 Intel Coporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT nxp_enet_ptp_clock
|
||||
|
||||
#include <zephyr/drivers/ptp_clock.h>
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/clock_control.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/drivers/ethernet/eth_nxp_enet.h>
|
||||
|
||||
#include "fsl_enet.h"
|
||||
|
||||
struct ptp_clock_nxp_enet_config {
|
||||
ENET_Type *base;
|
||||
const struct pinctrl_dev_config *pincfg;
|
||||
const struct device *port;
|
||||
const struct device *clock_dev;
|
||||
struct device *clock_subsys;
|
||||
void (*irq_config_func)(void);
|
||||
};
|
||||
|
||||
struct ptp_clock_nxp_enet_data {
|
||||
double clock_ratio;
|
||||
enet_handle_t enet_handle;
|
||||
struct k_mutex ptp_mutex;
|
||||
};
|
||||
|
||||
static int ptp_clock_nxp_enet_set(const struct device *dev,
|
||||
struct net_ptp_time *tm)
|
||||
{
|
||||
const struct ptp_clock_nxp_enet_config *config = dev->config;
|
||||
struct ptp_clock_nxp_enet_data *data = dev->data;
|
||||
enet_ptp_time_t enet_time;
|
||||
|
||||
enet_time.second = tm->second;
|
||||
enet_time.nanosecond = tm->nanosecond;
|
||||
|
||||
ENET_Ptp1588SetTimer(config->base, &data->enet_handle, &enet_time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ptp_clock_nxp_enet_get(const struct device *dev,
|
||||
struct net_ptp_time *tm)
|
||||
{
|
||||
const struct ptp_clock_nxp_enet_config *config = dev->config;
|
||||
struct ptp_clock_nxp_enet_data *data = dev->data;
|
||||
enet_ptp_time_t enet_time;
|
||||
|
||||
ENET_Ptp1588GetTimer(config->base, &data->enet_handle, &enet_time);
|
||||
|
||||
tm->second = enet_time.second;
|
||||
tm->nanosecond = enet_time.nanosecond;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ptp_clock_nxp_enet_adjust(const struct device *dev,
|
||||
int increment)
|
||||
{
|
||||
const struct ptp_clock_nxp_enet_config *config = dev->config;
|
||||
int ret = 0;
|
||||
int key;
|
||||
|
||||
if ((increment <= (int32_t)(-NSEC_PER_SEC)) ||
|
||||
(increment >= (int32_t)NSEC_PER_SEC)) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
key = irq_lock();
|
||||
if (config->base->ATPER != NSEC_PER_SEC) {
|
||||
ret = -EBUSY;
|
||||
} else {
|
||||
/* Seconds counter is handled by software. Change the
|
||||
* period of one software second to adjust the clock.
|
||||
*/
|
||||
config->base->ATPER = NSEC_PER_SEC - increment;
|
||||
ret = 0;
|
||||
}
|
||||
irq_unlock(key);
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
static int ptp_clock_nxp_enet_rate_adjust(const struct device *dev,
|
||||
double ratio)
|
||||
{
|
||||
const struct ptp_clock_nxp_enet_config *config = dev->config;
|
||||
struct ptp_clock_nxp_enet_data *data = dev->data;
|
||||
int corr;
|
||||
int32_t mul;
|
||||
double val;
|
||||
uint32_t enet_ref_pll_rate;
|
||||
|
||||
(void) clock_control_get_rate(config->clock_dev, config->clock_subsys,
|
||||
&enet_ref_pll_rate);
|
||||
int hw_inc = NSEC_PER_SEC / enet_ref_pll_rate;
|
||||
|
||||
/* No change needed. */
|
||||
if ((ratio > 1.0 && ratio - 1.0 < 0.00000001) ||
|
||||
(ratio < 1.0 && 1.0 - ratio < 0.00000001)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ratio *= data->clock_ratio;
|
||||
|
||||
/* Limit possible ratio. */
|
||||
if ((ratio > 1.0 + 1.0/(2 * hw_inc)) ||
|
||||
(ratio < 1.0 - 1.0/(2 * hw_inc))) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Save new ratio. */
|
||||
data->clock_ratio = ratio;
|
||||
|
||||
if (ratio < 1.0) {
|
||||
corr = hw_inc - 1;
|
||||
val = 1.0 / (hw_inc * (1.0 - ratio));
|
||||
} else if (ratio > 1.0) {
|
||||
corr = hw_inc + 1;
|
||||
val = 1.0 / (hw_inc * (ratio - 1.0));
|
||||
} else {
|
||||
val = 0;
|
||||
corr = hw_inc;
|
||||
}
|
||||
|
||||
if (val >= INT32_MAX) {
|
||||
/* Value is too high.
|
||||
* It is not possible to adjust the rate of the clock.
|
||||
*/
|
||||
mul = 0;
|
||||
} else {
|
||||
mul = val;
|
||||
}
|
||||
|
||||
k_mutex_lock(&data->ptp_mutex, K_FOREVER);
|
||||
|
||||
ENET_Ptp1588AdjustTimer(config->base, corr, mul);
|
||||
|
||||
k_mutex_unlock(&data->ptp_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nxp_enet_ptp_clock_callback(const struct device *dev,
|
||||
enum nxp_enet_callback_reason event,
|
||||
union nxp_enet_ptp_data *ptp_data)
|
||||
{
|
||||
const struct ptp_clock_nxp_enet_config *config = dev->config;
|
||||
struct ptp_clock_nxp_enet_data *data = dev->data;
|
||||
|
||||
if (event == nxp_enet_module_reset) {
|
||||
enet_ptp_config_t ptp_config;
|
||||
uint32_t enet_ref_pll_rate;
|
||||
uint8_t ptp_multicast[6] = { 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00 };
|
||||
uint8_t ptp_peer_multicast[6] = { 0x01, 0x80, 0xC2, 0x00, 0x00, 0x0E };
|
||||
|
||||
(void) clock_control_get_rate(config->clock_dev, config->clock_subsys,
|
||||
&enet_ref_pll_rate);
|
||||
|
||||
ENET_AddMulticastGroup(config->base, ptp_multicast);
|
||||
ENET_AddMulticastGroup(config->base, ptp_peer_multicast);
|
||||
|
||||
/* only for ERRATA_2579 */
|
||||
ptp_config.channel = kENET_PtpTimerChannel3;
|
||||
ptp_config.ptp1588ClockSrc_Hz = enet_ref_pll_rate;
|
||||
data->clock_ratio = 1.0;
|
||||
|
||||
ENET_Ptp1588SetChannelMode(config->base, kENET_PtpTimerChannel3,
|
||||
kENET_PtpChannelPulseHighonCompare, true);
|
||||
ENET_Ptp1588Configure(config->base, &data->enet_handle,
|
||||
&ptp_config);
|
||||
}
|
||||
|
||||
if (ptp_data != NULL) {
|
||||
/* Share the mutex with mac driver */
|
||||
ptp_data->for_mac.ptp_mutex = &data->ptp_mutex;
|
||||
}
|
||||
}
|
||||
|
||||
static int ptp_clock_nxp_enet_init(const struct device *port)
|
||||
{
|
||||
const struct ptp_clock_nxp_enet_config *config = port->config;
|
||||
struct ptp_clock_nxp_enet_data *data = port->data;
|
||||
int ret;
|
||||
|
||||
ret = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
k_mutex_init(&data->ptp_mutex);
|
||||
|
||||
config->irq_config_func();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ptp_clock_nxp_enet_isr(const struct device *dev)
|
||||
{
|
||||
const struct ptp_clock_nxp_enet_config *config = dev->config;
|
||||
struct ptp_clock_nxp_enet_data *data = dev->data;
|
||||
enet_ptp_timer_channel_t channel;
|
||||
|
||||
unsigned int irq_lock_key = irq_lock();
|
||||
|
||||
/* clear channel */
|
||||
for (channel = kENET_PtpTimerChannel1; channel <= kENET_PtpTimerChannel4; channel++) {
|
||||
if (ENET_Ptp1588GetChannelStatus(config->base, channel)) {
|
||||
ENET_Ptp1588ClearChannelStatus(config->base, channel);
|
||||
}
|
||||
}
|
||||
|
||||
ENET_TimeStampIRQHandler(config->base, &data->enet_handle);
|
||||
|
||||
irq_unlock(irq_lock_key);
|
||||
}
|
||||
|
||||
static const struct ptp_clock_driver_api ptp_clock_nxp_enet_api = {
|
||||
.set = ptp_clock_nxp_enet_set,
|
||||
.get = ptp_clock_nxp_enet_get,
|
||||
.adjust = ptp_clock_nxp_enet_adjust,
|
||||
.rate_adjust = ptp_clock_nxp_enet_rate_adjust,
|
||||
};
|
||||
|
||||
#define PTP_CLOCK_NXP_ENET_INIT(n) \
|
||||
static void nxp_enet_ptp_clock_##n##_irq_config_func(void) \
|
||||
{ \
|
||||
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, 0, irq), \
|
||||
DT_INST_IRQ_BY_IDX(n, 0, priority), \
|
||||
ptp_clock_nxp_enet_isr, \
|
||||
DEVICE_DT_INST_GET(n), \
|
||||
0); \
|
||||
irq_enable(DT_INST_IRQ_BY_IDX(n, 0, irq)); \
|
||||
} \
|
||||
\
|
||||
PINCTRL_DT_INST_DEFINE(n); \
|
||||
\
|
||||
static const struct ptp_clock_nxp_enet_config \
|
||||
ptp_clock_nxp_enet_##n##_config = { \
|
||||
.base = (ENET_Type *) DT_REG_ADDR(DT_INST_PARENT(n)), \
|
||||
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
|
||||
.port = DEVICE_DT_INST_GET(n), \
|
||||
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
|
||||
.clock_subsys = (void *) \
|
||||
DT_INST_CLOCKS_CELL_BY_IDX(n, 0, name), \
|
||||
.irq_config_func = \
|
||||
nxp_enet_ptp_clock_##n##_irq_config_func, \
|
||||
}; \
|
||||
\
|
||||
static struct ptp_clock_nxp_enet_data ptp_clock_nxp_enet_##n##_data; \
|
||||
\
|
||||
DEVICE_DT_INST_DEFINE(n, &ptp_clock_nxp_enet_init, NULL, \
|
||||
&ptp_clock_nxp_enet_##n##_data, \
|
||||
&ptp_clock_nxp_enet_##n##_config, \
|
||||
POST_KERNEL, CONFIG_PTP_CLOCK_INIT_PRIORITY, \
|
||||
&ptp_clock_nxp_enet_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(PTP_CLOCK_NXP_ENET_INIT)
|
||||
|
|
@ -7,7 +7,14 @@
|
|||
#ifndef ZEPHYR_INCLUDE_DRIVERS_ETH_NXP_ENET_H__
|
||||
#define ZEPHYR_INCLUDE_DRIVERS_ETH_NXP_ENET_H__
|
||||
|
||||
/*
|
||||
* This header is for NXP ENET driver development
|
||||
* and has definitions for internal implementations
|
||||
* not to be used by application
|
||||
*/
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -27,10 +34,22 @@ enum nxp_enet_callback_reason {
|
|||
nxp_enet_interrupt_enabled,
|
||||
};
|
||||
|
||||
struct nxp_enet_ptp_data_for_mac {
|
||||
struct k_mutex *ptp_mutex;
|
||||
};
|
||||
|
||||
union nxp_enet_ptp_data {
|
||||
struct nxp_enet_ptp_data_for_mac for_mac;
|
||||
};
|
||||
|
||||
/* Calback for mdio device called from mac driver */
|
||||
void nxp_enet_mdio_callback(const struct device *mdio_dev,
|
||||
enum nxp_enet_callback_reason event);
|
||||
|
||||
void nxp_enet_ptp_clock_callback(const struct device *dev,
|
||||
enum nxp_enet_callback_reason event,
|
||||
union nxp_enet_ptp_data *ptp_data);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue