mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 10:04:04 +02:00
Merge branch 'add-perout-library-for-rds-ptp-supported-phys'
Divya Koppera says: ==================== Add PEROUT library for RDS PTP supported phys Adds support for PEROUT library, where phy can generate periodic output signal on supported pin out. ==================== Link: https://patch.msgid.link/20250115090634.12941-1-divya.koppera@microchip.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
7d2eba0f83
|
|
@ -54,6 +54,243 @@ static int mchp_rds_phy_set_bits_mmd(struct mchp_rds_ptp_clock *clock,
|
|||
return phy_set_bits_mmd(phydev, PTP_MMD(clock), addr, val);
|
||||
}
|
||||
|
||||
static int mchp_get_pulsewidth(struct phy_device *phydev,
|
||||
struct ptp_perout_request *perout_request,
|
||||
int *pulse_width)
|
||||
{
|
||||
struct timespec64 ts_period;
|
||||
s64 ts_on_nsec, period_nsec;
|
||||
struct timespec64 ts_on;
|
||||
static const s64 sup_on_necs[] = {
|
||||
100, /* 100ns */
|
||||
500, /* 500ns */
|
||||
1000, /* 1us */
|
||||
5000, /* 5us */
|
||||
10000, /* 10us */
|
||||
50000, /* 50us */
|
||||
100000, /* 100us */
|
||||
500000, /* 500us */
|
||||
1000000, /* 1ms */
|
||||
5000000, /* 5ms */
|
||||
10000000, /* 10ms */
|
||||
50000000, /* 50ms */
|
||||
100000000, /* 100ms */
|
||||
200000000, /* 200ms */
|
||||
};
|
||||
|
||||
ts_period.tv_sec = perout_request->period.sec;
|
||||
ts_period.tv_nsec = perout_request->period.nsec;
|
||||
|
||||
ts_on.tv_sec = perout_request->on.sec;
|
||||
ts_on.tv_nsec = perout_request->on.nsec;
|
||||
ts_on_nsec = timespec64_to_ns(&ts_on);
|
||||
period_nsec = timespec64_to_ns(&ts_period);
|
||||
|
||||
if (period_nsec < 200) {
|
||||
phydev_warn(phydev, "perout period small, minimum is 200ns\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(sup_on_necs); i++) {
|
||||
if (ts_on_nsec <= sup_on_necs[i]) {
|
||||
*pulse_width = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
phydev_info(phydev, "pulse width is %d\n", *pulse_width);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_general_event_config(struct mchp_rds_ptp_clock *clock,
|
||||
int pulse_width)
|
||||
{
|
||||
int general_config;
|
||||
|
||||
general_config = mchp_rds_phy_read_mmd(clock, MCHP_RDS_PTP_GEN_CFG,
|
||||
MCHP_RDS_PTP_CLOCK);
|
||||
if (general_config < 0)
|
||||
return general_config;
|
||||
|
||||
general_config &= ~MCHP_RDS_PTP_GEN_CFG_LTC_EVT_MASK;
|
||||
general_config |= MCHP_RDS_PTP_GEN_CFG_LTC_EVT_SET(pulse_width);
|
||||
general_config &= ~MCHP_RDS_PTP_GEN_CFG_RELOAD_ADD;
|
||||
general_config |= MCHP_RDS_PTP_GEN_CFG_POLARITY;
|
||||
|
||||
return mchp_rds_phy_write_mmd(clock, MCHP_RDS_PTP_GEN_CFG,
|
||||
MCHP_RDS_PTP_CLOCK, general_config);
|
||||
}
|
||||
|
||||
static int mchp_set_clock_reload(struct mchp_rds_ptp_clock *clock,
|
||||
s64 period_sec, u32 period_nsec)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = mchp_rds_phy_write_mmd(clock,
|
||||
MCHP_RDS_PTP_CLK_TRGT_RELOAD_SEC_LO,
|
||||
MCHP_RDS_PTP_CLOCK,
|
||||
lower_16_bits(period_sec));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = mchp_rds_phy_write_mmd(clock,
|
||||
MCHP_RDS_PTP_CLK_TRGT_RELOAD_SEC_HI,
|
||||
MCHP_RDS_PTP_CLOCK,
|
||||
upper_16_bits(period_sec));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = mchp_rds_phy_write_mmd(clock,
|
||||
MCHP_RDS_PTP_CLK_TRGT_RELOAD_NS_LO,
|
||||
MCHP_RDS_PTP_CLOCK,
|
||||
lower_16_bits(period_nsec));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return mchp_rds_phy_write_mmd(clock,
|
||||
MCHP_RDS_PTP_CLK_TRGT_RELOAD_NS_HI,
|
||||
MCHP_RDS_PTP_CLOCK,
|
||||
upper_16_bits(period_nsec) & 0x3fff);
|
||||
}
|
||||
|
||||
static int mchp_set_clock_target(struct mchp_rds_ptp_clock *clock,
|
||||
s64 start_sec, u32 start_nsec)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Set the start time */
|
||||
rc = mchp_rds_phy_write_mmd(clock, MCHP_RDS_PTP_CLK_TRGT_SEC_LO,
|
||||
MCHP_RDS_PTP_CLOCK,
|
||||
lower_16_bits(start_sec));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = mchp_rds_phy_write_mmd(clock, MCHP_RDS_PTP_CLK_TRGT_SEC_HI,
|
||||
MCHP_RDS_PTP_CLOCK,
|
||||
upper_16_bits(start_sec));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = mchp_rds_phy_write_mmd(clock, MCHP_RDS_PTP_CLK_TRGT_NS_LO,
|
||||
MCHP_RDS_PTP_CLOCK,
|
||||
lower_16_bits(start_nsec));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return mchp_rds_phy_write_mmd(clock, MCHP_RDS_PTP_CLK_TRGT_NS_HI,
|
||||
MCHP_RDS_PTP_CLOCK,
|
||||
upper_16_bits(start_nsec) & 0x3fff);
|
||||
}
|
||||
|
||||
static int mchp_rds_ptp_perout_off(struct mchp_rds_ptp_clock *clock)
|
||||
{
|
||||
u16 general_config;
|
||||
int rc;
|
||||
|
||||
/* Set target to too far in the future, effectively disabling it */
|
||||
rc = mchp_set_clock_target(clock, 0xFFFFFFFF, 0);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
general_config = mchp_rds_phy_read_mmd(clock, MCHP_RDS_PTP_GEN_CFG,
|
||||
MCHP_RDS_PTP_CLOCK);
|
||||
general_config |= MCHP_RDS_PTP_GEN_CFG_RELOAD_ADD;
|
||||
rc = mchp_rds_phy_write_mmd(clock, MCHP_RDS_PTP_GEN_CFG,
|
||||
MCHP_RDS_PTP_CLOCK, general_config);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
clock->mchp_rds_ptp_event = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mchp_get_event(struct mchp_rds_ptp_clock *clock, int pin)
|
||||
{
|
||||
if (clock->mchp_rds_ptp_event < 0 && pin == clock->event_pin) {
|
||||
clock->mchp_rds_ptp_event = pin;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int mchp_rds_ptp_perout(struct ptp_clock_info *ptpci,
|
||||
struct ptp_perout_request *perout, int on)
|
||||
{
|
||||
struct mchp_rds_ptp_clock *clock = container_of(ptpci,
|
||||
struct mchp_rds_ptp_clock,
|
||||
caps);
|
||||
struct phy_device *phydev = clock->phydev;
|
||||
int ret, event_pin, pulsewidth;
|
||||
|
||||
/* Reject requests with unsupported flags */
|
||||
if (perout->flags & ~PTP_PEROUT_DUTY_CYCLE)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
event_pin = ptp_find_pin(clock->ptp_clock, PTP_PF_PEROUT,
|
||||
perout->index);
|
||||
if (event_pin != clock->event_pin)
|
||||
return -EINVAL;
|
||||
|
||||
if (!on) {
|
||||
ret = mchp_rds_ptp_perout_off(clock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!mchp_get_event(clock, event_pin))
|
||||
return -EINVAL;
|
||||
|
||||
ret = mchp_get_pulsewidth(phydev, perout, &pulsewidth);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Configure to pulse every period */
|
||||
ret = mchp_general_event_config(clock, pulsewidth);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = mchp_set_clock_target(clock, perout->start.sec,
|
||||
perout->start.nsec);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return mchp_set_clock_reload(clock, perout->period.sec,
|
||||
perout->period.nsec);
|
||||
}
|
||||
|
||||
static int mchp_rds_ptpci_enable(struct ptp_clock_info *ptpci,
|
||||
struct ptp_clock_request *request, int on)
|
||||
{
|
||||
switch (request->type) {
|
||||
case PTP_CLK_REQ_PEROUT:
|
||||
return mchp_rds_ptp_perout(ptpci, &request->perout, on);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static int mchp_rds_ptpci_verify(struct ptp_clock_info *ptpci, unsigned int pin,
|
||||
enum ptp_pin_function func, unsigned int chan)
|
||||
{
|
||||
struct mchp_rds_ptp_clock *clock = container_of(ptpci,
|
||||
struct mchp_rds_ptp_clock,
|
||||
caps);
|
||||
|
||||
if (!(pin == clock->event_pin && chan == 0))
|
||||
return -1;
|
||||
|
||||
switch (func) {
|
||||
case PTP_PF_NONE:
|
||||
case PTP_PF_PEROUT:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mchp_rds_ptp_flush_fifo(struct mchp_rds_ptp_clock *clock,
|
||||
enum mchp_rds_ptp_fifo_dir dir)
|
||||
{
|
||||
|
|
@ -479,6 +716,16 @@ static int mchp_rds_ptp_ltc_adjtime(struct ptp_clock_info *info, s64 delta)
|
|||
MCHP_RDS_PTP_CMD_CTL_LTC_STEP_NSEC);
|
||||
}
|
||||
|
||||
mutex_unlock(&clock->ptp_lock);
|
||||
info->gettime64(info, &ts);
|
||||
mutex_lock(&clock->ptp_lock);
|
||||
|
||||
/* Target update is required for pulse generation on events that
|
||||
* are enabled
|
||||
*/
|
||||
if (clock->mchp_rds_ptp_event >= 0)
|
||||
mchp_set_clock_target(clock,
|
||||
ts.tv_sec + MCHP_RDS_PTP_BUFFER_TIME, 0);
|
||||
out_unlock:
|
||||
mutex_unlock(&clock->ptp_lock);
|
||||
|
||||
|
|
@ -989,16 +1236,37 @@ struct mchp_rds_ptp_clock *mchp_rds_ptp_probe(struct phy_device *phydev, u8 mmd,
|
|||
clock->mmd = mmd;
|
||||
|
||||
mutex_init(&clock->ptp_lock);
|
||||
clock->pin_config = devm_kmalloc_array(&phydev->mdio.dev,
|
||||
MCHP_RDS_PTP_N_PIN,
|
||||
sizeof(*clock->pin_config),
|
||||
GFP_KERNEL);
|
||||
if (!clock->pin_config)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (int i = 0; i < MCHP_RDS_PTP_N_PIN; ++i) {
|
||||
struct ptp_pin_desc *p = &clock->pin_config[i];
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
snprintf(p->name, sizeof(p->name), "pin%d", i);
|
||||
p->index = i;
|
||||
p->func = PTP_PF_NONE;
|
||||
}
|
||||
/* Register PTP clock */
|
||||
clock->caps.owner = THIS_MODULE;
|
||||
snprintf(clock->caps.name, 30, "%s", phydev->drv->name);
|
||||
clock->caps.max_adj = MCHP_RDS_PTP_MAX_ADJ;
|
||||
clock->caps.n_ext_ts = 0;
|
||||
clock->caps.pps = 0;
|
||||
clock->caps.n_pins = MCHP_RDS_PTP_N_PIN;
|
||||
clock->caps.n_per_out = MCHP_RDS_PTP_N_PEROUT;
|
||||
clock->caps.pin_config = clock->pin_config;
|
||||
clock->caps.adjfine = mchp_rds_ptp_ltc_adjfine;
|
||||
clock->caps.adjtime = mchp_rds_ptp_ltc_adjtime;
|
||||
clock->caps.gettime64 = mchp_rds_ptp_ltc_gettime64;
|
||||
clock->caps.settime64 = mchp_rds_ptp_ltc_settime64;
|
||||
clock->caps.enable = mchp_rds_ptpci_enable;
|
||||
clock->caps.verify = mchp_rds_ptpci_verify;
|
||||
clock->caps.getcrosststamp = NULL;
|
||||
clock->ptp_clock = ptp_clock_register(&clock->caps,
|
||||
&phydev->mdio.dev);
|
||||
if (IS_ERR(clock->ptp_clock))
|
||||
|
|
@ -1021,6 +1289,8 @@ struct mchp_rds_ptp_clock *mchp_rds_ptp_probe(struct phy_device *phydev, u8 mmd,
|
|||
|
||||
phydev->mii_ts = &clock->mii_ts;
|
||||
|
||||
clock->mchp_rds_ptp_event = -1;
|
||||
|
||||
/* Timestamp selected by default to keep legacy API */
|
||||
phydev->default_timestamp = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -130,6 +130,23 @@
|
|||
#define MCHP_RDS_PTP_TSU_HARD_RESET 0xc1
|
||||
#define MCHP_RDS_PTP_TSU_HARDRESET BIT(0)
|
||||
|
||||
#define MCHP_RDS_PTP_CLK_TRGT_SEC_HI 0x15
|
||||
#define MCHP_RDS_PTP_CLK_TRGT_SEC_LO 0x16
|
||||
#define MCHP_RDS_PTP_CLK_TRGT_NS_HI 0x17
|
||||
#define MCHP_RDS_PTP_CLK_TRGT_NS_LO 0x18
|
||||
|
||||
#define MCHP_RDS_PTP_CLK_TRGT_RELOAD_SEC_HI 0x19
|
||||
#define MCHP_RDS_PTP_CLK_TRGT_RELOAD_SEC_LO 0x1a
|
||||
#define MCHP_RDS_PTP_CLK_TRGT_RELOAD_NS_HI 0x1b
|
||||
#define MCHP_RDS_PTP_CLK_TRGT_RELOAD_NS_LO 0x1c
|
||||
|
||||
#define MCHP_RDS_PTP_GEN_CFG 0x01
|
||||
#define MCHP_RDS_PTP_GEN_CFG_LTC_EVT_MASK GENMASK(11, 8)
|
||||
|
||||
#define MCHP_RDS_PTP_GEN_CFG_LTC_EVT_SET(value) (((value) & 0xF) << 4)
|
||||
#define MCHP_RDS_PTP_GEN_CFG_RELOAD_ADD BIT(0)
|
||||
#define MCHP_RDS_PTP_GEN_CFG_POLARITY BIT(1)
|
||||
|
||||
/* Represents 1ppm adjustment in 2^32 format with
|
||||
* each nsec contains 4 clock cycles in 250MHz.
|
||||
* The value is calculated as following: (1/1000000)/((2^-32)/4)
|
||||
|
|
@ -138,6 +155,10 @@
|
|||
#define MCHP_RDS_PTP_FIFO_SIZE 8
|
||||
#define MCHP_RDS_PTP_MAX_ADJ 31249999
|
||||
|
||||
#define MCHP_RDS_PTP_BUFFER_TIME 2
|
||||
#define MCHP_RDS_PTP_N_PIN 4
|
||||
#define MCHP_RDS_PTP_N_PEROUT 1
|
||||
|
||||
#define BASE_CLK(p) ((p)->clk_base_addr)
|
||||
#define BASE_PORT(p) ((p)->port_base_addr)
|
||||
#define PTP_MMD(p) ((p)->mmd)
|
||||
|
|
@ -176,6 +197,9 @@ struct mchp_rds_ptp_clock {
|
|||
/* Lock for phc */
|
||||
struct mutex ptp_lock;
|
||||
u8 mmd;
|
||||
int mchp_rds_ptp_event;
|
||||
int event_pin;
|
||||
struct ptp_pin_desc *pin_config;
|
||||
};
|
||||
|
||||
struct mchp_rds_ptp_rx_ts {
|
||||
|
|
|
|||
|
|
@ -238,6 +238,9 @@
|
|||
#define LAN887X_INT_MSK_LINK_UP_MSK BIT(1)
|
||||
#define LAN887X_INT_MSK_LINK_DOWN_MSK BIT(0)
|
||||
|
||||
#define LAN887X_MX_CHIP_TOP_REG_CONTROL1 0xF002
|
||||
#define LAN887X_MX_CHIP_TOP_REG_CONTROL1_EVT_EN BIT(8)
|
||||
|
||||
#define LAN887X_MX_CHIP_TOP_LINK_MSK (LAN887X_INT_MSK_LINK_UP_MSK |\
|
||||
LAN887X_INT_MSK_LINK_DOWN_MSK)
|
||||
|
||||
|
|
@ -1286,6 +1289,15 @@ static int lan887x_phy_init(struct phy_device *phydev)
|
|||
if (IS_ERR(priv->clock))
|
||||
return PTR_ERR(priv->clock);
|
||||
|
||||
/* Enable pin mux for EVT */
|
||||
phy_modify_mmd(phydev, MDIO_MMD_VEND1,
|
||||
LAN887X_MX_CHIP_TOP_REG_CONTROL1,
|
||||
LAN887X_MX_CHIP_TOP_REG_CONTROL1_EVT_EN,
|
||||
LAN887X_MX_CHIP_TOP_REG_CONTROL1_EVT_EN);
|
||||
|
||||
/* Initialize pin numbers specific to PEROUT */
|
||||
priv->clock->event_pin = 3;
|
||||
|
||||
priv->init_done = true;
|
||||
}
|
||||
|
||||
|
|
@ -2154,7 +2166,7 @@ static struct phy_driver microchip_t1_phy_driver[] = {
|
|||
|
||||
module_phy_driver(microchip_t1_phy_driver);
|
||||
|
||||
static const struct mdio_device_id __maybe_unused microchip_t1_tbl[] = {
|
||||
static struct mdio_device_id __maybe_unused microchip_t1_tbl[] = {
|
||||
{ PHY_ID_MATCH_MODEL(PHY_ID_LAN87XX) },
|
||||
{ PHY_ID_MATCH_MODEL(PHY_ID_LAN937X) },
|
||||
{ PHY_ID_MATCH_MODEL(PHY_ID_LAN887X) },
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user