18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2013-2016 Freescale Semiconductor Inc. 48c2ecf20Sopenharmony_ci * Copyright 2016-2018 NXP 58c2ecf20Sopenharmony_ci * Copyright 2020 NXP 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/of.h> 108c2ecf20Sopenharmony_ci#include <linux/of_address.h> 118c2ecf20Sopenharmony_ci#include <linux/msi.h> 128c2ecf20Sopenharmony_ci#include <linux/fsl/mc.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "dpaa2-ptp.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistatic int dpaa2_ptp_enable(struct ptp_clock_info *ptp, 178c2ecf20Sopenharmony_ci struct ptp_clock_request *rq, int on) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps); 208c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev; 218c2ecf20Sopenharmony_ci struct device *dev; 228c2ecf20Sopenharmony_ci u32 mask = 0; 238c2ecf20Sopenharmony_ci u32 bit; 248c2ecf20Sopenharmony_ci int err; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci dev = ptp_qoriq->dev; 278c2ecf20Sopenharmony_ci mc_dev = to_fsl_mc_device(dev); 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci switch (rq->type) { 308c2ecf20Sopenharmony_ci case PTP_CLK_REQ_EXTTS: 318c2ecf20Sopenharmony_ci switch (rq->extts.index) { 328c2ecf20Sopenharmony_ci case 0: 338c2ecf20Sopenharmony_ci bit = DPRTC_EVENT_ETS1; 348c2ecf20Sopenharmony_ci break; 358c2ecf20Sopenharmony_ci case 1: 368c2ecf20Sopenharmony_ci bit = DPRTC_EVENT_ETS2; 378c2ecf20Sopenharmony_ci break; 388c2ecf20Sopenharmony_ci default: 398c2ecf20Sopenharmony_ci return -EINVAL; 408c2ecf20Sopenharmony_ci } 418c2ecf20Sopenharmony_ci if (on) 428c2ecf20Sopenharmony_ci extts_clean_up(ptp_qoriq, rq->extts.index, false); 438c2ecf20Sopenharmony_ci break; 448c2ecf20Sopenharmony_ci case PTP_CLK_REQ_PPS: 458c2ecf20Sopenharmony_ci bit = DPRTC_EVENT_PPS; 468c2ecf20Sopenharmony_ci break; 478c2ecf20Sopenharmony_ci default: 488c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 498c2ecf20Sopenharmony_ci } 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci err = dprtc_get_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, 528c2ecf20Sopenharmony_ci DPRTC_IRQ_INDEX, &mask); 538c2ecf20Sopenharmony_ci if (err < 0) { 548c2ecf20Sopenharmony_ci dev_err(dev, "dprtc_get_irq_mask(): %d\n", err); 558c2ecf20Sopenharmony_ci return err; 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (on) 598c2ecf20Sopenharmony_ci mask |= bit; 608c2ecf20Sopenharmony_ci else 618c2ecf20Sopenharmony_ci mask &= ~bit; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci err = dprtc_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, 648c2ecf20Sopenharmony_ci DPRTC_IRQ_INDEX, mask); 658c2ecf20Sopenharmony_ci if (err < 0) { 668c2ecf20Sopenharmony_ci dev_err(dev, "dprtc_set_irq_mask(): %d\n", err); 678c2ecf20Sopenharmony_ci return err; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci return 0; 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic const struct ptp_clock_info dpaa2_ptp_caps = { 748c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 758c2ecf20Sopenharmony_ci .name = "DPAA2 PTP Clock", 768c2ecf20Sopenharmony_ci .max_adj = 512000, 778c2ecf20Sopenharmony_ci .n_alarm = 2, 788c2ecf20Sopenharmony_ci .n_ext_ts = 2, 798c2ecf20Sopenharmony_ci .n_per_out = 3, 808c2ecf20Sopenharmony_ci .n_pins = 0, 818c2ecf20Sopenharmony_ci .pps = 1, 828c2ecf20Sopenharmony_ci .adjfine = ptp_qoriq_adjfine, 838c2ecf20Sopenharmony_ci .adjtime = ptp_qoriq_adjtime, 848c2ecf20Sopenharmony_ci .gettime64 = ptp_qoriq_gettime, 858c2ecf20Sopenharmony_ci .settime64 = ptp_qoriq_settime, 868c2ecf20Sopenharmony_ci .enable = dpaa2_ptp_enable, 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic irqreturn_t dpaa2_ptp_irq_handler_thread(int irq, void *priv) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct ptp_qoriq *ptp_qoriq = priv; 928c2ecf20Sopenharmony_ci struct ptp_clock_event event; 938c2ecf20Sopenharmony_ci struct fsl_mc_device *mc_dev; 948c2ecf20Sopenharmony_ci struct device *dev; 958c2ecf20Sopenharmony_ci u32 status = 0; 968c2ecf20Sopenharmony_ci int err; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci dev = ptp_qoriq->dev; 998c2ecf20Sopenharmony_ci mc_dev = to_fsl_mc_device(dev); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci err = dprtc_get_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle, 1028c2ecf20Sopenharmony_ci DPRTC_IRQ_INDEX, &status); 1038c2ecf20Sopenharmony_ci if (unlikely(err)) { 1048c2ecf20Sopenharmony_ci dev_err(dev, "dprtc_get_irq_status err %d\n", err); 1058c2ecf20Sopenharmony_ci return IRQ_NONE; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (status & DPRTC_EVENT_PPS) { 1098c2ecf20Sopenharmony_ci event.type = PTP_CLOCK_PPS; 1108c2ecf20Sopenharmony_ci ptp_clock_event(ptp_qoriq->clock, &event); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (status & DPRTC_EVENT_ETS1) 1148c2ecf20Sopenharmony_ci extts_clean_up(ptp_qoriq, 0, true); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci if (status & DPRTC_EVENT_ETS2) 1178c2ecf20Sopenharmony_ci extts_clean_up(ptp_qoriq, 1, true); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci err = dprtc_clear_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle, 1208c2ecf20Sopenharmony_ci DPRTC_IRQ_INDEX, status); 1218c2ecf20Sopenharmony_ci if (unlikely(err)) { 1228c2ecf20Sopenharmony_ci dev_err(dev, "dprtc_clear_irq_status err %d\n", err); 1238c2ecf20Sopenharmony_ci return IRQ_NONE; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int dpaa2_ptp_probe(struct fsl_mc_device *mc_dev) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct device *dev = &mc_dev->dev; 1328c2ecf20Sopenharmony_ci struct fsl_mc_device_irq *irq; 1338c2ecf20Sopenharmony_ci struct ptp_qoriq *ptp_qoriq; 1348c2ecf20Sopenharmony_ci struct device_node *node; 1358c2ecf20Sopenharmony_ci void __iomem *base; 1368c2ecf20Sopenharmony_ci int err; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci ptp_qoriq = devm_kzalloc(dev, sizeof(*ptp_qoriq), GFP_KERNEL); 1398c2ecf20Sopenharmony_ci if (!ptp_qoriq) 1408c2ecf20Sopenharmony_ci return -ENOMEM; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci err = fsl_mc_portal_allocate(mc_dev, 0, &mc_dev->mc_io); 1438c2ecf20Sopenharmony_ci if (err) { 1448c2ecf20Sopenharmony_ci if (err == -ENXIO) 1458c2ecf20Sopenharmony_ci err = -EPROBE_DEFER; 1468c2ecf20Sopenharmony_ci else 1478c2ecf20Sopenharmony_ci dev_err(dev, "fsl_mc_portal_allocate err %d\n", err); 1488c2ecf20Sopenharmony_ci goto err_exit; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci err = dprtc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, 1528c2ecf20Sopenharmony_ci &mc_dev->mc_handle); 1538c2ecf20Sopenharmony_ci if (err) { 1548c2ecf20Sopenharmony_ci dev_err(dev, "dprtc_open err %d\n", err); 1558c2ecf20Sopenharmony_ci goto err_free_mcp; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci ptp_qoriq->dev = dev; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "fsl,dpaa2-ptp"); 1618c2ecf20Sopenharmony_ci if (!node) { 1628c2ecf20Sopenharmony_ci err = -ENODEV; 1638c2ecf20Sopenharmony_ci goto err_close; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci dev->of_node = node; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci base = of_iomap(node, 0); 1698c2ecf20Sopenharmony_ci if (!base) { 1708c2ecf20Sopenharmony_ci err = -ENOMEM; 1718c2ecf20Sopenharmony_ci goto err_put; 1728c2ecf20Sopenharmony_ci } 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci err = fsl_mc_allocate_irqs(mc_dev); 1758c2ecf20Sopenharmony_ci if (err) { 1768c2ecf20Sopenharmony_ci dev_err(dev, "MC irqs allocation failed\n"); 1778c2ecf20Sopenharmony_ci goto err_unmap; 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci irq = mc_dev->irqs[0]; 1818c2ecf20Sopenharmony_ci ptp_qoriq->irq = irq->msi_desc->irq; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci err = request_threaded_irq(ptp_qoriq->irq, NULL, 1848c2ecf20Sopenharmony_ci dpaa2_ptp_irq_handler_thread, 1858c2ecf20Sopenharmony_ci IRQF_NO_SUSPEND | IRQF_ONESHOT, 1868c2ecf20Sopenharmony_ci dev_name(dev), ptp_qoriq); 1878c2ecf20Sopenharmony_ci if (err < 0) { 1888c2ecf20Sopenharmony_ci dev_err(dev, "devm_request_threaded_irq(): %d\n", err); 1898c2ecf20Sopenharmony_ci goto err_free_mc_irq; 1908c2ecf20Sopenharmony_ci } 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci err = dprtc_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, 1938c2ecf20Sopenharmony_ci DPRTC_IRQ_INDEX, 1); 1948c2ecf20Sopenharmony_ci if (err < 0) { 1958c2ecf20Sopenharmony_ci dev_err(dev, "dprtc_set_irq_enable(): %d\n", err); 1968c2ecf20Sopenharmony_ci goto err_free_threaded_irq; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci err = ptp_qoriq_init(ptp_qoriq, base, &dpaa2_ptp_caps); 2008c2ecf20Sopenharmony_ci if (err) 2018c2ecf20Sopenharmony_ci goto err_free_threaded_irq; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci dpaa2_phc_index = ptp_qoriq->phc_index; 2048c2ecf20Sopenharmony_ci dpaa2_ptp = ptp_qoriq; 2058c2ecf20Sopenharmony_ci dev_set_drvdata(dev, ptp_qoriq); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cierr_free_threaded_irq: 2108c2ecf20Sopenharmony_ci free_irq(ptp_qoriq->irq, ptp_qoriq); 2118c2ecf20Sopenharmony_cierr_free_mc_irq: 2128c2ecf20Sopenharmony_ci fsl_mc_free_irqs(mc_dev); 2138c2ecf20Sopenharmony_cierr_unmap: 2148c2ecf20Sopenharmony_ci iounmap(base); 2158c2ecf20Sopenharmony_cierr_put: 2168c2ecf20Sopenharmony_ci of_node_put(node); 2178c2ecf20Sopenharmony_cierr_close: 2188c2ecf20Sopenharmony_ci dprtc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); 2198c2ecf20Sopenharmony_cierr_free_mcp: 2208c2ecf20Sopenharmony_ci fsl_mc_portal_free(mc_dev->mc_io); 2218c2ecf20Sopenharmony_cierr_exit: 2228c2ecf20Sopenharmony_ci return err; 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic int dpaa2_ptp_remove(struct fsl_mc_device *mc_dev) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct device *dev = &mc_dev->dev; 2288c2ecf20Sopenharmony_ci struct ptp_qoriq *ptp_qoriq; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci ptp_qoriq = dev_get_drvdata(dev); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci dpaa2_phc_index = -1; 2338c2ecf20Sopenharmony_ci ptp_qoriq_free(ptp_qoriq); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci fsl_mc_free_irqs(mc_dev); 2368c2ecf20Sopenharmony_ci dprtc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); 2378c2ecf20Sopenharmony_ci fsl_mc_portal_free(mc_dev->mc_io); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci return 0; 2408c2ecf20Sopenharmony_ci} 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_cistatic const struct fsl_mc_device_id dpaa2_ptp_match_id_table[] = { 2438c2ecf20Sopenharmony_ci { 2448c2ecf20Sopenharmony_ci .vendor = FSL_MC_VENDOR_FREESCALE, 2458c2ecf20Sopenharmony_ci .obj_type = "dprtc", 2468c2ecf20Sopenharmony_ci }, 2478c2ecf20Sopenharmony_ci {} 2488c2ecf20Sopenharmony_ci}; 2498c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(fslmc, dpaa2_ptp_match_id_table); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic struct fsl_mc_driver dpaa2_ptp_drv = { 2528c2ecf20Sopenharmony_ci .driver = { 2538c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 2548c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 2558c2ecf20Sopenharmony_ci }, 2568c2ecf20Sopenharmony_ci .probe = dpaa2_ptp_probe, 2578c2ecf20Sopenharmony_ci .remove = dpaa2_ptp_remove, 2588c2ecf20Sopenharmony_ci .match_id_table = dpaa2_ptp_match_id_table, 2598c2ecf20Sopenharmony_ci}; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cimodule_fsl_mc_driver(dpaa2_ptp_drv); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2648c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DPAA2 PTP Clock Driver"); 265