mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-16 02:44:26 +08:00
Merge branch 'PTP_CLK-pin-configuration-for-SJA1105-DSA-driver'
Vladimir Oltean says: ==================== PTP_CLK pin configuration for SJA1105 DSA driver This series adds support for the PTP_CLK pin on SJA1105 to be configured via the PTP subsystem, in the "periodic output" and "external timestamp input" modes. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
9970de8b01
@ -38,10 +38,13 @@ struct sja1105_regs {
|
|||||||
u64 config;
|
u64 config;
|
||||||
u64 sgmii;
|
u64 sgmii;
|
||||||
u64 rmii_pll1;
|
u64 rmii_pll1;
|
||||||
|
u64 ptppinst;
|
||||||
|
u64 ptppindur;
|
||||||
u64 ptp_control;
|
u64 ptp_control;
|
||||||
u64 ptpclkval;
|
u64 ptpclkval;
|
||||||
u64 ptpclkrate;
|
u64 ptpclkrate;
|
||||||
u64 ptpclkcorp;
|
u64 ptpclkcorp;
|
||||||
|
u64 ptpsyncts;
|
||||||
u64 ptpschtm;
|
u64 ptpschtm;
|
||||||
u64 ptpegr_ts[SJA1105_NUM_PORTS];
|
u64 ptpegr_ts[SJA1105_NUM_PORTS];
|
||||||
u64 pad_mii_tx[SJA1105_NUM_PORTS];
|
u64 pad_mii_tx[SJA1105_NUM_PORTS];
|
||||||
@ -214,5 +217,7 @@ size_t sja1105_vlan_lookup_entry_packing(void *buf, void *entry_ptr,
|
|||||||
enum packing_op op);
|
enum packing_op op);
|
||||||
size_t sja1105pqrs_mac_config_entry_packing(void *buf, void *entry_ptr,
|
size_t sja1105pqrs_mac_config_entry_packing(void *buf, void *entry_ptr,
|
||||||
enum packing_op op);
|
enum packing_op op);
|
||||||
|
size_t sja1105pqrs_avb_params_entry_packing(void *buf, void *entry_ptr,
|
||||||
|
enum packing_op op);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -124,6 +124,9 @@
|
|||||||
#define SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD \
|
#define SJA1105ET_SIZE_GENERAL_PARAMS_DYN_CMD \
|
||||||
SJA1105_SIZE_DYN_CMD
|
SJA1105_SIZE_DYN_CMD
|
||||||
|
|
||||||
|
#define SJA1105PQRS_SIZE_AVB_PARAMS_DYN_CMD \
|
||||||
|
(SJA1105_SIZE_DYN_CMD + SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY)
|
||||||
|
|
||||||
#define SJA1105_MAX_DYN_CMD_SIZE \
|
#define SJA1105_MAX_DYN_CMD_SIZE \
|
||||||
SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD
|
SJA1105PQRS_SIZE_MAC_CONFIG_DYN_CMD
|
||||||
|
|
||||||
@ -481,6 +484,18 @@ sja1105et_general_params_entry_packing(void *buf, void *entry_ptr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sja1105pqrs_avb_params_cmd_packing(void *buf, struct sja1105_dyn_cmd *cmd,
|
||||||
|
enum packing_op op)
|
||||||
|
{
|
||||||
|
u8 *p = buf + SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY;
|
||||||
|
const int size = SJA1105_SIZE_DYN_CMD;
|
||||||
|
|
||||||
|
sja1105_packing(p, &cmd->valid, 31, 31, size, op);
|
||||||
|
sja1105_packing(p, &cmd->errors, 30, 30, size, op);
|
||||||
|
sja1105_packing(p, &cmd->rdwrset, 29, 29, size, op);
|
||||||
|
}
|
||||||
|
|
||||||
#define OP_READ BIT(0)
|
#define OP_READ BIT(0)
|
||||||
#define OP_WRITE BIT(1)
|
#define OP_WRITE BIT(1)
|
||||||
#define OP_DEL BIT(2)
|
#define OP_DEL BIT(2)
|
||||||
@ -610,7 +625,14 @@ struct sja1105_dynamic_table_ops sja1105pqrs_dyn_ops[BLK_IDX_MAX_DYN] = {
|
|||||||
.addr = 0x38,
|
.addr = 0x38,
|
||||||
},
|
},
|
||||||
[BLK_IDX_L2_FORWARDING_PARAMS] = {0},
|
[BLK_IDX_L2_FORWARDING_PARAMS] = {0},
|
||||||
[BLK_IDX_AVB_PARAMS] = {0},
|
[BLK_IDX_AVB_PARAMS] = {
|
||||||
|
.entry_packing = sja1105pqrs_avb_params_entry_packing,
|
||||||
|
.cmd_packing = sja1105pqrs_avb_params_cmd_packing,
|
||||||
|
.max_entry_count = SJA1105_MAX_AVB_PARAMS_COUNT,
|
||||||
|
.access = (OP_READ | OP_WRITE),
|
||||||
|
.packed_size = SJA1105PQRS_SIZE_AVB_PARAMS_DYN_CMD,
|
||||||
|
.addr = 0x8003,
|
||||||
|
},
|
||||||
[BLK_IDX_GENERAL_PARAMS] = {
|
[BLK_IDX_GENERAL_PARAMS] = {
|
||||||
.entry_packing = sja1105et_general_params_entry_packing,
|
.entry_packing = sja1105et_general_params_entry_packing,
|
||||||
.cmd_packing = sja1105et_general_params_cmd_packing,
|
.cmd_packing = sja1105et_general_params_cmd_packing,
|
||||||
|
@ -479,6 +479,43 @@ static int sja1105_init_general_params(struct sja1105_private *priv)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sja1105_init_avb_params(struct sja1105_private *priv)
|
||||||
|
{
|
||||||
|
struct sja1105_avb_params_entry *avb;
|
||||||
|
struct sja1105_table *table;
|
||||||
|
|
||||||
|
table = &priv->static_config.tables[BLK_IDX_AVB_PARAMS];
|
||||||
|
|
||||||
|
/* Discard previous AVB Parameters Table */
|
||||||
|
if (table->entry_count) {
|
||||||
|
kfree(table->entries);
|
||||||
|
table->entry_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table->entries = kcalloc(SJA1105_MAX_AVB_PARAMS_COUNT,
|
||||||
|
table->ops->unpacked_entry_size, GFP_KERNEL);
|
||||||
|
if (!table->entries)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
table->entry_count = SJA1105_MAX_AVB_PARAMS_COUNT;
|
||||||
|
|
||||||
|
avb = table->entries;
|
||||||
|
|
||||||
|
/* Configure the MAC addresses for meta frames */
|
||||||
|
avb->destmeta = SJA1105_META_DMAC;
|
||||||
|
avb->srcmeta = SJA1105_META_SMAC;
|
||||||
|
/* On P/Q/R/S, configure the direction of the PTP_CLK pin as input by
|
||||||
|
* default. This is because there might be boards with a hardware
|
||||||
|
* layout where enabling the pin as output might cause an electrical
|
||||||
|
* clash. On E/T the pin is always an output, which the board designers
|
||||||
|
* probably already knew, so even if there are going to be electrical
|
||||||
|
* issues, there's nothing we can do.
|
||||||
|
*/
|
||||||
|
avb->cas_master = false;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000)
|
#define SJA1105_RATE_MBPS(speed) (((speed) * 64000) / 1000)
|
||||||
|
|
||||||
static void sja1105_setup_policer(struct sja1105_l2_policing_entry *policing,
|
static void sja1105_setup_policer(struct sja1105_l2_policing_entry *policing,
|
||||||
@ -567,6 +604,9 @@ static int sja1105_static_config_load(struct sja1105_private *priv,
|
|||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
rc = sja1105_init_general_params(priv);
|
rc = sja1105_init_general_params(priv);
|
||||||
|
if (rc < 0)
|
||||||
|
return rc;
|
||||||
|
rc = sja1105_init_avb_params(priv);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
@ -14,6 +14,17 @@
|
|||||||
#define SJA1105_MAX_ADJ_PPB 32000000
|
#define SJA1105_MAX_ADJ_PPB 32000000
|
||||||
#define SJA1105_SIZE_PTP_CMD 4
|
#define SJA1105_SIZE_PTP_CMD 4
|
||||||
|
|
||||||
|
/* PTPSYNCTS has no interrupt or update mechanism, because the intended
|
||||||
|
* hardware use case is for the timestamp to be collected synchronously,
|
||||||
|
* immediately after the CAS_MASTER SJA1105 switch has triggered a CASSYNC
|
||||||
|
* pulse on the PTP_CLK pin. When used as a generic extts source, it needs
|
||||||
|
* polling and a comparison with the old value. The polling interval is just
|
||||||
|
* the Nyquist rate of a canonical PPS input (e.g. from a GPS module).
|
||||||
|
* Anything of higher frequency than 1 Hz will be lost, since there is no
|
||||||
|
* timestamp FIFO.
|
||||||
|
*/
|
||||||
|
#define SJA1105_EXTTS_INTERVAL (HZ / 2)
|
||||||
|
|
||||||
/* This range is actually +/- SJA1105_MAX_ADJ_PPB
|
/* This range is actually +/- SJA1105_MAX_ADJ_PPB
|
||||||
* divided by 1000 (ppb -> ppm) and with a 16-bit
|
* divided by 1000 (ppb -> ppm) and with a 16-bit
|
||||||
* "fractional" part (actually fixed point).
|
* "fractional" part (actually fixed point).
|
||||||
@ -39,44 +50,13 @@ enum sja1105_ptp_clk_mode {
|
|||||||
PTP_SET_MODE = 0,
|
PTP_SET_MODE = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define extts_to_data(d) \
|
||||||
|
container_of((d), struct sja1105_ptp_data, extts_work)
|
||||||
#define ptp_caps_to_data(d) \
|
#define ptp_caps_to_data(d) \
|
||||||
container_of((d), struct sja1105_ptp_data, caps)
|
container_of((d), struct sja1105_ptp_data, caps)
|
||||||
#define ptp_data_to_sja1105(d) \
|
#define ptp_data_to_sja1105(d) \
|
||||||
container_of((d), struct sja1105_private, ptp_data)
|
container_of((d), struct sja1105_private, ptp_data)
|
||||||
|
|
||||||
static int sja1105_init_avb_params(struct sja1105_private *priv,
|
|
||||||
bool on)
|
|
||||||
{
|
|
||||||
struct sja1105_avb_params_entry *avb;
|
|
||||||
struct sja1105_table *table;
|
|
||||||
|
|
||||||
table = &priv->static_config.tables[BLK_IDX_AVB_PARAMS];
|
|
||||||
|
|
||||||
/* Discard previous AVB Parameters Table */
|
|
||||||
if (table->entry_count) {
|
|
||||||
kfree(table->entries);
|
|
||||||
table->entry_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Configure the reception of meta frames only if requested */
|
|
||||||
if (!on)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
table->entries = kcalloc(SJA1105_MAX_AVB_PARAMS_COUNT,
|
|
||||||
table->ops->unpacked_entry_size, GFP_KERNEL);
|
|
||||||
if (!table->entries)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
table->entry_count = SJA1105_MAX_AVB_PARAMS_COUNT;
|
|
||||||
|
|
||||||
avb = table->entries;
|
|
||||||
|
|
||||||
avb->destmeta = SJA1105_META_DMAC;
|
|
||||||
avb->srcmeta = SJA1105_META_SMAC;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Must be called only with priv->tagger_data.state bit
|
/* Must be called only with priv->tagger_data.state bit
|
||||||
* SJA1105_HWTS_RX_EN cleared
|
* SJA1105_HWTS_RX_EN cleared
|
||||||
*/
|
*/
|
||||||
@ -86,17 +66,12 @@ static int sja1105_change_rxtstamping(struct sja1105_private *priv,
|
|||||||
struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
|
struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
|
||||||
struct sja1105_general_params_entry *general_params;
|
struct sja1105_general_params_entry *general_params;
|
||||||
struct sja1105_table *table;
|
struct sja1105_table *table;
|
||||||
int rc;
|
|
||||||
|
|
||||||
table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS];
|
table = &priv->static_config.tables[BLK_IDX_GENERAL_PARAMS];
|
||||||
general_params = table->entries;
|
general_params = table->entries;
|
||||||
general_params->send_meta1 = on;
|
general_params->send_meta1 = on;
|
||||||
general_params->send_meta0 = on;
|
general_params->send_meta0 = on;
|
||||||
|
|
||||||
rc = sja1105_init_avb_params(priv, on);
|
|
||||||
if (rc < 0)
|
|
||||||
return rc;
|
|
||||||
|
|
||||||
/* Initialize the meta state machine to a known state */
|
/* Initialize the meta state machine to a known state */
|
||||||
if (priv->tagger_data.stampable_skb) {
|
if (priv->tagger_data.stampable_skb) {
|
||||||
kfree_skb(priv->tagger_data.stampable_skb);
|
kfree_skb(priv->tagger_data.stampable_skb);
|
||||||
@ -206,6 +181,8 @@ void sja1105et_ptp_cmd_packing(u8 *buf, struct sja1105_ptp_cmd *cmd,
|
|||||||
sja1105_packing(buf, &valid, 31, 31, size, op);
|
sja1105_packing(buf, &valid, 31, 31, size, op);
|
||||||
sja1105_packing(buf, &cmd->ptpstrtsch, 30, 30, size, op);
|
sja1105_packing(buf, &cmd->ptpstrtsch, 30, 30, size, op);
|
||||||
sja1105_packing(buf, &cmd->ptpstopsch, 29, 29, size, op);
|
sja1105_packing(buf, &cmd->ptpstopsch, 29, 29, size, op);
|
||||||
|
sja1105_packing(buf, &cmd->startptpcp, 28, 28, size, op);
|
||||||
|
sja1105_packing(buf, &cmd->stopptpcp, 27, 27, size, op);
|
||||||
sja1105_packing(buf, &cmd->resptp, 2, 2, size, op);
|
sja1105_packing(buf, &cmd->resptp, 2, 2, size, op);
|
||||||
sja1105_packing(buf, &cmd->corrclk4ts, 1, 1, size, op);
|
sja1105_packing(buf, &cmd->corrclk4ts, 1, 1, size, op);
|
||||||
sja1105_packing(buf, &cmd->ptpclkadd, 0, 0, size, op);
|
sja1105_packing(buf, &cmd->ptpclkadd, 0, 0, size, op);
|
||||||
@ -221,6 +198,8 @@ void sja1105pqrs_ptp_cmd_packing(u8 *buf, struct sja1105_ptp_cmd *cmd,
|
|||||||
sja1105_packing(buf, &valid, 31, 31, size, op);
|
sja1105_packing(buf, &valid, 31, 31, size, op);
|
||||||
sja1105_packing(buf, &cmd->ptpstrtsch, 30, 30, size, op);
|
sja1105_packing(buf, &cmd->ptpstrtsch, 30, 30, size, op);
|
||||||
sja1105_packing(buf, &cmd->ptpstopsch, 29, 29, size, op);
|
sja1105_packing(buf, &cmd->ptpstopsch, 29, 29, size, op);
|
||||||
|
sja1105_packing(buf, &cmd->startptpcp, 28, 28, size, op);
|
||||||
|
sja1105_packing(buf, &cmd->stopptpcp, 27, 27, size, op);
|
||||||
sja1105_packing(buf, &cmd->resptp, 3, 3, size, op);
|
sja1105_packing(buf, &cmd->resptp, 3, 3, size, op);
|
||||||
sja1105_packing(buf, &cmd->corrclk4ts, 2, 2, size, op);
|
sja1105_packing(buf, &cmd->corrclk4ts, 2, 2, size, op);
|
||||||
sja1105_packing(buf, &cmd->ptpclkadd, 0, 0, size, op);
|
sja1105_packing(buf, &cmd->ptpclkadd, 0, 0, size, op);
|
||||||
@ -615,6 +594,227 @@ static int sja1105_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sja1105_ptp_extts_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct delayed_work *dw = to_delayed_work(work);
|
||||||
|
struct sja1105_ptp_data *ptp_data = extts_to_data(dw);
|
||||||
|
struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
|
||||||
|
const struct sja1105_regs *regs = priv->info->regs;
|
||||||
|
struct ptp_clock_event event;
|
||||||
|
u64 ptpsyncts = 0;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
mutex_lock(&ptp_data->lock);
|
||||||
|
|
||||||
|
rc = sja1105_xfer_u64(priv, SPI_READ, regs->ptpsyncts, &ptpsyncts,
|
||||||
|
NULL);
|
||||||
|
if (rc < 0)
|
||||||
|
dev_err_ratelimited(priv->ds->dev,
|
||||||
|
"Failed to read PTPSYNCTS: %d\n", rc);
|
||||||
|
|
||||||
|
if (ptpsyncts && ptp_data->ptpsyncts != ptpsyncts) {
|
||||||
|
event.index = 0;
|
||||||
|
event.type = PTP_CLOCK_EXTTS;
|
||||||
|
event.timestamp = ns_to_ktime(sja1105_ticks_to_ns(ptpsyncts));
|
||||||
|
ptp_clock_event(ptp_data->clock, &event);
|
||||||
|
|
||||||
|
ptp_data->ptpsyncts = ptpsyncts;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&ptp_data->lock);
|
||||||
|
|
||||||
|
schedule_delayed_work(&ptp_data->extts_work, SJA1105_EXTTS_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sja1105_change_ptp_clk_pin_func(struct sja1105_private *priv,
|
||||||
|
enum ptp_pin_function func)
|
||||||
|
{
|
||||||
|
struct sja1105_avb_params_entry *avb;
|
||||||
|
enum ptp_pin_function old_func;
|
||||||
|
|
||||||
|
avb = priv->static_config.tables[BLK_IDX_AVB_PARAMS].entries;
|
||||||
|
|
||||||
|
if (priv->info->device_id == SJA1105E_DEVICE_ID ||
|
||||||
|
priv->info->device_id == SJA1105T_DEVICE_ID ||
|
||||||
|
avb->cas_master)
|
||||||
|
old_func = PTP_PF_PEROUT;
|
||||||
|
else
|
||||||
|
old_func = PTP_PF_EXTTS;
|
||||||
|
|
||||||
|
if (func == old_func)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
avb->cas_master = (func == PTP_PF_PEROUT);
|
||||||
|
|
||||||
|
return sja1105_dynamic_config_write(priv, BLK_IDX_AVB_PARAMS, 0, avb,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The PTP_CLK pin may be configured to toggle with a 50% duty cycle and a
|
||||||
|
* frequency f:
|
||||||
|
*
|
||||||
|
* NSEC_PER_SEC
|
||||||
|
* f = ----------------------
|
||||||
|
* (PTPPINDUR * 8 ns) * 2
|
||||||
|
*/
|
||||||
|
static int sja1105_per_out_enable(struct sja1105_private *priv,
|
||||||
|
struct ptp_perout_request *perout,
|
||||||
|
bool on)
|
||||||
|
{
|
||||||
|
struct sja1105_ptp_data *ptp_data = &priv->ptp_data;
|
||||||
|
const struct sja1105_regs *regs = priv->info->regs;
|
||||||
|
struct sja1105_ptp_cmd cmd = ptp_data->cmd;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* We only support one channel */
|
||||||
|
if (perout->index != 0)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
/* Reject requests with unsupported flags */
|
||||||
|
if (perout->flags)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
mutex_lock(&ptp_data->lock);
|
||||||
|
|
||||||
|
rc = sja1105_change_ptp_clk_pin_func(priv, PTP_PF_PEROUT);
|
||||||
|
if (rc)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
struct timespec64 pin_duration_ts = {
|
||||||
|
.tv_sec = perout->period.sec,
|
||||||
|
.tv_nsec = perout->period.nsec,
|
||||||
|
};
|
||||||
|
struct timespec64 pin_start_ts = {
|
||||||
|
.tv_sec = perout->start.sec,
|
||||||
|
.tv_nsec = perout->start.nsec,
|
||||||
|
};
|
||||||
|
u64 pin_duration = timespec64_to_ns(&pin_duration_ts);
|
||||||
|
u64 pin_start = timespec64_to_ns(&pin_start_ts);
|
||||||
|
u32 pin_duration32;
|
||||||
|
u64 now;
|
||||||
|
|
||||||
|
/* ptppindur: 32 bit register which holds the interval between
|
||||||
|
* 2 edges on PTP_CLK. So check for truncation which happens
|
||||||
|
* at periods larger than around 68.7 seconds.
|
||||||
|
*/
|
||||||
|
pin_duration = ns_to_sja1105_ticks(pin_duration / 2);
|
||||||
|
if (pin_duration > U32_MAX) {
|
||||||
|
rc = -ERANGE;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
pin_duration32 = pin_duration;
|
||||||
|
|
||||||
|
/* ptppins: 64 bit register which needs to hold a PTP time
|
||||||
|
* larger than the current time, otherwise the startptpcp
|
||||||
|
* command won't do anything. So advance the current time
|
||||||
|
* by a number of periods in a way that won't alter the
|
||||||
|
* phase offset.
|
||||||
|
*/
|
||||||
|
rc = __sja1105_ptp_gettimex(priv->ds, &now, NULL);
|
||||||
|
if (rc < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
pin_start = future_base_time(pin_start, pin_duration,
|
||||||
|
now + 1ull * NSEC_PER_SEC);
|
||||||
|
pin_start = ns_to_sja1105_ticks(pin_start);
|
||||||
|
|
||||||
|
rc = sja1105_xfer_u64(priv, SPI_WRITE, regs->ptppinst,
|
||||||
|
&pin_start, NULL);
|
||||||
|
if (rc < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
rc = sja1105_xfer_u32(priv, SPI_WRITE, regs->ptppindur,
|
||||||
|
&pin_duration32, NULL);
|
||||||
|
if (rc < 0)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (on)
|
||||||
|
cmd.startptpcp = true;
|
||||||
|
else
|
||||||
|
cmd.stopptpcp = true;
|
||||||
|
|
||||||
|
rc = sja1105_ptp_commit(priv->ds, &cmd, SPI_WRITE);
|
||||||
|
|
||||||
|
out:
|
||||||
|
mutex_unlock(&ptp_data->lock);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sja1105_extts_enable(struct sja1105_private *priv,
|
||||||
|
struct ptp_extts_request *extts,
|
||||||
|
bool on)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
/* We only support one channel */
|
||||||
|
if (extts->index != 0)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
/* Reject requests with unsupported flags */
|
||||||
|
if (extts->flags)
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
rc = sja1105_change_ptp_clk_pin_func(priv, PTP_PF_EXTTS);
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
if (on)
|
||||||
|
schedule_delayed_work(&priv->ptp_data.extts_work,
|
||||||
|
SJA1105_EXTTS_INTERVAL);
|
||||||
|
else
|
||||||
|
cancel_delayed_work_sync(&priv->ptp_data.extts_work);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sja1105_ptp_enable(struct ptp_clock_info *ptp,
|
||||||
|
struct ptp_clock_request *req, int on)
|
||||||
|
{
|
||||||
|
struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp);
|
||||||
|
struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
|
||||||
|
int rc = -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (req->type == PTP_CLK_REQ_PEROUT)
|
||||||
|
rc = sja1105_per_out_enable(priv, &req->perout, on);
|
||||||
|
else if (req->type == PTP_CLK_REQ_EXTTS)
|
||||||
|
rc = sja1105_extts_enable(priv, &req->extts, on);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sja1105_ptp_verify_pin(struct ptp_clock_info *ptp, unsigned int pin,
|
||||||
|
enum ptp_pin_function func, unsigned int chan)
|
||||||
|
{
|
||||||
|
struct sja1105_ptp_data *ptp_data = ptp_caps_to_data(ptp);
|
||||||
|
struct sja1105_private *priv = ptp_data_to_sja1105(ptp_data);
|
||||||
|
|
||||||
|
if (chan != 0 || pin != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
switch (func) {
|
||||||
|
case PTP_PF_NONE:
|
||||||
|
case PTP_PF_PEROUT:
|
||||||
|
break;
|
||||||
|
case PTP_PF_EXTTS:
|
||||||
|
if (priv->info->device_id == SJA1105E_DEVICE_ID ||
|
||||||
|
priv->info->device_id == SJA1105T_DEVICE_ID)
|
||||||
|
return -1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct ptp_pin_desc sja1105_ptp_pin = {
|
||||||
|
.name = "ptp_clk",
|
||||||
|
.index = 0,
|
||||||
|
.func = PTP_PF_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
int sja1105_ptp_clock_register(struct dsa_switch *ds)
|
int sja1105_ptp_clock_register(struct dsa_switch *ds)
|
||||||
{
|
{
|
||||||
struct sja1105_private *priv = ds->priv;
|
struct sja1105_private *priv = ds->priv;
|
||||||
@ -628,8 +828,14 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds)
|
|||||||
.adjtime = sja1105_ptp_adjtime,
|
.adjtime = sja1105_ptp_adjtime,
|
||||||
.gettimex64 = sja1105_ptp_gettimex,
|
.gettimex64 = sja1105_ptp_gettimex,
|
||||||
.settime64 = sja1105_ptp_settime,
|
.settime64 = sja1105_ptp_settime,
|
||||||
|
.enable = sja1105_ptp_enable,
|
||||||
|
.verify = sja1105_ptp_verify_pin,
|
||||||
.do_aux_work = sja1105_rxtstamp_work,
|
.do_aux_work = sja1105_rxtstamp_work,
|
||||||
.max_adj = SJA1105_MAX_ADJ_PPB,
|
.max_adj = SJA1105_MAX_ADJ_PPB,
|
||||||
|
.pin_config = &sja1105_ptp_pin,
|
||||||
|
.n_pins = 1,
|
||||||
|
.n_ext_ts = 1,
|
||||||
|
.n_per_out = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
skb_queue_head_init(&ptp_data->skb_rxtstamp_queue);
|
skb_queue_head_init(&ptp_data->skb_rxtstamp_queue);
|
||||||
@ -642,6 +848,8 @@ int sja1105_ptp_clock_register(struct dsa_switch *ds)
|
|||||||
ptp_data->cmd.corrclk4ts = true;
|
ptp_data->cmd.corrclk4ts = true;
|
||||||
ptp_data->cmd.ptpclkadd = PTP_SET_MODE;
|
ptp_data->cmd.ptpclkadd = PTP_SET_MODE;
|
||||||
|
|
||||||
|
INIT_DELAYED_WORK(&ptp_data->extts_work, sja1105_ptp_extts_work);
|
||||||
|
|
||||||
return sja1105_ptp_reset(ds);
|
return sja1105_ptp_reset(ds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -653,6 +861,7 @@ void sja1105_ptp_clock_unregister(struct dsa_switch *ds)
|
|||||||
if (IS_ERR_OR_NULL(ptp_data->clock))
|
if (IS_ERR_OR_NULL(ptp_data->clock))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
cancel_delayed_work_sync(&ptp_data->extts_work);
|
||||||
ptp_cancel_worker_sync(ptp_data->clock);
|
ptp_cancel_worker_sync(ptp_data->clock);
|
||||||
skb_queue_purge(&ptp_data->skb_rxtstamp_queue);
|
skb_queue_purge(&ptp_data->skb_rxtstamp_queue);
|
||||||
ptp_clock_unregister(ptp_data->clock);
|
ptp_clock_unregister(ptp_data->clock);
|
||||||
|
@ -21,7 +21,36 @@ static inline s64 sja1105_ticks_to_ns(s64 ticks)
|
|||||||
return ticks * SJA1105_TICK_NS;
|
return ticks * SJA1105_TICK_NS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Calculate the first base_time in the future that satisfies this
|
||||||
|
* relationship:
|
||||||
|
*
|
||||||
|
* future_base_time = base_time + N x cycle_time >= now, or
|
||||||
|
*
|
||||||
|
* now - base_time
|
||||||
|
* N >= ---------------
|
||||||
|
* cycle_time
|
||||||
|
*
|
||||||
|
* Because N is an integer, the ceiling value of the above "a / b" ratio
|
||||||
|
* is in fact precisely the floor value of "(a + b - 1) / b", which is
|
||||||
|
* easier to calculate only having integer division tools.
|
||||||
|
*/
|
||||||
|
static inline s64 future_base_time(s64 base_time, s64 cycle_time, s64 now)
|
||||||
|
{
|
||||||
|
s64 a, b, n;
|
||||||
|
|
||||||
|
if (base_time >= now)
|
||||||
|
return base_time;
|
||||||
|
|
||||||
|
a = now - base_time;
|
||||||
|
b = cycle_time;
|
||||||
|
n = div_s64(a + b - 1, b);
|
||||||
|
|
||||||
|
return base_time + n * cycle_time;
|
||||||
|
}
|
||||||
|
|
||||||
struct sja1105_ptp_cmd {
|
struct sja1105_ptp_cmd {
|
||||||
|
u64 startptpcp; /* start toggling PTP_CLK pin */
|
||||||
|
u64 stopptpcp; /* stop toggling PTP_CLK pin */
|
||||||
u64 ptpstrtsch; /* start schedule */
|
u64 ptpstrtsch; /* start schedule */
|
||||||
u64 ptpstopsch; /* stop schedule */
|
u64 ptpstopsch; /* stop schedule */
|
||||||
u64 resptp; /* reset */
|
u64 resptp; /* reset */
|
||||||
@ -30,12 +59,14 @@ struct sja1105_ptp_cmd {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct sja1105_ptp_data {
|
struct sja1105_ptp_data {
|
||||||
|
struct delayed_work extts_work;
|
||||||
struct sk_buff_head skb_rxtstamp_queue;
|
struct sk_buff_head skb_rxtstamp_queue;
|
||||||
struct ptp_clock_info caps;
|
struct ptp_clock_info caps;
|
||||||
struct ptp_clock *clock;
|
struct ptp_clock *clock;
|
||||||
struct sja1105_ptp_cmd cmd;
|
struct sja1105_ptp_cmd cmd;
|
||||||
/* Serializes all operations on the PTP hardware clock */
|
/* Serializes all operations on the PTP hardware clock */
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
|
u64 ptpsyncts;
|
||||||
};
|
};
|
||||||
|
|
||||||
int sja1105_ptp_clock_register(struct dsa_switch *ds);
|
int sja1105_ptp_clock_register(struct dsa_switch *ds);
|
||||||
|
@ -458,6 +458,8 @@ static struct sja1105_regs sja1105et_regs = {
|
|||||||
.rmii_ext_tx_clk = {0x100018, 0x10001F, 0x100026, 0x10002D, 0x100034},
|
.rmii_ext_tx_clk = {0x100018, 0x10001F, 0x100026, 0x10002D, 0x100034},
|
||||||
.ptpegr_ts = {0xC0, 0xC2, 0xC4, 0xC6, 0xC8},
|
.ptpegr_ts = {0xC0, 0xC2, 0xC4, 0xC6, 0xC8},
|
||||||
.ptpschtm = 0x12, /* Spans 0x12 to 0x13 */
|
.ptpschtm = 0x12, /* Spans 0x12 to 0x13 */
|
||||||
|
.ptppinst = 0x14,
|
||||||
|
.ptppindur = 0x16,
|
||||||
.ptp_control = 0x17,
|
.ptp_control = 0x17,
|
||||||
.ptpclkval = 0x18, /* Spans 0x18 to 0x19 */
|
.ptpclkval = 0x18, /* Spans 0x18 to 0x19 */
|
||||||
.ptpclkrate = 0x1A,
|
.ptpclkrate = 0x1A,
|
||||||
@ -491,10 +493,13 @@ static struct sja1105_regs sja1105pqrs_regs = {
|
|||||||
.qlevel = {0x604, 0x614, 0x624, 0x634, 0x644},
|
.qlevel = {0x604, 0x614, 0x624, 0x634, 0x644},
|
||||||
.ptpegr_ts = {0xC0, 0xC4, 0xC8, 0xCC, 0xD0},
|
.ptpegr_ts = {0xC0, 0xC4, 0xC8, 0xCC, 0xD0},
|
||||||
.ptpschtm = 0x13, /* Spans 0x13 to 0x14 */
|
.ptpschtm = 0x13, /* Spans 0x13 to 0x14 */
|
||||||
|
.ptppinst = 0x15,
|
||||||
|
.ptppindur = 0x17,
|
||||||
.ptp_control = 0x18,
|
.ptp_control = 0x18,
|
||||||
.ptpclkval = 0x19,
|
.ptpclkval = 0x19,
|
||||||
.ptpclkrate = 0x1B,
|
.ptpclkrate = 0x1B,
|
||||||
.ptpclkcorp = 0x1E,
|
.ptpclkcorp = 0x1E,
|
||||||
|
.ptpsyncts = 0x1F,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sja1105_info sja1105e_info = {
|
struct sja1105_info sja1105e_info = {
|
||||||
|
@ -102,12 +102,13 @@ static size_t sja1105et_avb_params_entry_packing(void *buf, void *entry_ptr,
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t sja1105pqrs_avb_params_entry_packing(void *buf, void *entry_ptr,
|
size_t sja1105pqrs_avb_params_entry_packing(void *buf, void *entry_ptr,
|
||||||
enum packing_op op)
|
enum packing_op op)
|
||||||
{
|
{
|
||||||
const size_t size = SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY;
|
const size_t size = SJA1105PQRS_SIZE_AVB_PARAMS_ENTRY;
|
||||||
struct sja1105_avb_params_entry *entry = entry_ptr;
|
struct sja1105_avb_params_entry *entry = entry_ptr;
|
||||||
|
|
||||||
|
sja1105_packing(buf, &entry->cas_master, 126, 126, size, op);
|
||||||
sja1105_packing(buf, &entry->destmeta, 125, 78, size, op);
|
sja1105_packing(buf, &entry->destmeta, 125, 78, size, op);
|
||||||
sja1105_packing(buf, &entry->srcmeta, 77, 30, size, op);
|
sja1105_packing(buf, &entry->srcmeta, 77, 30, size, op);
|
||||||
return size;
|
return size;
|
||||||
|
@ -230,6 +230,7 @@ struct sja1105_l2_policing_entry {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct sja1105_avb_params_entry {
|
struct sja1105_avb_params_entry {
|
||||||
|
u64 cas_master;
|
||||||
u64 destmeta;
|
u64 destmeta;
|
||||||
u64 srcmeta;
|
u64 srcmeta;
|
||||||
};
|
};
|
||||||
|
@ -28,33 +28,6 @@ static s64 sja1105_delta_to_ns(s64 delta)
|
|||||||
return delta * 200;
|
return delta * 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate the first base_time in the future that satisfies this
|
|
||||||
* relationship:
|
|
||||||
*
|
|
||||||
* future_base_time = base_time + N x cycle_time >= now, or
|
|
||||||
*
|
|
||||||
* now - base_time
|
|
||||||
* N >= ---------------
|
|
||||||
* cycle_time
|
|
||||||
*
|
|
||||||
* Because N is an integer, the ceiling value of the above "a / b" ratio
|
|
||||||
* is in fact precisely the floor value of "(a + b - 1) / b", which is
|
|
||||||
* easier to calculate only having integer division tools.
|
|
||||||
*/
|
|
||||||
static s64 future_base_time(s64 base_time, s64 cycle_time, s64 now)
|
|
||||||
{
|
|
||||||
s64 a, b, n;
|
|
||||||
|
|
||||||
if (base_time >= now)
|
|
||||||
return base_time;
|
|
||||||
|
|
||||||
a = now - base_time;
|
|
||||||
b = cycle_time;
|
|
||||||
n = div_s64(a + b - 1, b);
|
|
||||||
|
|
||||||
return base_time + n * cycle_time;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sja1105_tas_set_runtime_params(struct sja1105_private *priv)
|
static int sja1105_tas_set_runtime_params(struct sja1105_private *priv)
|
||||||
{
|
{
|
||||||
struct sja1105_tas_data *tas_data = &priv->tas_data;
|
struct sja1105_tas_data *tas_data = &priv->tas_data;
|
||||||
|
Loading…
Reference in New Issue
Block a user