mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
net: dsa: microchip: add periodic output support for the KSZ8463
KSZ8463 has several periodic outputs. The way it handles them differ from the other KSZ switches: - KSZ8463 has one set of registers for each of its 12 units while others switches share on set of configuration registers for all their units. - Maximum size of the KSZ8463 pulse width is smaller - KSZ8463 has 12 outputs while others only have 2 Add support for the KSZ8463 periodics outputs through a set of KSZ8463 specific functions. Signed-off-by: Bastien Curutchet (Schneider Electric) <bastien.curutchet@bootlin.com> Link: https://patch.msgid.link/20260925-ksz-perout-v4-11-d675bd21ce49@bootlin.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
a7cc3395d1
commit
64854d9aab
|
|
@ -2589,7 +2589,7 @@ static int ksz8463_setup(struct dsa_switch *ds)
|
|||
if (ret)
|
||||
goto free_girq;
|
||||
|
||||
ksz_ptp_set_caps(ds);
|
||||
ksz8463_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] = {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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,
|
||||
|
|
@ -242,6 +326,61 @@ static int ksz_ptp_compute_perout_cycle(struct ksz_device *dev,
|
|||
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)
|
||||
|
|
@ -853,6 +992,18 @@ static int ksz_ptp_restart_perout(struct ksz_device *dev)
|
|||
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)
|
||||
{
|
||||
|
|
@ -879,6 +1030,41 @@ static int __ksz_ptp_settime(struct ksz_device *dev,
|
|||
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)
|
||||
{
|
||||
|
|
@ -993,6 +1179,40 @@ static int __ksz_ptp_adjtime(struct ksz_device *dev, s64 delta)
|
|||
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);
|
||||
|
|
@ -1027,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)
|
||||
{
|
||||
|
|
@ -1103,6 +1343,27 @@ 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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -33,6 +33,7 @@ struct ksz_ptp_data {
|
|||
};
|
||||
|
||||
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);
|
||||
|
|
@ -66,6 +67,7 @@ struct ksz_ptp_data {
|
|||
};
|
||||
|
||||
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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user