mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
Merge branch 'net-dsa-microchip-add-periodic-output-support-for-the-ksz8463'
Bastien Curutchet says: ==================== net: dsa: microchip: add periodic output support for the KSZ8463 This series aims to add periodic output support for the KSZ8463. The way the KSZ8463 handles periodic output differs from that of the other KSZ switches. It therefore needs its own set of perout callbacks (configure, start, reset, enable, restart). Patch 1 saves the full request to be able to retrieve all its information in case of restart Patches 2 to 4 prepare the driver to accept different kinds of periodic output settings. Patches 5 to 10 extract a set of functions from the existing periodic output support that can be reused by the KSZ8463, avoiding code duplication as much as possible. Patch 11 adds periodic output support for the KSZ8463. ==================== Link: https://patch.msgid.link/20260925-ksz-perout-v4-0-d675bd21ce49@bootlin.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
aa65d37326
|
|
@ -2589,6 +2589,7 @@ static int ksz8463_setup(struct dsa_switch *ds)
|
|||
if (ret)
|
||||
goto free_girq;
|
||||
|
||||
ksz8463_ptp_set_caps(ds);
|
||||
ret = ksz_ptp_clock_register(ds);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "Failed to register PTP clock: %d\n",
|
||||
|
|
@ -2898,6 +2899,7 @@ static int ksz8_setup(struct dsa_switch *ds)
|
|||
}
|
||||
|
||||
if (dev->info->ptp_capable) {
|
||||
ksz_ptp_set_caps(ds);
|
||||
ret = ksz_ptp_clock_register(ds);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "Failed to register PTP clock: %d\n",
|
||||
|
|
|
|||
|
|
@ -1781,6 +1781,7 @@ static int ksz9477_setup(struct dsa_switch *ds)
|
|||
}
|
||||
|
||||
if (dev->info->ptp_capable) {
|
||||
ksz_ptp_set_caps(ds);
|
||||
ret = ksz_ptp_clock_register(ds);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "Failed to register PTP clock: %d\n",
|
||||
|
|
|
|||
|
|
@ -1175,6 +1175,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
|
|||
.supports_mii = {false, false, true},
|
||||
.supports_rmii = {false, false, true},
|
||||
.internal_phy = {true, true, false},
|
||||
.n_pins = 12,
|
||||
.n_per_out = 12,
|
||||
},
|
||||
|
||||
[KSZ8563] = {
|
||||
|
|
@ -1208,6 +1210,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
|
|||
.ptp_capable = true,
|
||||
.wr_table = &ksz8563_register_set,
|
||||
.rd_table = &ksz8563_register_set,
|
||||
.n_pins = 2,
|
||||
.n_per_out = 3,
|
||||
},
|
||||
|
||||
[KSZ8795] = {
|
||||
|
|
@ -1443,6 +1447,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
|
|||
.sgmii_port = 7,
|
||||
.wr_table = &ksz9477_register_set,
|
||||
.rd_table = &ksz9477_register_set,
|
||||
.n_pins = 2,
|
||||
.n_per_out = 3,
|
||||
},
|
||||
|
||||
[KSZ9896] = {
|
||||
|
|
@ -1608,6 +1614,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
|
|||
.internal_phy = {true, true, false},
|
||||
.gbit_capable = {true, true, true},
|
||||
.ptp_capable = true,
|
||||
.n_pins = 2,
|
||||
.n_per_out = 3,
|
||||
},
|
||||
|
||||
[KSZ8567] = {
|
||||
|
|
@ -1645,6 +1653,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
|
|||
.gbit_capable = {false, false, false, false, false,
|
||||
true, true},
|
||||
.ptp_capable = true,
|
||||
.n_pins = 2,
|
||||
.n_per_out = 3,
|
||||
},
|
||||
|
||||
[KSZ9567] = {
|
||||
|
|
@ -1679,6 +1689,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
|
|||
true, false, false},
|
||||
.gbit_capable = {true, true, true, true, true, true, true},
|
||||
.ptp_capable = true,
|
||||
.n_pins = 2,
|
||||
.n_per_out = 3,
|
||||
},
|
||||
|
||||
[LAN9370] = {
|
||||
|
|
@ -1710,6 +1722,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
|
|||
.supports_rgmii = {false, false, false, false, true},
|
||||
.internal_phy = {true, true, true, true, false},
|
||||
.ptp_capable = true,
|
||||
.n_pins = 2,
|
||||
.n_per_out = 3,
|
||||
},
|
||||
|
||||
[LAN9371] = {
|
||||
|
|
@ -1741,6 +1755,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
|
|||
.supports_rgmii = {false, false, false, false, true, true},
|
||||
.internal_phy = {true, true, true, true, false, false},
|
||||
.ptp_capable = true,
|
||||
.n_pins = 2,
|
||||
.n_per_out = 3,
|
||||
},
|
||||
|
||||
[LAN9372] = {
|
||||
|
|
@ -1776,6 +1792,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
|
|||
.internal_phy = {true, true, true, true,
|
||||
false, false, true, true},
|
||||
.ptp_capable = true,
|
||||
.n_pins = 2,
|
||||
.n_per_out = 3,
|
||||
},
|
||||
|
||||
[LAN9373] = {
|
||||
|
|
@ -1811,6 +1829,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
|
|||
.internal_phy = {true, true, true, false,
|
||||
false, false, true, true},
|
||||
.ptp_capable = true,
|
||||
.n_pins = 2,
|
||||
.n_per_out = 3,
|
||||
},
|
||||
|
||||
[LAN9374] = {
|
||||
|
|
@ -1846,6 +1866,8 @@ const struct ksz_chip_data ksz_switch_chips[] = {
|
|||
.internal_phy = {true, true, true, true,
|
||||
false, false, true, true},
|
||||
.ptp_capable = true,
|
||||
.n_pins = 2,
|
||||
.n_per_out = 3,
|
||||
},
|
||||
|
||||
[LAN9646] = {
|
||||
|
|
|
|||
|
|
@ -139,6 +139,8 @@ struct ksz_chip_data {
|
|||
u8 sgmii_port;
|
||||
const struct regmap_access_table *wr_table;
|
||||
const struct regmap_access_table *rd_table;
|
||||
const u8 n_pins;
|
||||
const u8 n_per_out;
|
||||
};
|
||||
|
||||
struct ksz_irq {
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
*/
|
||||
#define KSZ_MAX_DRIFT_CORR 6249999
|
||||
#define KSZ_MAX_PULSE_WIDTH 125000000LL
|
||||
#define KSZ8463_MAX_PULSE_WIDTH 500000LL
|
||||
|
||||
#define KSZ_PTP_INC_NS 40ULL /* HW clock is incremented every 40 ns (by 40) */
|
||||
#define KSZ_PTP_SUBNS_BITS 32
|
||||
|
|
@ -63,6 +64,17 @@ static int ksz_ptp_tou_gpio(struct ksz_device *dev)
|
|||
LED_SRC_PTP_GPIO_1 | LED_SRC_PTP_GPIO_2);
|
||||
}
|
||||
|
||||
static int ksz8463_ptp_tou_reset(struct ksz_device *dev, u8 unit)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ksz_rmw16(dev, KSZ8463_TOU_SW_RST, BIT(unit), BIT(unit));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ksz_rmw16(dev, KSZ8463_TOU_SW_RST, BIT(unit), 0);
|
||||
}
|
||||
|
||||
static int ksz_ptp_tou_reset(struct ksz_device *dev, u8 unit)
|
||||
{
|
||||
u32 data;
|
||||
|
|
@ -86,7 +98,7 @@ static int ksz_ptp_tou_reset(struct ksz_device *dev, u8 unit)
|
|||
0);
|
||||
}
|
||||
|
||||
static int ksz_ptp_tou_pulse_verify(u64 pulse_ns)
|
||||
static int ksz_ptp_tou_pulse_verify(u64 pulse_ns, u32 mask)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
|
|
@ -94,7 +106,7 @@ static int ksz_ptp_tou_pulse_verify(u64 pulse_ns)
|
|||
return -EINVAL;
|
||||
|
||||
data = (pulse_ns / 8);
|
||||
if (!FIELD_FIT(TRIG_PULSE_WIDTH_M, data))
|
||||
if ((mask & data) != data)
|
||||
return -ERANGE;
|
||||
|
||||
return 0;
|
||||
|
|
@ -120,6 +132,28 @@ static int ksz_ptp_tou_target_time_set(struct ksz_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ksz8463_ptp_tou_start(struct ksz_device *dev, u8 unit)
|
||||
{
|
||||
u16 data;
|
||||
int ret;
|
||||
|
||||
ret = ksz_rmw16(dev, KSZ8463_TOU_EN, BIT(unit), BIT(unit));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz_read16(dev, KSZ8463_TOU_ACTIVE, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!(data & BIT(unit))) {
|
||||
dev_err(dev->dev, "%s: Trigger unit%d error!\n", __func__,
|
||||
unit);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksz_ptp_tou_start(struct ksz_device *dev, u8 unit)
|
||||
{
|
||||
u32 data;
|
||||
|
|
@ -147,6 +181,56 @@ static int ksz_ptp_tou_start(struct ksz_device *dev, u8 unit)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ksz8463_ptp_configure_perout(struct ksz_device *dev,
|
||||
struct ptp_perout_request const *request,
|
||||
u32 cycle_width_ns, u32 pulse_width_ns,
|
||||
u8 index)
|
||||
{
|
||||
struct ptp_pin_desc *pin = &dev->ptp_data.pin_config[index];
|
||||
u16 cfg_base = KSZ8463_TRIG1_CFG + KSZ8463_TRIGN_CFG_SIZE * pin->chan;
|
||||
u16 data;
|
||||
int ret;
|
||||
|
||||
/* Hardware has only 32 bit for the second field */
|
||||
if ((request->start.sec & 0xffffffff) != request->start.sec)
|
||||
return -EINVAL;
|
||||
|
||||
data = KSZ8463_NOTIFY_BIT |
|
||||
FIELD_PREP(KSZ8463_PATTERN_M, TRIG_POS_PERIOD) |
|
||||
pin->index;
|
||||
ret = ksz_write16(dev, cfg_base + KSZ8463_PATTERN_OFF, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz_write32(dev, cfg_base + KSZ8463_CYCLE_WIDTH_OFF,
|
||||
cycle_width_ns);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Set cycle count 0 - Infinite */
|
||||
ret = ksz_write16(dev, cfg_base + KSZ8463_CYCLE_CNT_OFF, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* KSZ8463 uses a 8 ns unit value to compute the pulse width */
|
||||
data = (pulse_width_ns / 8);
|
||||
ret = ksz_write16(dev, cfg_base + KSZ8463_PULSE_WIDTH_OFF, data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz_write32(dev, cfg_base + KSZ8463_TARGET_NSEC,
|
||||
request->start.nsec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz_write32(dev, cfg_base + KSZ8463_TARGET_SEC,
|
||||
request->start.sec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksz_ptp_configure_perout(struct ksz_device *dev,
|
||||
u32 cycle_width_ns, u32 pulse_width_ns,
|
||||
struct timespec64 const *target_time,
|
||||
|
|
@ -183,17 +267,11 @@ static int ksz_ptp_configure_perout(struct ksz_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ksz_ptp_enable_perout(struct ksz_device *dev,
|
||||
struct ptp_perout_request const *request,
|
||||
int on)
|
||||
static int ksz_ptp_get_pin(struct ksz_device *dev,
|
||||
struct ptp_perout_request const *request)
|
||||
{
|
||||
struct ksz_ptp_data *ptp_data = &dev->ptp_data;
|
||||
u64 req_pulse_width_ns;
|
||||
u64 cycle_width_ns;
|
||||
u64 pulse_width_ns;
|
||||
int pin = 0;
|
||||
u32 data32;
|
||||
int ret;
|
||||
int pin;
|
||||
|
||||
if (request->flags & ~PTP_PEROUT_DUTY_CYCLE)
|
||||
return -EOPNOTSUPP;
|
||||
|
|
@ -206,6 +284,119 @@ static int ksz_ptp_enable_perout(struct ksz_device *dev,
|
|||
if (pin < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return pin;
|
||||
}
|
||||
|
||||
static int ksz_ptp_compute_perout_cycle(struct ksz_device *dev,
|
||||
struct ptp_perout_request const *request,
|
||||
u64 max_pulse_width,
|
||||
u64 *cycle_width_ns,
|
||||
u64 *pulse_width_ns)
|
||||
{
|
||||
struct timespec64 tmp;
|
||||
|
||||
if (request->period.sec < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!request->period.sec && !request->period.nsec)
|
||||
return -EINVAL;
|
||||
|
||||
tmp.tv_sec = request->period.sec;
|
||||
tmp.tv_nsec = request->period.nsec;
|
||||
*cycle_width_ns = timespec64_to_ns(&tmp);
|
||||
if ((*cycle_width_ns & TRIG_CYCLE_WIDTH_M) != *cycle_width_ns) {
|
||||
*cycle_width_ns = 0;
|
||||
*pulse_width_ns = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (request->flags & PTP_PEROUT_DUTY_CYCLE) {
|
||||
*pulse_width_ns = request->on.sec * NSEC_PER_SEC
|
||||
+ request->on.nsec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Use a duty cycle of 50%. Maximum pulse width supported by the
|
||||
* hardware is a little bit more than 125 ms.
|
||||
*/
|
||||
*pulse_width_ns = (request->period.sec * NSEC_PER_SEC +
|
||||
request->period.nsec) / 2;
|
||||
*pulse_width_ns = min_t(u64, *pulse_width_ns, max_pulse_width);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksz8463_ptp_enable_perout(struct ksz_device *dev,
|
||||
struct ptp_perout_request const *request,
|
||||
int on)
|
||||
{
|
||||
struct ksz_ptp_data *ptp_data = &dev->ptp_data;
|
||||
u64 cycle_width_ns;
|
||||
u64 pulse_width_ns;
|
||||
int pin;
|
||||
int ret;
|
||||
|
||||
pin = ksz_ptp_get_pin(dev, request);
|
||||
if (pin < 0)
|
||||
return pin;
|
||||
|
||||
ret = ksz8463_ptp_tou_reset(dev, request->index);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!on) {
|
||||
ptp_data->tou_mode = KSZ_PTP_TOU_IDLE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ksz_ptp_compute_perout_cycle(dev, request,
|
||||
KSZ8463_MAX_PULSE_WIDTH,
|
||||
&cycle_width_ns, &pulse_width_ns);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz_ptp_tou_pulse_verify(pulse_width_ns,
|
||||
KSZ8463_TRIG_PULSE_WIDTH_M);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz8463_ptp_configure_perout(dev, request,
|
||||
cycle_width_ns, pulse_width_ns,
|
||||
pin);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz_ptp_tou_gpio(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz8463_ptp_tou_start(dev, request->index);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(&ptp_data->perout_request, request,
|
||||
sizeof(struct ptp_perout_request));
|
||||
ptp_data->tou_mode = KSZ_PTP_TOU_PEROUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksz_ptp_enable_perout(struct ksz_device *dev,
|
||||
struct ptp_perout_request const *request,
|
||||
int on)
|
||||
{
|
||||
struct ksz_ptp_data *ptp_data = &dev->ptp_data;
|
||||
struct timespec64 tmp;
|
||||
u64 cycle_width_ns;
|
||||
u64 pulse_width_ns;
|
||||
int pin = 0;
|
||||
u32 data32;
|
||||
int ret;
|
||||
|
||||
pin = ksz_ptp_get_pin(dev, request);
|
||||
if (pin < 0)
|
||||
return pin;
|
||||
|
||||
data32 = FIELD_PREP(PTP_GPIO_INDEX, pin) |
|
||||
FIELD_PREP(PTP_TOU_INDEX, request->index);
|
||||
ret = ksz_rmw32(dev, REG_PTP_UNIT_INDEX__4,
|
||||
|
|
@ -222,36 +413,19 @@ static int ksz_ptp_enable_perout(struct ksz_device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
ptp_data->perout_target_time_first.tv_sec = request->start.sec;
|
||||
ptp_data->perout_target_time_first.tv_nsec = request->start.nsec;
|
||||
|
||||
ptp_data->perout_period.tv_sec = request->period.sec;
|
||||
ptp_data->perout_period.tv_nsec = request->period.nsec;
|
||||
|
||||
cycle_width_ns = timespec64_to_ns(&ptp_data->perout_period);
|
||||
if ((cycle_width_ns & TRIG_CYCLE_WIDTH_M) != cycle_width_ns)
|
||||
return -EINVAL;
|
||||
|
||||
if (request->flags & PTP_PEROUT_DUTY_CYCLE) {
|
||||
pulse_width_ns = request->on.sec * NSEC_PER_SEC +
|
||||
request->on.nsec;
|
||||
} else {
|
||||
/* Use a duty cycle of 50%. Maximum pulse width supported by the
|
||||
* hardware is a little bit more than 125 ms.
|
||||
*/
|
||||
req_pulse_width_ns = (request->period.sec * NSEC_PER_SEC +
|
||||
request->period.nsec) / 2;
|
||||
pulse_width_ns = min_t(u64, req_pulse_width_ns,
|
||||
KSZ_MAX_PULSE_WIDTH);
|
||||
}
|
||||
|
||||
ret = ksz_ptp_tou_pulse_verify(pulse_width_ns);
|
||||
ret = ksz_ptp_compute_perout_cycle(dev, request, KSZ_MAX_PULSE_WIDTH,
|
||||
&cycle_width_ns, &pulse_width_ns);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz_ptp_tou_pulse_verify(pulse_width_ns, TRIG_PULSE_WIDTH_M);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tmp.tv_sec = request->start.sec;
|
||||
tmp.tv_nsec = request->start.nsec;
|
||||
ret = ksz_ptp_configure_perout(dev, cycle_width_ns, pulse_width_ns,
|
||||
&ptp_data->perout_target_time_first,
|
||||
pin);
|
||||
&tmp, pin);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -263,6 +437,8 @@ static int ksz_ptp_enable_perout(struct ksz_device *dev,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(&ptp_data->perout_request, request,
|
||||
sizeof(struct ptp_perout_request));
|
||||
ptp_data->tou_mode = KSZ_PTP_TOU_PEROUT;
|
||||
|
||||
return 0;
|
||||
|
|
@ -756,13 +932,14 @@ static int ksz_ptp_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ksz_ptp_restart_perout(struct ksz_device *dev)
|
||||
static int ksz_ptp_prepare_restart_perout(struct ksz_device *dev,
|
||||
struct ptp_perout_request *request)
|
||||
{
|
||||
struct ksz_ptp_data *ptp_data = &dev->ptp_data;
|
||||
s64 now_ns, first_ns, period_ns, next_ns;
|
||||
struct ptp_perout_request request;
|
||||
struct timespec64 next;
|
||||
struct timespec64 now;
|
||||
struct timespec64 tmp;
|
||||
unsigned int count;
|
||||
int ret;
|
||||
|
||||
|
|
@ -773,10 +950,14 @@ static int ksz_ptp_restart_perout(struct ksz_device *dev)
|
|||
return ret;
|
||||
|
||||
now_ns = timespec64_to_ns(&now);
|
||||
first_ns = timespec64_to_ns(&ptp_data->perout_target_time_first);
|
||||
tmp.tv_sec = ptp_data->perout_request.start.sec;
|
||||
tmp.tv_nsec = ptp_data->perout_request.start.nsec;
|
||||
first_ns = timespec64_to_ns(&tmp);
|
||||
|
||||
/* Calculate next perout event based on start time and period */
|
||||
period_ns = timespec64_to_ns(&ptp_data->perout_period);
|
||||
tmp.tv_sec = ptp_data->perout_request.period.sec;
|
||||
tmp.tv_nsec = ptp_data->perout_request.period.nsec;
|
||||
period_ns = timespec64_to_ns(&tmp);
|
||||
|
||||
if (first_ns < now_ns) {
|
||||
count = div_u64(now_ns - first_ns, period_ns);
|
||||
|
|
@ -791,40 +972,109 @@ static int ksz_ptp_restart_perout(struct ksz_device *dev)
|
|||
|
||||
/* Restart periodic output signal */
|
||||
next = ns_to_timespec64(next_ns);
|
||||
request.start.sec = next.tv_sec;
|
||||
request.start.nsec = next.tv_nsec;
|
||||
request.period.sec = ptp_data->perout_period.tv_sec;
|
||||
request.period.nsec = ptp_data->perout_period.tv_nsec;
|
||||
request.index = 0;
|
||||
request.flags = 0;
|
||||
memcpy(request, &ptp_data->perout_request,
|
||||
sizeof(struct ptp_perout_request));
|
||||
request->start.sec = next.tv_sec;
|
||||
request->start.nsec = next.tv_nsec;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksz_ptp_restart_perout(struct ksz_device *dev)
|
||||
{
|
||||
struct ptp_perout_request request;
|
||||
int ret;
|
||||
|
||||
ret = ksz_ptp_prepare_restart_perout(dev, &request);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ksz_ptp_enable_perout(dev, &request, 1);
|
||||
}
|
||||
|
||||
static int ksz8463_ptp_restart_perout(struct ksz_device *dev)
|
||||
{
|
||||
struct ptp_perout_request request;
|
||||
int ret;
|
||||
|
||||
ret = ksz_ptp_prepare_restart_perout(dev, &request);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return ksz8463_ptp_enable_perout(dev, &request, 1);
|
||||
}
|
||||
|
||||
static int __ksz_ptp_settime(struct ksz_device *dev,
|
||||
const struct timespec64 *ts)
|
||||
{
|
||||
const u16 *regs = dev->info->regs;
|
||||
int ret;
|
||||
|
||||
/* Write to shadow registers and Load PTP clock */
|
||||
ret = ksz_write16(dev, regs[PTP_RTC_SUB_NANOSEC], PTP_RTC_0NS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz_write32(dev, regs[PTP_RTC_NANOSEC], ts->tv_nsec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz_write32(dev, regs[PTP_RTC_SEC], ts->tv_sec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = ksz_rmw16(dev, regs[PTP_CLK_CTRL], PTP_LOAD_TIME, PTP_LOAD_TIME);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksz8463_ptp_settime(struct ptp_clock_info *ptp,
|
||||
const struct timespec64 *ts)
|
||||
{
|
||||
struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
|
||||
struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ptp_data->lock);
|
||||
|
||||
ret = __ksz_ptp_settime(dev, ts);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
switch (ptp_data->tou_mode) {
|
||||
case KSZ_PTP_TOU_IDLE:
|
||||
break;
|
||||
|
||||
case KSZ_PTP_TOU_PEROUT:
|
||||
ret = ksz8463_ptp_restart_perout(dev);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
spin_lock_bh(&ptp_data->clock_lock);
|
||||
ptp_data->clock_time = *ts;
|
||||
spin_unlock_bh(&ptp_data->clock_lock);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&ptp_data->lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ksz_ptp_settime(struct ptp_clock_info *ptp,
|
||||
const struct timespec64 *ts)
|
||||
{
|
||||
struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
|
||||
struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
|
||||
const u16 *regs = dev->info->regs;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ptp_data->lock);
|
||||
|
||||
/* Write to shadow registers and Load PTP clock */
|
||||
ret = ksz_write16(dev, regs[PTP_RTC_SUB_NANOSEC], PTP_RTC_0NS);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = ksz_write32(dev, regs[PTP_RTC_NANOSEC], ts->tv_nsec);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = ksz_write32(dev, regs[PTP_RTC_SEC], ts->tv_sec);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
ret = ksz_rmw16(dev, regs[PTP_CLK_CTRL], PTP_LOAD_TIME, PTP_LOAD_TIME);
|
||||
ret = __ksz_ptp_settime(dev, ts);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
|
|
@ -890,18 +1140,13 @@ static int ksz_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||||
static int __ksz_ptp_adjtime(struct ksz_device *dev, s64 delta)
|
||||
{
|
||||
struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
|
||||
struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
|
||||
struct timespec64 delta64 = ns_to_timespec64(delta);
|
||||
const u16 *regs = dev->info->regs;
|
||||
s32 sec, nsec;
|
||||
u16 data16;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ptp_data->lock);
|
||||
|
||||
/* do not use ns_to_timespec64(),
|
||||
* both sec and nsec are subtracted by hw
|
||||
*/
|
||||
|
|
@ -909,15 +1154,15 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
|||
|
||||
ret = ksz_write32(dev, regs[PTP_RTC_NANOSEC], abs(nsec));
|
||||
if (ret)
|
||||
goto unlock;
|
||||
return ret;
|
||||
|
||||
ret = ksz_write32(dev, regs[PTP_RTC_SEC], abs(sec));
|
||||
if (ret)
|
||||
goto unlock;
|
||||
return ret;
|
||||
|
||||
ret = ksz_read16(dev, regs[PTP_CLK_CTRL], &data16);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
return ret;
|
||||
|
||||
data16 |= PTP_STEP_ADJ;
|
||||
|
||||
|
|
@ -928,6 +1173,56 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
|||
data16 |= PTP_STEP_DIR;
|
||||
|
||||
ret = ksz_write16(dev, regs[PTP_CLK_CTRL], data16);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ksz8463_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||||
{
|
||||
struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
|
||||
struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
|
||||
struct timespec64 delta64 = ns_to_timespec64(delta);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ptp_data->lock);
|
||||
|
||||
ret = __ksz_ptp_adjtime(dev, delta);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
switch (ptp_data->tou_mode) {
|
||||
case KSZ_PTP_TOU_IDLE:
|
||||
break;
|
||||
|
||||
case KSZ_PTP_TOU_PEROUT:
|
||||
ret = ksz8463_ptp_restart_perout(dev);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
spin_lock_bh(&ptp_data->clock_lock);
|
||||
ptp_data->clock_time = timespec64_add(ptp_data->clock_time, delta64);
|
||||
spin_unlock_bh(&ptp_data->clock_lock);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&ptp_data->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
||||
{
|
||||
struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
|
||||
struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
|
||||
struct timespec64 delta64 = ns_to_timespec64(delta);
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ptp_data->lock);
|
||||
|
||||
ret = __ksz_ptp_adjtime(dev, delta);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
|
|
@ -952,6 +1247,26 @@ static int ksz_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ksz8463_ptp_enable(struct ptp_clock_info *ptp,
|
||||
struct ptp_clock_request *req, int on)
|
||||
{
|
||||
struct ksz_ptp_data *ptp_data = ptp_caps_to_data(ptp);
|
||||
struct ksz_device *dev = ptp_data_to_ksz_dev(ptp_data);
|
||||
int ret;
|
||||
|
||||
switch (req->type) {
|
||||
case PTP_CLK_REQ_PEROUT:
|
||||
mutex_lock(&ptp_data->lock);
|
||||
ret = ksz8463_ptp_enable_perout(dev, &req->perout, on);
|
||||
mutex_unlock(&ptp_data->lock);
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ksz_ptp_enable(struct ptp_clock_info *ptp,
|
||||
struct ptp_clock_request *req, int on)
|
||||
{
|
||||
|
|
@ -1028,6 +1343,48 @@ static int ksz_ptp_start_clock(struct ksz_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void ksz8463_ptp_set_caps(struct dsa_switch *ds)
|
||||
{
|
||||
struct ksz_device *dev = ds->priv;
|
||||
struct ksz_ptp_data *ptp_data;
|
||||
|
||||
ptp_data = &dev->ptp_data;
|
||||
|
||||
ptp_data->caps.owner = THIS_MODULE;
|
||||
snprintf(ptp_data->caps.name, 16, "Microchip Clock");
|
||||
ptp_data->caps.max_adj = KSZ_MAX_DRIFT_CORR;
|
||||
ptp_data->caps.gettime64 = ksz_ptp_gettime;
|
||||
ptp_data->caps.settime64 = ksz8463_ptp_settime;
|
||||
ptp_data->caps.adjfine = ksz_ptp_adjfine;
|
||||
ptp_data->caps.adjtime = ksz8463_ptp_adjtime;
|
||||
ptp_data->caps.do_aux_work = ksz_ptp_do_aux_work;
|
||||
ptp_data->caps.enable = ksz8463_ptp_enable;
|
||||
ptp_data->caps.verify = ksz_ptp_verify_pin;
|
||||
ptp_data->caps.n_pins = dev->info->n_pins;
|
||||
ptp_data->caps.n_per_out = dev->info->n_per_out;
|
||||
}
|
||||
|
||||
void ksz_ptp_set_caps(struct dsa_switch *ds)
|
||||
{
|
||||
struct ksz_device *dev = ds->priv;
|
||||
struct ksz_ptp_data *ptp_data;
|
||||
|
||||
ptp_data = &dev->ptp_data;
|
||||
|
||||
ptp_data->caps.owner = THIS_MODULE;
|
||||
snprintf(ptp_data->caps.name, 16, "Microchip Clock");
|
||||
ptp_data->caps.max_adj = KSZ_MAX_DRIFT_CORR;
|
||||
ptp_data->caps.gettime64 = ksz_ptp_gettime;
|
||||
ptp_data->caps.settime64 = ksz_ptp_settime;
|
||||
ptp_data->caps.adjfine = ksz_ptp_adjfine;
|
||||
ptp_data->caps.adjtime = ksz_ptp_adjtime;
|
||||
ptp_data->caps.do_aux_work = ksz_ptp_do_aux_work;
|
||||
ptp_data->caps.enable = ksz_ptp_enable;
|
||||
ptp_data->caps.verify = ksz_ptp_verify_pin;
|
||||
ptp_data->caps.n_pins = dev->info->n_pins;
|
||||
ptp_data->caps.n_per_out = dev->info->n_per_out;
|
||||
}
|
||||
|
||||
int ksz_ptp_clock_register(struct dsa_switch *ds)
|
||||
{
|
||||
struct ksz_device *dev = ds->priv;
|
||||
|
|
@ -1040,24 +1397,11 @@ int ksz_ptp_clock_register(struct dsa_switch *ds)
|
|||
mutex_init(&ptp_data->lock);
|
||||
spin_lock_init(&ptp_data->clock_lock);
|
||||
|
||||
ptp_data->caps.owner = THIS_MODULE;
|
||||
snprintf(ptp_data->caps.name, 16, "Microchip Clock");
|
||||
ptp_data->caps.max_adj = KSZ_MAX_DRIFT_CORR;
|
||||
ptp_data->caps.gettime64 = ksz_ptp_gettime;
|
||||
ptp_data->caps.settime64 = ksz_ptp_settime;
|
||||
ptp_data->caps.adjfine = ksz_ptp_adjfine;
|
||||
ptp_data->caps.adjtime = ksz_ptp_adjtime;
|
||||
ptp_data->caps.do_aux_work = ksz_ptp_do_aux_work;
|
||||
ptp_data->caps.enable = ksz_ptp_enable;
|
||||
ptp_data->caps.verify = ksz_ptp_verify_pin;
|
||||
ptp_data->caps.n_pins = KSZ_PTP_N_GPIO;
|
||||
ptp_data->caps.n_per_out = 3;
|
||||
|
||||
ret = ksz_ptp_start_clock(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < KSZ_PTP_N_GPIO; i++) {
|
||||
for (i = 0; i < dev->info->n_pins; i++) {
|
||||
struct ptp_pin_desc *ptp_pin = &ptp_data->pin_config[i];
|
||||
|
||||
snprintf(ptp_pin->name,
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
#include <linux/ptp_clock_kernel.h>
|
||||
|
||||
#define KSZ_PTP_N_GPIO 2
|
||||
#define KSZ_PTP_N_GPIO 12
|
||||
|
||||
enum ksz_ptp_tou_mode {
|
||||
KSZ_PTP_TOU_IDLE,
|
||||
|
|
@ -29,10 +29,11 @@ struct ksz_ptp_data {
|
|||
spinlock_t clock_lock;
|
||||
struct timespec64 clock_time;
|
||||
enum ksz_ptp_tou_mode tou_mode;
|
||||
struct timespec64 perout_target_time_first; /* start of first pulse */
|
||||
struct timespec64 perout_period;
|
||||
struct ptp_perout_request perout_request;
|
||||
};
|
||||
|
||||
void ksz_ptp_set_caps(struct dsa_switch *ds);
|
||||
void ksz8463_ptp_set_caps(struct dsa_switch *ds);
|
||||
int ksz_ptp_clock_register(struct dsa_switch *ds);
|
||||
|
||||
void ksz_ptp_clock_unregister(struct dsa_switch *ds);
|
||||
|
|
@ -65,6 +66,8 @@ struct ksz_ptp_data {
|
|||
struct mutex lock;
|
||||
};
|
||||
|
||||
static inline void ksz_ptp_set_caps(struct dsa_switch *ds) { }
|
||||
static inline void ksz8463_ptp_set_caps(struct dsa_switch *ds) { }
|
||||
static inline int ksz_ptp_clock_register(struct dsa_switch *ds)
|
||||
{
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -51,6 +51,22 @@
|
|||
|
||||
#define REG_PTP_UNIT_INDEX__4 0x0520
|
||||
|
||||
#define KSZ8463_TOU_ACTIVE 0x202
|
||||
#define KSZ8463_TOU_EN 0x206
|
||||
#define KSZ8463_TOU_SW_RST 0x208
|
||||
|
||||
#define KSZ8463_TRIGN_CFG_SIZE 0x20
|
||||
#define KSZ8463_TRIG1_CFG 0x220
|
||||
#define KSZ8463_TARGET_NSEC 0x000
|
||||
#define KSZ8463_TARGET_SEC 0x004
|
||||
#define KSZ8463_PATTERN_OFF 0x008
|
||||
#define KSZ8463_NOTIFY_BIT BIT(8)
|
||||
#define KSZ8463_PATTERN_M GENMASK(6, 4)
|
||||
#define KSZ8463_PULSE_WIDTH_OFF 0x00A
|
||||
#define KSZ8463_TRIG_PULSE_WIDTH_M GENMASK(15, 0)
|
||||
#define KSZ8463_CYCLE_WIDTH_OFF 0x00C
|
||||
#define KSZ8463_CYCLE_CNT_OFF 0x010
|
||||
|
||||
#define PTP_GPIO_INDEX GENMASK(19, 16)
|
||||
#define PTP_TSI_INDEX BIT(8)
|
||||
#define PTP_TOU_INDEX GENMASK(1, 0)
|
||||
|
|
|
|||
|
|
@ -867,6 +867,7 @@ static int lan937x_setup(struct dsa_switch *ds)
|
|||
}
|
||||
}
|
||||
|
||||
ksz_ptp_set_caps(ds);
|
||||
ret = ksz_ptp_clock_register(ds);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "Failed to register PTP clock: %d\n",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user