162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright 2013-2016 Freescale Semiconductor Inc. 462306a36Sopenharmony_ci * Copyright 2016-2018 NXP 562306a36Sopenharmony_ci * Copyright 2020 NXP 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/of.h> 1062306a36Sopenharmony_ci#include <linux/of_address.h> 1162306a36Sopenharmony_ci#include <linux/fsl/mc.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "dpaa2-ptp.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_cistatic int dpaa2_ptp_enable(struct ptp_clock_info *ptp, 1662306a36Sopenharmony_ci struct ptp_clock_request *rq, int on) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci struct ptp_qoriq *ptp_qoriq = container_of(ptp, struct ptp_qoriq, caps); 1962306a36Sopenharmony_ci struct fsl_mc_device *mc_dev; 2062306a36Sopenharmony_ci struct device *dev; 2162306a36Sopenharmony_ci u32 mask = 0; 2262306a36Sopenharmony_ci u32 bit; 2362306a36Sopenharmony_ci int err; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci dev = ptp_qoriq->dev; 2662306a36Sopenharmony_ci mc_dev = to_fsl_mc_device(dev); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci switch (rq->type) { 2962306a36Sopenharmony_ci case PTP_CLK_REQ_EXTTS: 3062306a36Sopenharmony_ci switch (rq->extts.index) { 3162306a36Sopenharmony_ci case 0: 3262306a36Sopenharmony_ci bit = DPRTC_EVENT_ETS1; 3362306a36Sopenharmony_ci break; 3462306a36Sopenharmony_ci case 1: 3562306a36Sopenharmony_ci bit = DPRTC_EVENT_ETS2; 3662306a36Sopenharmony_ci break; 3762306a36Sopenharmony_ci default: 3862306a36Sopenharmony_ci return -EINVAL; 3962306a36Sopenharmony_ci } 4062306a36Sopenharmony_ci if (on) 4162306a36Sopenharmony_ci extts_clean_up(ptp_qoriq, rq->extts.index, false); 4262306a36Sopenharmony_ci break; 4362306a36Sopenharmony_ci case PTP_CLK_REQ_PPS: 4462306a36Sopenharmony_ci bit = DPRTC_EVENT_PPS; 4562306a36Sopenharmony_ci break; 4662306a36Sopenharmony_ci default: 4762306a36Sopenharmony_ci return -EOPNOTSUPP; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci err = dprtc_get_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, 5162306a36Sopenharmony_ci DPRTC_IRQ_INDEX, &mask); 5262306a36Sopenharmony_ci if (err < 0) { 5362306a36Sopenharmony_ci dev_err(dev, "dprtc_get_irq_mask(): %d\n", err); 5462306a36Sopenharmony_ci return err; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (on) 5862306a36Sopenharmony_ci mask |= bit; 5962306a36Sopenharmony_ci else 6062306a36Sopenharmony_ci mask &= ~bit; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci err = dprtc_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, 6362306a36Sopenharmony_ci DPRTC_IRQ_INDEX, mask); 6462306a36Sopenharmony_ci if (err < 0) { 6562306a36Sopenharmony_ci dev_err(dev, "dprtc_set_irq_mask(): %d\n", err); 6662306a36Sopenharmony_ci return err; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return 0; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic const struct ptp_clock_info dpaa2_ptp_caps = { 7362306a36Sopenharmony_ci .owner = THIS_MODULE, 7462306a36Sopenharmony_ci .name = "DPAA2 PTP Clock", 7562306a36Sopenharmony_ci .max_adj = 512000, 7662306a36Sopenharmony_ci .n_alarm = 2, 7762306a36Sopenharmony_ci .n_ext_ts = 2, 7862306a36Sopenharmony_ci .n_per_out = 3, 7962306a36Sopenharmony_ci .n_pins = 0, 8062306a36Sopenharmony_ci .pps = 1, 8162306a36Sopenharmony_ci .adjfine = ptp_qoriq_adjfine, 8262306a36Sopenharmony_ci .adjtime = ptp_qoriq_adjtime, 8362306a36Sopenharmony_ci .gettime64 = ptp_qoriq_gettime, 8462306a36Sopenharmony_ci .settime64 = ptp_qoriq_settime, 8562306a36Sopenharmony_ci .enable = dpaa2_ptp_enable, 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic irqreturn_t dpaa2_ptp_irq_handler_thread(int irq, void *priv) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct ptp_qoriq *ptp_qoriq = priv; 9162306a36Sopenharmony_ci struct ptp_clock_event event; 9262306a36Sopenharmony_ci struct fsl_mc_device *mc_dev; 9362306a36Sopenharmony_ci struct device *dev; 9462306a36Sopenharmony_ci u32 status = 0; 9562306a36Sopenharmony_ci int err; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci dev = ptp_qoriq->dev; 9862306a36Sopenharmony_ci mc_dev = to_fsl_mc_device(dev); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci err = dprtc_get_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle, 10162306a36Sopenharmony_ci DPRTC_IRQ_INDEX, &status); 10262306a36Sopenharmony_ci if (unlikely(err)) { 10362306a36Sopenharmony_ci dev_err(dev, "dprtc_get_irq_status err %d\n", err); 10462306a36Sopenharmony_ci return IRQ_NONE; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (status & DPRTC_EVENT_PPS) { 10862306a36Sopenharmony_ci event.type = PTP_CLOCK_PPS; 10962306a36Sopenharmony_ci ptp_clock_event(ptp_qoriq->clock, &event); 11062306a36Sopenharmony_ci } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (status & DPRTC_EVENT_ETS1) 11362306a36Sopenharmony_ci extts_clean_up(ptp_qoriq, 0, true); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci if (status & DPRTC_EVENT_ETS2) 11662306a36Sopenharmony_ci extts_clean_up(ptp_qoriq, 1, true); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci err = dprtc_clear_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle, 11962306a36Sopenharmony_ci DPRTC_IRQ_INDEX, status); 12062306a36Sopenharmony_ci if (unlikely(err)) { 12162306a36Sopenharmony_ci dev_err(dev, "dprtc_clear_irq_status err %d\n", err); 12262306a36Sopenharmony_ci return IRQ_NONE; 12362306a36Sopenharmony_ci } 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci return IRQ_HANDLED; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic int dpaa2_ptp_probe(struct fsl_mc_device *mc_dev) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct device *dev = &mc_dev->dev; 13162306a36Sopenharmony_ci struct ptp_qoriq *ptp_qoriq; 13262306a36Sopenharmony_ci struct device_node *node; 13362306a36Sopenharmony_ci void __iomem *base; 13462306a36Sopenharmony_ci int err; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci ptp_qoriq = devm_kzalloc(dev, sizeof(*ptp_qoriq), GFP_KERNEL); 13762306a36Sopenharmony_ci if (!ptp_qoriq) 13862306a36Sopenharmony_ci return -ENOMEM; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci err = fsl_mc_portal_allocate(mc_dev, 0, &mc_dev->mc_io); 14162306a36Sopenharmony_ci if (err) { 14262306a36Sopenharmony_ci if (err == -ENXIO) 14362306a36Sopenharmony_ci err = -EPROBE_DEFER; 14462306a36Sopenharmony_ci else 14562306a36Sopenharmony_ci dev_err(dev, "fsl_mc_portal_allocate err %d\n", err); 14662306a36Sopenharmony_ci goto err_exit; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci err = dprtc_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, 15062306a36Sopenharmony_ci &mc_dev->mc_handle); 15162306a36Sopenharmony_ci if (err) { 15262306a36Sopenharmony_ci dev_err(dev, "dprtc_open err %d\n", err); 15362306a36Sopenharmony_ci goto err_free_mcp; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ptp_qoriq->dev = dev; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci node = of_find_compatible_node(NULL, NULL, "fsl,dpaa2-ptp"); 15962306a36Sopenharmony_ci if (!node) { 16062306a36Sopenharmony_ci err = -ENODEV; 16162306a36Sopenharmony_ci goto err_close; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci dev->of_node = node; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci base = of_iomap(node, 0); 16762306a36Sopenharmony_ci if (!base) { 16862306a36Sopenharmony_ci err = -ENOMEM; 16962306a36Sopenharmony_ci goto err_put; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci err = fsl_mc_allocate_irqs(mc_dev); 17362306a36Sopenharmony_ci if (err) { 17462306a36Sopenharmony_ci dev_err(dev, "MC irqs allocation failed\n"); 17562306a36Sopenharmony_ci goto err_unmap; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ptp_qoriq->irq = mc_dev->irqs[0]->virq; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci err = request_threaded_irq(ptp_qoriq->irq, NULL, 18162306a36Sopenharmony_ci dpaa2_ptp_irq_handler_thread, 18262306a36Sopenharmony_ci IRQF_NO_SUSPEND | IRQF_ONESHOT, 18362306a36Sopenharmony_ci dev_name(dev), ptp_qoriq); 18462306a36Sopenharmony_ci if (err < 0) { 18562306a36Sopenharmony_ci dev_err(dev, "devm_request_threaded_irq(): %d\n", err); 18662306a36Sopenharmony_ci goto err_free_mc_irq; 18762306a36Sopenharmony_ci } 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci err = dprtc_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, 19062306a36Sopenharmony_ci DPRTC_IRQ_INDEX, 1); 19162306a36Sopenharmony_ci if (err < 0) { 19262306a36Sopenharmony_ci dev_err(dev, "dprtc_set_irq_enable(): %d\n", err); 19362306a36Sopenharmony_ci goto err_free_threaded_irq; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci err = ptp_qoriq_init(ptp_qoriq, base, &dpaa2_ptp_caps); 19762306a36Sopenharmony_ci if (err) 19862306a36Sopenharmony_ci goto err_free_threaded_irq; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci dpaa2_phc_index = ptp_qoriq->phc_index; 20162306a36Sopenharmony_ci dpaa2_ptp = ptp_qoriq; 20262306a36Sopenharmony_ci dev_set_drvdata(dev, ptp_qoriq); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cierr_free_threaded_irq: 20762306a36Sopenharmony_ci free_irq(ptp_qoriq->irq, ptp_qoriq); 20862306a36Sopenharmony_cierr_free_mc_irq: 20962306a36Sopenharmony_ci fsl_mc_free_irqs(mc_dev); 21062306a36Sopenharmony_cierr_unmap: 21162306a36Sopenharmony_ci iounmap(base); 21262306a36Sopenharmony_cierr_put: 21362306a36Sopenharmony_ci of_node_put(node); 21462306a36Sopenharmony_cierr_close: 21562306a36Sopenharmony_ci dprtc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); 21662306a36Sopenharmony_cierr_free_mcp: 21762306a36Sopenharmony_ci fsl_mc_portal_free(mc_dev->mc_io); 21862306a36Sopenharmony_cierr_exit: 21962306a36Sopenharmony_ci return err; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void dpaa2_ptp_remove(struct fsl_mc_device *mc_dev) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci struct device *dev = &mc_dev->dev; 22562306a36Sopenharmony_ci struct ptp_qoriq *ptp_qoriq; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci ptp_qoriq = dev_get_drvdata(dev); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci dpaa2_phc_index = -1; 23062306a36Sopenharmony_ci ptp_qoriq_free(ptp_qoriq); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci fsl_mc_free_irqs(mc_dev); 23362306a36Sopenharmony_ci dprtc_close(mc_dev->mc_io, 0, mc_dev->mc_handle); 23462306a36Sopenharmony_ci fsl_mc_portal_free(mc_dev->mc_io); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic const struct fsl_mc_device_id dpaa2_ptp_match_id_table[] = { 23862306a36Sopenharmony_ci { 23962306a36Sopenharmony_ci .vendor = FSL_MC_VENDOR_FREESCALE, 24062306a36Sopenharmony_ci .obj_type = "dprtc", 24162306a36Sopenharmony_ci }, 24262306a36Sopenharmony_ci {} 24362306a36Sopenharmony_ci}; 24462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(fslmc, dpaa2_ptp_match_id_table); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic struct fsl_mc_driver dpaa2_ptp_drv = { 24762306a36Sopenharmony_ci .driver = { 24862306a36Sopenharmony_ci .name = KBUILD_MODNAME, 24962306a36Sopenharmony_ci .owner = THIS_MODULE, 25062306a36Sopenharmony_ci }, 25162306a36Sopenharmony_ci .probe = dpaa2_ptp_probe, 25262306a36Sopenharmony_ci .remove = dpaa2_ptp_remove, 25362306a36Sopenharmony_ci .match_id_table = dpaa2_ptp_match_id_table, 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cimodule_fsl_mc_driver(dpaa2_ptp_drv); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 25962306a36Sopenharmony_ciMODULE_DESCRIPTION("DPAA2 PTP Clock Driver"); 260