18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Marvell PTP driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2020 Marvell International Ltd. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "ptp.h" 138c2ecf20Sopenharmony_ci#include "mbox.h" 148c2ecf20Sopenharmony_ci#include "rvu.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define DRV_NAME "Marvell PTP Driver" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define PCI_DEVID_OCTEONTX2_PTP 0xA00C 198c2ecf20Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_98xx_PTP 0xB100 208c2ecf20Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_96XX_PTP 0xB200 218c2ecf20Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_95XX_PTP 0xB300 228c2ecf20Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_LOKI_PTP 0xB400 238c2ecf20Sopenharmony_ci#define PCI_SUBSYS_DEVID_OCTX2_95MM_PTP 0xB500 248c2ecf20Sopenharmony_ci#define PCI_DEVID_OCTEONTX2_RST 0xA085 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define PCI_PTP_BAR_NO 0 278c2ecf20Sopenharmony_ci#define PCI_RST_BAR_NO 0 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define PTP_CLOCK_CFG 0xF00ULL 308c2ecf20Sopenharmony_ci#define PTP_CLOCK_CFG_PTP_EN BIT_ULL(0) 318c2ecf20Sopenharmony_ci#define PTP_CLOCK_LO 0xF08ULL 328c2ecf20Sopenharmony_ci#define PTP_CLOCK_HI 0xF10ULL 338c2ecf20Sopenharmony_ci#define PTP_CLOCK_COMP 0xF18ULL 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#define RST_BOOT 0x1600ULL 368c2ecf20Sopenharmony_ci#define RST_MUL_BITS GENMASK_ULL(38, 33) 378c2ecf20Sopenharmony_ci#define CLOCK_BASE_RATE 50000000ULL 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic u64 get_clock_rate(void) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci u64 cfg, ret = CLOCK_BASE_RATE * 16; 428c2ecf20Sopenharmony_ci struct pci_dev *pdev; 438c2ecf20Sopenharmony_ci void __iomem *base; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci /* To get the input clock frequency with which PTP co-processor 468c2ecf20Sopenharmony_ci * block is running the base frequency(50 MHz) needs to be multiplied 478c2ecf20Sopenharmony_ci * with multiplier bits present in RST_BOOT register of RESET block. 488c2ecf20Sopenharmony_ci * Hence below code gets the multiplier bits from the RESET PCI 498c2ecf20Sopenharmony_ci * device present in the system. 508c2ecf20Sopenharmony_ci */ 518c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, 528c2ecf20Sopenharmony_ci PCI_DEVID_OCTEONTX2_RST, NULL); 538c2ecf20Sopenharmony_ci if (!pdev) 548c2ecf20Sopenharmony_ci goto error; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci base = pci_ioremap_bar(pdev, PCI_RST_BAR_NO); 578c2ecf20Sopenharmony_ci if (!base) 588c2ecf20Sopenharmony_ci goto error_put_pdev; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci cfg = readq(base + RST_BOOT); 618c2ecf20Sopenharmony_ci ret = CLOCK_BASE_RATE * FIELD_GET(RST_MUL_BITS, cfg); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci iounmap(base); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cierror_put_pdev: 668c2ecf20Sopenharmony_ci pci_dev_put(pdev); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cierror: 698c2ecf20Sopenharmony_ci return ret; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistruct ptp *ptp_get(void) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct pci_dev *pdev; 758c2ecf20Sopenharmony_ci struct ptp *ptp; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* If the PTP pci device is found on the system and ptp 788c2ecf20Sopenharmony_ci * driver is bound to it then the PTP pci device is returned 798c2ecf20Sopenharmony_ci * to the caller(rvu driver). 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci pdev = pci_get_device(PCI_VENDOR_ID_CAVIUM, 828c2ecf20Sopenharmony_ci PCI_DEVID_OCTEONTX2_PTP, NULL); 838c2ecf20Sopenharmony_ci if (!pdev) 848c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci ptp = pci_get_drvdata(pdev); 878c2ecf20Sopenharmony_ci if (!ptp) 888c2ecf20Sopenharmony_ci ptp = ERR_PTR(-EPROBE_DEFER); 898c2ecf20Sopenharmony_ci if (IS_ERR(ptp)) 908c2ecf20Sopenharmony_ci pci_dev_put(pdev); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return ptp; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_civoid ptp_put(struct ptp *ptp) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci if (!ptp) 988c2ecf20Sopenharmony_ci return; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci pci_dev_put(ptp->pdev); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int ptp_adjfine(struct ptp *ptp, long scaled_ppm) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci bool neg_adj = false; 1068c2ecf20Sopenharmony_ci u64 comp; 1078c2ecf20Sopenharmony_ci u64 adj; 1088c2ecf20Sopenharmony_ci s64 ppb; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (scaled_ppm < 0) { 1118c2ecf20Sopenharmony_ci neg_adj = true; 1128c2ecf20Sopenharmony_ci scaled_ppm = -scaled_ppm; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci /* The hardware adds the clock compensation value to the PTP clock 1168c2ecf20Sopenharmony_ci * on every coprocessor clock cycle. Typical convention is that it 1178c2ecf20Sopenharmony_ci * represent number of nanosecond betwen each cycle. In this 1188c2ecf20Sopenharmony_ci * convention compensation value is in 64 bit fixed-point 1198c2ecf20Sopenharmony_ci * representation where upper 32 bits are number of nanoseconds 1208c2ecf20Sopenharmony_ci * and lower is fractions of nanosecond. 1218c2ecf20Sopenharmony_ci * The scaled_ppm represent the ratio in "parts per million" by which 1228c2ecf20Sopenharmony_ci * the compensation value should be corrected. 1238c2ecf20Sopenharmony_ci * To calculate new compenstation value we use 64bit fixed point 1248c2ecf20Sopenharmony_ci * arithmetic on following formula 1258c2ecf20Sopenharmony_ci * comp = tbase + tbase * scaled_ppm / (1M * 2^16) 1268c2ecf20Sopenharmony_ci * where tbase is the basic compensation value calculated 1278c2ecf20Sopenharmony_ci * initialy in the probe function. 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_ci comp = ((u64)1000000000ull << 32) / ptp->clock_rate; 1308c2ecf20Sopenharmony_ci /* convert scaled_ppm to ppb */ 1318c2ecf20Sopenharmony_ci ppb = 1 + scaled_ppm; 1328c2ecf20Sopenharmony_ci ppb *= 125; 1338c2ecf20Sopenharmony_ci ppb >>= 13; 1348c2ecf20Sopenharmony_ci adj = comp * ppb; 1358c2ecf20Sopenharmony_ci adj = div_u64(adj, 1000000000ull); 1368c2ecf20Sopenharmony_ci comp = neg_adj ? comp - adj : comp + adj; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci writeq(comp, ptp->reg_base + PTP_CLOCK_COMP); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci return 0; 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int ptp_get_clock(struct ptp *ptp, u64 *clk) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci /* Return the current PTP clock */ 1468c2ecf20Sopenharmony_ci *clk = readq(ptp->reg_base + PTP_CLOCK_HI); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return 0; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic int ptp_probe(struct pci_dev *pdev, 1528c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1558c2ecf20Sopenharmony_ci struct ptp *ptp; 1568c2ecf20Sopenharmony_ci u64 clock_comp; 1578c2ecf20Sopenharmony_ci u64 clock_cfg; 1588c2ecf20Sopenharmony_ci int err; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci ptp = devm_kzalloc(dev, sizeof(*ptp), GFP_KERNEL); 1618c2ecf20Sopenharmony_ci if (!ptp) { 1628c2ecf20Sopenharmony_ci err = -ENOMEM; 1638c2ecf20Sopenharmony_ci goto error; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci ptp->pdev = pdev; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci err = pcim_enable_device(pdev); 1698c2ecf20Sopenharmony_ci if (err) 1708c2ecf20Sopenharmony_ci goto error_free; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci err = pcim_iomap_regions(pdev, 1 << PCI_PTP_BAR_NO, pci_name(pdev)); 1738c2ecf20Sopenharmony_ci if (err) 1748c2ecf20Sopenharmony_ci goto error_free; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ptp->reg_base = pcim_iomap_table(pdev)[PCI_PTP_BAR_NO]; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ptp->clock_rate = get_clock_rate(); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci /* Enable PTP clock */ 1818c2ecf20Sopenharmony_ci clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG); 1828c2ecf20Sopenharmony_ci clock_cfg |= PTP_CLOCK_CFG_PTP_EN; 1838c2ecf20Sopenharmony_ci writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci clock_comp = ((u64)1000000000ull << 32) / ptp->clock_rate; 1868c2ecf20Sopenharmony_ci /* Initial compensation value to start the nanosecs counter */ 1878c2ecf20Sopenharmony_ci writeq(clock_comp, ptp->reg_base + PTP_CLOCK_COMP); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, ptp); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci return 0; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_cierror_free: 1948c2ecf20Sopenharmony_ci devm_kfree(dev, ptp); 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cierror: 1978c2ecf20Sopenharmony_ci /* For `ptp_get()` we need to differentiate between the case 1988c2ecf20Sopenharmony_ci * when the core has not tried to probe this device and the case when 1998c2ecf20Sopenharmony_ci * the probe failed. In the later case we pretend that the 2008c2ecf20Sopenharmony_ci * initialization was successful and keep the error in 2018c2ecf20Sopenharmony_ci * `dev->driver_data`. 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, ERR_PTR(err)); 2048c2ecf20Sopenharmony_ci return 0; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void ptp_remove(struct pci_dev *pdev) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct ptp *ptp = pci_get_drvdata(pdev); 2108c2ecf20Sopenharmony_ci u64 clock_cfg; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(ptp)) 2138c2ecf20Sopenharmony_ci return; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* Disable PTP clock */ 2168c2ecf20Sopenharmony_ci clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG); 2178c2ecf20Sopenharmony_ci clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN; 2188c2ecf20Sopenharmony_ci writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic const struct pci_device_id ptp_id_table[] = { 2228c2ecf20Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, 2238c2ecf20Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 2248c2ecf20Sopenharmony_ci PCI_SUBSYS_DEVID_OCTX2_98xx_PTP) }, 2258c2ecf20Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, 2268c2ecf20Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 2278c2ecf20Sopenharmony_ci PCI_SUBSYS_DEVID_OCTX2_96XX_PTP) }, 2288c2ecf20Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, 2298c2ecf20Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 2308c2ecf20Sopenharmony_ci PCI_SUBSYS_DEVID_OCTX2_95XX_PTP) }, 2318c2ecf20Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, 2328c2ecf20Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 2338c2ecf20Sopenharmony_ci PCI_SUBSYS_DEVID_OCTX2_LOKI_PTP) }, 2348c2ecf20Sopenharmony_ci { PCI_DEVICE_SUB(PCI_VENDOR_ID_CAVIUM, PCI_DEVID_OCTEONTX2_PTP, 2358c2ecf20Sopenharmony_ci PCI_VENDOR_ID_CAVIUM, 2368c2ecf20Sopenharmony_ci PCI_SUBSYS_DEVID_OCTX2_95MM_PTP) }, 2378c2ecf20Sopenharmony_ci { 0, } 2388c2ecf20Sopenharmony_ci}; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistruct pci_driver ptp_driver = { 2418c2ecf20Sopenharmony_ci .name = DRV_NAME, 2428c2ecf20Sopenharmony_ci .id_table = ptp_id_table, 2438c2ecf20Sopenharmony_ci .probe = ptp_probe, 2448c2ecf20Sopenharmony_ci .remove = ptp_remove, 2458c2ecf20Sopenharmony_ci}; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ciint rvu_mbox_handler_ptp_op(struct rvu *rvu, struct ptp_req *req, 2488c2ecf20Sopenharmony_ci struct ptp_rsp *rsp) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci int err = 0; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci /* This function is the PTP mailbox handler invoked when 2538c2ecf20Sopenharmony_ci * called by AF consumers/netdev drivers via mailbox mechanism. 2548c2ecf20Sopenharmony_ci * It is used by netdev driver to get the PTP clock and to set 2558c2ecf20Sopenharmony_ci * frequency adjustments. Since mailbox can be called without 2568c2ecf20Sopenharmony_ci * notion of whether the driver is bound to ptp device below 2578c2ecf20Sopenharmony_ci * validation is needed as first step. 2588c2ecf20Sopenharmony_ci */ 2598c2ecf20Sopenharmony_ci if (!rvu->ptp) 2608c2ecf20Sopenharmony_ci return -ENODEV; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci switch (req->op) { 2638c2ecf20Sopenharmony_ci case PTP_OP_ADJFINE: 2648c2ecf20Sopenharmony_ci err = ptp_adjfine(rvu->ptp, req->scaled_ppm); 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci case PTP_OP_GET_CLOCK: 2678c2ecf20Sopenharmony_ci err = ptp_get_clock(rvu->ptp, &rsp->clk); 2688c2ecf20Sopenharmony_ci break; 2698c2ecf20Sopenharmony_ci default: 2708c2ecf20Sopenharmony_ci err = -EINVAL; 2718c2ecf20Sopenharmony_ci break; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci return err; 2758c2ecf20Sopenharmony_ci} 276