blob: 12fb3f4d95cd9b0f9a70b410c5d4a9a60fc8a005 [file] [log] [blame]
/*
* Copyright (c) 2018, The Linux Foundation. All rights reserved.
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all copies.
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <linux/kconfig.h>
#include <linux/version.h>
#include <linux/phy.h>
#include <linux/kthread.h>
#include "hsl.h"
#include "ssdk_plat.h"
#include "hsl_phy.h"
#include "qca808x_phy.h"
#if defined(IN_LINUX_STD_PTP)
#include <linux/ptp_clock_kernel.h>
enum {
PTP_PKT_SEQID_UNMATCHED,
PTP_PKT_SEQID_MATCHED,
PTP_PKT_SEQID_MATCH_MAX
};
enum {
QCA808X_PTP_MSG_SYNC,
QCA808X_PTP_MSG_DREQ,
QCA808X_PTP_MSG_PREQ,
QCA808X_PTP_MSG_PRESP,
QCA808X_PTP_MSG_MAX
};
typedef struct {
/* ptp filter class */
a_int32_t ptp_type;
/* ptp frame type */
a_int32_t pkt_type;
} qca808x_ptp_cb;
/* statistics for the event packet*/
typedef struct {
/* the counter saves the packet with sequence id
* matched and unmatched */
a_uint64_t sync_cnt[PTP_PKT_SEQID_MATCH_MAX];
a_uint64_t delay_req_cnt[PTP_PKT_SEQID_MATCH_MAX];
a_uint64_t pdelay_req_cnt[PTP_PKT_SEQID_MATCH_MAX];
a_uint64_t pdelay_resp_cnt[PTP_PKT_SEQID_MATCH_MAX];
a_uint64_t event_pkt_cnt;
} ptp_packet_stat;
typedef struct {
a_uint8_t reserved0;
a_uint8_t reserved1;
a_uint8_t msg_type;
a_uint16_t seqid;
a_uint32_t reserved2;
a_int64_t correction;
} qca808x_embeded_ts;
struct qca808x_ptp_info {
a_int32_t hwts_tx_type;
a_int32_t hwts_rx_type;
struct qca808x_ptp_clock *clock;
struct delayed_work tx_ts_work;
struct delayed_work rx_ts_work;
/* work for writing ingress time to register */
struct delayed_work ingress_trig_work;
a_int32_t ingress_time;
struct sk_buff_head tx_queue;
struct sk_buff_head rx_queue;
qca808x_embeded_ts embeded_ts;
};
#endif
struct qca808x_phy_info {
struct list_head list;
a_uint32_t dev_id;
/* phy real address,it is the mdio addr or the i2c slave addr */
a_uint32_t phy_addr;
/* the address of phy device, it is a fake addr for the i2c accessed phy */
a_uint32_t phydev_addr;
#if defined(IN_LINUX_STD_PTP)
a_int32_t speed;
a_uint16_t clock_mode;
a_uint16_t step_mode;
/* work for gps sencond sync */
struct delayed_work ts_schedule_work;
a_bool_t gps_seconds_sync_en;
/*the statistics array records the counter of
* rx and tx ptp event packet */
ptp_packet_stat pkt_stat[2];
#endif
};
typedef struct {
struct phy_device *phydev;
struct qca808x_phy_info *phy_info;
#if defined(IN_LINUX_STD_PTP)
struct qca808x_ptp_info ptp_info;
#endif
} qca808x_priv;
#if defined(IN_LINUX_STD_PTP)
struct qca808x_ptp_clock{
struct ptp_clock_info caps;
struct ptp_clock *ptp_clock;
struct mutex tsreg_lock;
qca808x_priv *priv;
};
struct qca808x_phy_info* qca808x_phy_info_get(a_uint32_t phy_addr);
void qca808x_ptp_change_notify(struct phy_device *phydev);
int qca808x_hwtstamp(struct phy_device *phydev, struct ifreq *ifr);
bool qca808x_rxtstamp(struct phy_device *phydev, struct sk_buff *skb, int type);
void qca808x_txtstamp(struct phy_device *phydev, struct sk_buff *skb, int type);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4,4,0))
int qca808x_ts_info(struct phy_device *phydev, struct ethtool_ts_info *info);
#endif
sw_error_t qca808x_ptp_config_init(struct phy_device *phydev);
int qca808x_ptp_init(qca808x_priv *priv);
void qca808x_ptp_deinit(qca808x_priv *priv);
#endif
void qca808x_phydev_init(a_uint32_t dev_id, a_uint32_t port_id);
void qca808x_phydev_deinit(a_uint32_t dev_id, a_uint32_t port_id);
a_int32_t qca808x_phy_driver_register(void);
void qca808x_phy_driver_unregister(void);