162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PTP 1588 clock using the EG20T PCH 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2010 OMICRON electronics GmbH 662306a36Sopenharmony_ci * Copyright (C) 2011-2012 LAPIS SEMICONDUCTOR Co., LTD. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This code was derived from the IXP46X driver. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/device.h> 1262306a36Sopenharmony_ci#include <linux/err.h> 1362306a36Sopenharmony_ci#include <linux/interrupt.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/io-64-nonatomic-lo-hi.h> 1662306a36Sopenharmony_ci#include <linux/io-64-nonatomic-hi-lo.h> 1762306a36Sopenharmony_ci#include <linux/irq.h> 1862306a36Sopenharmony_ci#include <linux/kernel.h> 1962306a36Sopenharmony_ci#include <linux/module.h> 2062306a36Sopenharmony_ci#include <linux/pci.h> 2162306a36Sopenharmony_ci#include <linux/ptp_clock_kernel.h> 2262306a36Sopenharmony_ci#include <linux/ptp_pch.h> 2362306a36Sopenharmony_ci#include <linux/slab.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define STATION_ADDR_LEN 20 2662306a36Sopenharmony_ci#define PCI_DEVICE_ID_PCH_1588 0x8819 2762306a36Sopenharmony_ci#define IO_MEM_BAR 1 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define DEFAULT_ADDEND 0xA0000000 3062306a36Sopenharmony_ci#define TICKS_NS_SHIFT 5 3162306a36Sopenharmony_ci#define N_EXT_TS 2 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cienum pch_status { 3462306a36Sopenharmony_ci PCH_SUCCESS, 3562306a36Sopenharmony_ci PCH_INVALIDPARAM, 3662306a36Sopenharmony_ci PCH_NOTIMESTAMP, 3762306a36Sopenharmony_ci PCH_INTERRUPTMODEINUSE, 3862306a36Sopenharmony_ci PCH_FAILED, 3962306a36Sopenharmony_ci PCH_UNSUPPORTED, 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * struct pch_ts_regs - IEEE 1588 registers 4462306a36Sopenharmony_ci */ 4562306a36Sopenharmony_cistruct pch_ts_regs { 4662306a36Sopenharmony_ci u32 control; 4762306a36Sopenharmony_ci u32 event; 4862306a36Sopenharmony_ci u32 addend; 4962306a36Sopenharmony_ci u32 accum; 5062306a36Sopenharmony_ci u32 test; 5162306a36Sopenharmony_ci u32 ts_compare; 5262306a36Sopenharmony_ci u32 rsystime_lo; 5362306a36Sopenharmony_ci u32 rsystime_hi; 5462306a36Sopenharmony_ci u32 systime_lo; 5562306a36Sopenharmony_ci u32 systime_hi; 5662306a36Sopenharmony_ci u32 trgt_lo; 5762306a36Sopenharmony_ci u32 trgt_hi; 5862306a36Sopenharmony_ci u32 asms_lo; 5962306a36Sopenharmony_ci u32 asms_hi; 6062306a36Sopenharmony_ci u32 amms_lo; 6162306a36Sopenharmony_ci u32 amms_hi; 6262306a36Sopenharmony_ci u32 ch_control; 6362306a36Sopenharmony_ci u32 ch_event; 6462306a36Sopenharmony_ci u32 tx_snap_lo; 6562306a36Sopenharmony_ci u32 tx_snap_hi; 6662306a36Sopenharmony_ci u32 rx_snap_lo; 6762306a36Sopenharmony_ci u32 rx_snap_hi; 6862306a36Sopenharmony_ci u32 src_uuid_lo; 6962306a36Sopenharmony_ci u32 src_uuid_hi; 7062306a36Sopenharmony_ci u32 can_status; 7162306a36Sopenharmony_ci u32 can_snap_lo; 7262306a36Sopenharmony_ci u32 can_snap_hi; 7362306a36Sopenharmony_ci u32 ts_sel; 7462306a36Sopenharmony_ci u32 ts_st[6]; 7562306a36Sopenharmony_ci u32 reserve1[14]; 7662306a36Sopenharmony_ci u32 stl_max_set_en; 7762306a36Sopenharmony_ci u32 stl_max_set; 7862306a36Sopenharmony_ci u32 reserve2[13]; 7962306a36Sopenharmony_ci u32 srst; 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci#define PCH_TSC_RESET (1 << 0) 8362306a36Sopenharmony_ci#define PCH_TSC_TTM_MASK (1 << 1) 8462306a36Sopenharmony_ci#define PCH_TSC_ASMS_MASK (1 << 2) 8562306a36Sopenharmony_ci#define PCH_TSC_AMMS_MASK (1 << 3) 8662306a36Sopenharmony_ci#define PCH_TSC_PPSM_MASK (1 << 4) 8762306a36Sopenharmony_ci#define PCH_TSE_TTIPEND (1 << 1) 8862306a36Sopenharmony_ci#define PCH_TSE_SNS (1 << 2) 8962306a36Sopenharmony_ci#define PCH_TSE_SNM (1 << 3) 9062306a36Sopenharmony_ci#define PCH_TSE_PPS (1 << 4) 9162306a36Sopenharmony_ci#define PCH_CC_MM (1 << 0) 9262306a36Sopenharmony_ci#define PCH_CC_TA (1 << 1) 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci#define PCH_CC_MODE_SHIFT 16 9562306a36Sopenharmony_ci#define PCH_CC_MODE_MASK 0x001F0000 9662306a36Sopenharmony_ci#define PCH_CC_VERSION (1 << 31) 9762306a36Sopenharmony_ci#define PCH_CE_TXS (1 << 0) 9862306a36Sopenharmony_ci#define PCH_CE_RXS (1 << 1) 9962306a36Sopenharmony_ci#define PCH_CE_OVR (1 << 0) 10062306a36Sopenharmony_ci#define PCH_CE_VAL (1 << 1) 10162306a36Sopenharmony_ci#define PCH_ECS_ETH (1 << 0) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci#define PCH_ECS_CAN (1 << 1) 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci#define PCH_IEEE1588_ETH (1 << 0) 10662306a36Sopenharmony_ci#define PCH_IEEE1588_CAN (1 << 1) 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci/* 10962306a36Sopenharmony_ci * struct pch_dev - Driver private data 11062306a36Sopenharmony_ci */ 11162306a36Sopenharmony_cistruct pch_dev { 11262306a36Sopenharmony_ci struct pch_ts_regs __iomem *regs; 11362306a36Sopenharmony_ci struct ptp_clock *ptp_clock; 11462306a36Sopenharmony_ci struct ptp_clock_info caps; 11562306a36Sopenharmony_ci int exts0_enabled; 11662306a36Sopenharmony_ci int exts1_enabled; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci u32 irq; 11962306a36Sopenharmony_ci struct pci_dev *pdev; 12062306a36Sopenharmony_ci spinlock_t register_lock; 12162306a36Sopenharmony_ci}; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * struct pch_params - 1588 module parameter 12562306a36Sopenharmony_ci */ 12662306a36Sopenharmony_cistruct pch_params { 12762306a36Sopenharmony_ci u8 station[STATION_ADDR_LEN]; 12862306a36Sopenharmony_ci}; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* structure to hold the module parameters */ 13162306a36Sopenharmony_cistatic struct pch_params pch_param = { 13262306a36Sopenharmony_ci "00:00:00:00:00:00" 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* 13662306a36Sopenharmony_ci * Register access functions 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_cistatic inline void pch_eth_enable_set(struct pch_dev *chip) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci u32 val; 14162306a36Sopenharmony_ci /* SET the eth_enable bit */ 14262306a36Sopenharmony_ci val = ioread32(&chip->regs->ts_sel) | (PCH_ECS_ETH); 14362306a36Sopenharmony_ci iowrite32(val, (&chip->regs->ts_sel)); 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic u64 pch_systime_read(struct pch_ts_regs __iomem *regs) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci u64 ns; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci ns = ioread64_lo_hi(®s->systime_lo); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci return ns << TICKS_NS_SHIFT; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic void pch_systime_write(struct pch_ts_regs __iomem *regs, u64 ns) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci iowrite64_lo_hi(ns >> TICKS_NS_SHIFT, ®s->systime_lo); 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic inline void pch_block_reset(struct pch_dev *chip) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci u32 val; 16362306a36Sopenharmony_ci /* Reset Hardware Assist block */ 16462306a36Sopenharmony_ci val = ioread32(&chip->regs->control) | PCH_TSC_RESET; 16562306a36Sopenharmony_ci iowrite32(val, (&chip->regs->control)); 16662306a36Sopenharmony_ci val = val & ~PCH_TSC_RESET; 16762306a36Sopenharmony_ci iowrite32(val, (&chip->regs->control)); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_civoid pch_ch_control_write(struct pci_dev *pdev, u32 val) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct pch_dev *chip = pci_get_drvdata(pdev); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci iowrite32(val, (&chip->regs->ch_control)); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ciEXPORT_SYMBOL(pch_ch_control_write); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ciu32 pch_ch_event_read(struct pci_dev *pdev) 17962306a36Sopenharmony_ci{ 18062306a36Sopenharmony_ci struct pch_dev *chip = pci_get_drvdata(pdev); 18162306a36Sopenharmony_ci u32 val; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci val = ioread32(&chip->regs->ch_event); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci return val; 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ciEXPORT_SYMBOL(pch_ch_event_read); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_civoid pch_ch_event_write(struct pci_dev *pdev, u32 val) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct pch_dev *chip = pci_get_drvdata(pdev); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci iowrite32(val, (&chip->regs->ch_event)); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ciEXPORT_SYMBOL(pch_ch_event_write); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ciu32 pch_src_uuid_lo_read(struct pci_dev *pdev) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct pch_dev *chip = pci_get_drvdata(pdev); 20062306a36Sopenharmony_ci u32 val; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci val = ioread32(&chip->regs->src_uuid_lo); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return val; 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ciEXPORT_SYMBOL(pch_src_uuid_lo_read); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ciu32 pch_src_uuid_hi_read(struct pci_dev *pdev) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct pch_dev *chip = pci_get_drvdata(pdev); 21162306a36Sopenharmony_ci u32 val; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci val = ioread32(&chip->regs->src_uuid_hi); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return val; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ciEXPORT_SYMBOL(pch_src_uuid_hi_read); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ciu64 pch_rx_snap_read(struct pci_dev *pdev) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci struct pch_dev *chip = pci_get_drvdata(pdev); 22262306a36Sopenharmony_ci u64 ns; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci ns = ioread64_lo_hi(&chip->regs->rx_snap_lo); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return ns << TICKS_NS_SHIFT; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ciEXPORT_SYMBOL(pch_rx_snap_read); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ciu64 pch_tx_snap_read(struct pci_dev *pdev) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci struct pch_dev *chip = pci_get_drvdata(pdev); 23362306a36Sopenharmony_ci u64 ns; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci ns = ioread64_lo_hi(&chip->regs->tx_snap_lo); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return ns << TICKS_NS_SHIFT; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ciEXPORT_SYMBOL(pch_tx_snap_read); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci/* This function enables all 64 bits in system time registers [high & low]. 24262306a36Sopenharmony_ciThis is a work-around for non continuous value in the SystemTime Register*/ 24362306a36Sopenharmony_cistatic void pch_set_system_time_count(struct pch_dev *chip) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci iowrite32(0x01, &chip->regs->stl_max_set_en); 24662306a36Sopenharmony_ci iowrite32(0xFFFFFFFF, &chip->regs->stl_max_set); 24762306a36Sopenharmony_ci iowrite32(0x00, &chip->regs->stl_max_set_en); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic void pch_reset(struct pch_dev *chip) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci /* Reset Hardware Assist */ 25362306a36Sopenharmony_ci pch_block_reset(chip); 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci /* enable all 32 bits in system time registers */ 25662306a36Sopenharmony_ci pch_set_system_time_count(chip); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci/** 26062306a36Sopenharmony_ci * pch_set_station_address() - This API sets the station address used by 26162306a36Sopenharmony_ci * IEEE 1588 hardware when looking at PTP 26262306a36Sopenharmony_ci * traffic on the ethernet interface 26362306a36Sopenharmony_ci * @addr: dress which contain the column separated address to be used. 26462306a36Sopenharmony_ci * @pdev: PCI device. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_ciint pch_set_station_address(u8 *addr, struct pci_dev *pdev) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct pch_dev *chip = pci_get_drvdata(pdev); 26962306a36Sopenharmony_ci bool valid; 27062306a36Sopenharmony_ci u64 mac; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Verify the parameter */ 27362306a36Sopenharmony_ci if ((chip->regs == NULL) || addr == (u8 *)NULL) { 27462306a36Sopenharmony_ci dev_err(&pdev->dev, 27562306a36Sopenharmony_ci "invalid params returning PCH_INVALIDPARAM\n"); 27662306a36Sopenharmony_ci return PCH_INVALIDPARAM; 27762306a36Sopenharmony_ci } 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci valid = mac_pton(addr, (u8 *)&mac); 28062306a36Sopenharmony_ci if (!valid) { 28162306a36Sopenharmony_ci dev_err(&pdev->dev, "invalid params returning PCH_INVALIDPARAM\n"); 28262306a36Sopenharmony_ci return PCH_INVALIDPARAM; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci dev_dbg(&pdev->dev, "invoking pch_station_set\n"); 28662306a36Sopenharmony_ci iowrite64_lo_hi(mac, &chip->regs->ts_st); 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci} 28962306a36Sopenharmony_ciEXPORT_SYMBOL(pch_set_station_address); 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci/* 29262306a36Sopenharmony_ci * Interrupt service routine 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_cistatic irqreturn_t isr(int irq, void *priv) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci struct pch_dev *pch_dev = priv; 29762306a36Sopenharmony_ci struct pch_ts_regs __iomem *regs = pch_dev->regs; 29862306a36Sopenharmony_ci struct ptp_clock_event event; 29962306a36Sopenharmony_ci u32 ack = 0, val; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci val = ioread32(®s->event); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (val & PCH_TSE_SNS) { 30462306a36Sopenharmony_ci ack |= PCH_TSE_SNS; 30562306a36Sopenharmony_ci if (pch_dev->exts0_enabled) { 30662306a36Sopenharmony_ci event.type = PTP_CLOCK_EXTTS; 30762306a36Sopenharmony_ci event.index = 0; 30862306a36Sopenharmony_ci event.timestamp = ioread64_hi_lo(®s->asms_hi); 30962306a36Sopenharmony_ci event.timestamp <<= TICKS_NS_SHIFT; 31062306a36Sopenharmony_ci ptp_clock_event(pch_dev->ptp_clock, &event); 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (val & PCH_TSE_SNM) { 31562306a36Sopenharmony_ci ack |= PCH_TSE_SNM; 31662306a36Sopenharmony_ci if (pch_dev->exts1_enabled) { 31762306a36Sopenharmony_ci event.type = PTP_CLOCK_EXTTS; 31862306a36Sopenharmony_ci event.index = 1; 31962306a36Sopenharmony_ci event.timestamp = ioread64_hi_lo(®s->asms_hi); 32062306a36Sopenharmony_ci event.timestamp <<= TICKS_NS_SHIFT; 32162306a36Sopenharmony_ci ptp_clock_event(pch_dev->ptp_clock, &event); 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (val & PCH_TSE_TTIPEND) 32662306a36Sopenharmony_ci ack |= PCH_TSE_TTIPEND; /* this bit seems to be always set */ 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (ack) { 32962306a36Sopenharmony_ci iowrite32(ack, ®s->event); 33062306a36Sopenharmony_ci return IRQ_HANDLED; 33162306a36Sopenharmony_ci } else 33262306a36Sopenharmony_ci return IRQ_NONE; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/* 33662306a36Sopenharmony_ci * PTP clock operations 33762306a36Sopenharmony_ci */ 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic int ptp_pch_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci u32 addend; 34262306a36Sopenharmony_ci struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps); 34362306a36Sopenharmony_ci struct pch_ts_regs __iomem *regs = pch_dev->regs; 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci addend = adjust_by_scaled_ppm(DEFAULT_ADDEND, scaled_ppm); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci iowrite32(addend, ®s->addend); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic int ptp_pch_adjtime(struct ptp_clock_info *ptp, s64 delta) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci s64 now; 35562306a36Sopenharmony_ci unsigned long flags; 35662306a36Sopenharmony_ci struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps); 35762306a36Sopenharmony_ci struct pch_ts_regs __iomem *regs = pch_dev->regs; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci spin_lock_irqsave(&pch_dev->register_lock, flags); 36062306a36Sopenharmony_ci now = pch_systime_read(regs); 36162306a36Sopenharmony_ci now += delta; 36262306a36Sopenharmony_ci pch_systime_write(regs, now); 36362306a36Sopenharmony_ci spin_unlock_irqrestore(&pch_dev->register_lock, flags); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci return 0; 36662306a36Sopenharmony_ci} 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic int ptp_pch_gettime(struct ptp_clock_info *ptp, struct timespec64 *ts) 36962306a36Sopenharmony_ci{ 37062306a36Sopenharmony_ci u64 ns; 37162306a36Sopenharmony_ci unsigned long flags; 37262306a36Sopenharmony_ci struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps); 37362306a36Sopenharmony_ci struct pch_ts_regs __iomem *regs = pch_dev->regs; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci spin_lock_irqsave(&pch_dev->register_lock, flags); 37662306a36Sopenharmony_ci ns = pch_systime_read(regs); 37762306a36Sopenharmony_ci spin_unlock_irqrestore(&pch_dev->register_lock, flags); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci *ts = ns_to_timespec64(ns); 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int ptp_pch_settime(struct ptp_clock_info *ptp, 38462306a36Sopenharmony_ci const struct timespec64 *ts) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci u64 ns; 38762306a36Sopenharmony_ci unsigned long flags; 38862306a36Sopenharmony_ci struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps); 38962306a36Sopenharmony_ci struct pch_ts_regs __iomem *regs = pch_dev->regs; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci ns = timespec64_to_ns(ts); 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci spin_lock_irqsave(&pch_dev->register_lock, flags); 39462306a36Sopenharmony_ci pch_systime_write(regs, ns); 39562306a36Sopenharmony_ci spin_unlock_irqrestore(&pch_dev->register_lock, flags); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int ptp_pch_enable(struct ptp_clock_info *ptp, 40162306a36Sopenharmony_ci struct ptp_clock_request *rq, int on) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci struct pch_dev *pch_dev = container_of(ptp, struct pch_dev, caps); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci switch (rq->type) { 40662306a36Sopenharmony_ci case PTP_CLK_REQ_EXTTS: 40762306a36Sopenharmony_ci switch (rq->extts.index) { 40862306a36Sopenharmony_ci case 0: 40962306a36Sopenharmony_ci pch_dev->exts0_enabled = on ? 1 : 0; 41062306a36Sopenharmony_ci break; 41162306a36Sopenharmony_ci case 1: 41262306a36Sopenharmony_ci pch_dev->exts1_enabled = on ? 1 : 0; 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci default: 41562306a36Sopenharmony_ci return -EINVAL; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci return 0; 41862306a36Sopenharmony_ci default: 41962306a36Sopenharmony_ci break; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return -EOPNOTSUPP; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic const struct ptp_clock_info ptp_pch_caps = { 42662306a36Sopenharmony_ci .owner = THIS_MODULE, 42762306a36Sopenharmony_ci .name = "PCH timer", 42862306a36Sopenharmony_ci .max_adj = 50000000, 42962306a36Sopenharmony_ci .n_ext_ts = N_EXT_TS, 43062306a36Sopenharmony_ci .n_pins = 0, 43162306a36Sopenharmony_ci .pps = 0, 43262306a36Sopenharmony_ci .adjfine = ptp_pch_adjfine, 43362306a36Sopenharmony_ci .adjtime = ptp_pch_adjtime, 43462306a36Sopenharmony_ci .gettime64 = ptp_pch_gettime, 43562306a36Sopenharmony_ci .settime64 = ptp_pch_settime, 43662306a36Sopenharmony_ci .enable = ptp_pch_enable, 43762306a36Sopenharmony_ci}; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_cistatic void pch_remove(struct pci_dev *pdev) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci struct pch_dev *chip = pci_get_drvdata(pdev); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci free_irq(pdev->irq, chip); 44462306a36Sopenharmony_ci ptp_clock_unregister(chip->ptp_clock); 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic s32 44862306a36Sopenharmony_cipch_probe(struct pci_dev *pdev, const struct pci_device_id *id) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci s32 ret; 45162306a36Sopenharmony_ci unsigned long flags; 45262306a36Sopenharmony_ci struct pch_dev *chip; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); 45562306a36Sopenharmony_ci if (chip == NULL) 45662306a36Sopenharmony_ci return -ENOMEM; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* enable the 1588 pci device */ 45962306a36Sopenharmony_ci ret = pcim_enable_device(pdev); 46062306a36Sopenharmony_ci if (ret != 0) { 46162306a36Sopenharmony_ci dev_err(&pdev->dev, "could not enable the pci device\n"); 46262306a36Sopenharmony_ci return ret; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci ret = pcim_iomap_regions(pdev, BIT(IO_MEM_BAR), "1588_regs"); 46662306a36Sopenharmony_ci if (ret) { 46762306a36Sopenharmony_ci dev_err(&pdev->dev, "could not locate IO memory address\n"); 46862306a36Sopenharmony_ci return ret; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* get the virtual address to the 1588 registers */ 47262306a36Sopenharmony_ci chip->regs = pcim_iomap_table(pdev)[IO_MEM_BAR]; 47362306a36Sopenharmony_ci chip->caps = ptp_pch_caps; 47462306a36Sopenharmony_ci chip->ptp_clock = ptp_clock_register(&chip->caps, &pdev->dev); 47562306a36Sopenharmony_ci if (IS_ERR(chip->ptp_clock)) 47662306a36Sopenharmony_ci return PTR_ERR(chip->ptp_clock); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci spin_lock_init(&chip->register_lock); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci ret = request_irq(pdev->irq, &isr, IRQF_SHARED, KBUILD_MODNAME, chip); 48162306a36Sopenharmony_ci if (ret != 0) { 48262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get irq %d\n", pdev->irq); 48362306a36Sopenharmony_ci goto err_req_irq; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci /* indicate success */ 48762306a36Sopenharmony_ci chip->irq = pdev->irq; 48862306a36Sopenharmony_ci chip->pdev = pdev; 48962306a36Sopenharmony_ci pci_set_drvdata(pdev, chip); 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci spin_lock_irqsave(&chip->register_lock, flags); 49262306a36Sopenharmony_ci /* reset the ieee1588 h/w */ 49362306a36Sopenharmony_ci pch_reset(chip); 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci iowrite32(DEFAULT_ADDEND, &chip->regs->addend); 49662306a36Sopenharmony_ci iowrite64_lo_hi(1, &chip->regs->trgt_lo); 49762306a36Sopenharmony_ci iowrite32(PCH_TSE_TTIPEND, &chip->regs->event); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci pch_eth_enable_set(chip); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (strcmp(pch_param.station, "00:00:00:00:00:00") != 0) { 50262306a36Sopenharmony_ci if (pch_set_station_address(pch_param.station, pdev) != 0) { 50362306a36Sopenharmony_ci dev_err(&pdev->dev, 50462306a36Sopenharmony_ci "Invalid station address parameter\n" 50562306a36Sopenharmony_ci "Module loaded but station address not set correctly\n" 50662306a36Sopenharmony_ci ); 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci spin_unlock_irqrestore(&chip->register_lock, flags); 51062306a36Sopenharmony_ci return 0; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_cierr_req_irq: 51362306a36Sopenharmony_ci ptp_clock_unregister(chip->ptp_clock); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci dev_err(&pdev->dev, "probe failed(ret=0x%x)\n", ret); 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci return ret; 51862306a36Sopenharmony_ci} 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_cistatic const struct pci_device_id pch_ieee1588_pcidev_id[] = { 52162306a36Sopenharmony_ci { 52262306a36Sopenharmony_ci .vendor = PCI_VENDOR_ID_INTEL, 52362306a36Sopenharmony_ci .device = PCI_DEVICE_ID_PCH_1588 52462306a36Sopenharmony_ci }, 52562306a36Sopenharmony_ci {0} 52662306a36Sopenharmony_ci}; 52762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, pch_ieee1588_pcidev_id); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic struct pci_driver pch_driver = { 53062306a36Sopenharmony_ci .name = KBUILD_MODNAME, 53162306a36Sopenharmony_ci .id_table = pch_ieee1588_pcidev_id, 53262306a36Sopenharmony_ci .probe = pch_probe, 53362306a36Sopenharmony_ci .remove = pch_remove, 53462306a36Sopenharmony_ci}; 53562306a36Sopenharmony_cimodule_pci_driver(pch_driver); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_cimodule_param_string(station, 53862306a36Sopenharmony_ci pch_param.station, sizeof(pch_param.station), 0444); 53962306a36Sopenharmony_ciMODULE_PARM_DESC(station, 54062306a36Sopenharmony_ci "IEEE 1588 station address to use - colon separated hex values"); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ciMODULE_AUTHOR("LAPIS SEMICONDUCTOR, <tshimizu818@gmail.com>"); 54362306a36Sopenharmony_ciMODULE_DESCRIPTION("PTP clock using the EG20T timer"); 54462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 545