The PSE GPIO and TGPIO shares the PCI device, albeit being accessed at different MMIO BAR offset. Thus, to properly register device drivers and enable support of both functionalities, it depends on intel-ehl-gpio driver (CONFIG_MFD_INTEL_EHL_PSE_GPIO need to enable)to initialize hardware resources. note: this driver only supports Intel Atom x6000 series processors.
Signed-off-by: Zqiang <[email protected]> --- kernel/drivers/ptp/Kconfig | 24 ++ kernel/drivers/ptp/Makefile | 3 + kernel/drivers/ptp/pse-tgpio.c | 568 +++++++++++++++++++++++++++++++++ 3 files changed, 595 insertions(+) create mode 100644 kernel/drivers/ptp/pse-tgpio.c diff --git a/kernel/drivers/ptp/Kconfig b/kernel/drivers/ptp/Kconfig index 12e1e4b..177a389 100644 --- a/kernel/drivers/ptp/Kconfig +++ b/kernel/drivers/ptp/Kconfig @@ -17,4 +17,28 @@ config XENO_DRIVERS_PTP_PMC_TGPIO Intel Advanced Menu > PCH IO Configuration > Enabled Timed GPIO0 [Enable] Intel Advanced Menu > PCH IO Configuration > Enabled Timed GPIO1 [Enable] +config XENO_DRIVERS_PTP_PSE_TGPIO + tristate "Intel PSE Timed GPIO" + depends on XENO_DRIVERS_PTP + default n + help + This driver adds support for Intel PSE Timed-Aware GPIO (TGPIO) + note: BIOS configuration is required to properly assign the pin, + PSE TGPIO only for Intel Atom x6000 Series Processor. + To enable PSE Time-Aware GPIO, configure the following BIOS options: + Intel Advanced Menu > PCH IO Configuration > PSE configuration > Eclite + > Disable + Intel Advanced Menu > PCH IO Configuration > Security Configuration > + Force unlock on all GPIO pads > [Enabled] + Intel Advanced Menu > PCH IO Configuration > SerialIo Configuration > + SPI2 Controller > [Disabled] + Intel Advanced Menu > PCH IO Configuration > PSE configuration > SPI0 > + [Host owned with pin muxed] + Intel Advanced Menu > PCH IO Configuration > PSE configuration > + GPIO/TGPIO 0 > [Host owned with pin muxed] + Intel Advanced Menu > PCH IO Configuration > PSE configuration > + GPIO/TGPIO 0 MUX SELECTION > MID + Intel Advanced Menu > PCH IO Configuration > PSE configuration > + GPIO/TGPIO 0 Pin Selection > Check pins 28 and 27 + endmenu diff --git a/kernel/drivers/ptp/Makefile b/kernel/drivers/ptp/Makefile index aa1671c..3208427 100644 --- a/kernel/drivers/ptp/Makefile +++ b/kernel/drivers/ptp/Makefile @@ -3,3 +3,6 @@ xeno_ptp-y := ptp.o obj-$(CONFIG_XENO_DRIVERS_PTP_PMC_TGPIO) += xeno_pmc-tgpio.o xeno_pmc-tgpio-y := pmc-tgpio.o + +obj-$(CONFIG_XENO_DRIVERS_PTP_PSE_TGPIO) += xeno_pse-tgpio.o +xeno_pse-tgpio-y := pse-tgpio.o diff --git a/kernel/drivers/ptp/pse-tgpio.c b/kernel/drivers/ptp/pse-tgpio.c new file mode 100644 index 0000000..c5604bc --- /dev/null +++ b/kernel/drivers/ptp/pse-tgpio.c @@ -0,0 +1,568 @@ +/** + * I/O handling lifted from drivers/ptp/ptp-intel-tgpio-plat.c + * Intel Timed GPIO Controller Driver + * + * Copyright (C) 2018 Intel Corporation + * Author: Felipe Balbi <[email protected]> + * + * RTDM integration by: + * Copyright (C) 2022 Zqiang <[email protected]> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ +#include <linux/acpi.h> +#include <linux/bitops.h> +#include <linux/gpio.h> +#include <linux/io-64-nonatomic-lo-hi.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/mfd/core.h> +#include <linux/pm_runtime.h> +#include "ptp.h" + +#define TGPIOCTL(n) (((n) * 0x40) + 0x00) +#define TGPIOCOMPV31_0(n) (((n) * 0x40) + 0x04) +#define TGPIOCOMPV63_32(n) (((n) * 0x40) + 0x08) +#define TGPIOPIV31_0(n) (((n) * 0x40) + 0x0c) +#define TGPIOPIV63_32(n) (((n) * 0x40) + 0x10) +#define TGPIOTCV31_0(n) (((n) * 0x40) + 0x14) +#define TGPIOTCV63_32(n) (((n) * 0x40) + 0x18) +#define TGPIOECCV31_0(n) (((n) * 0x40) + 0x1c) +#define TGPIOECCV63_32(n) (((n) * 0x40) + 0x20) +#define TGPIOEC31_0(n) (((n) * 0x40) + 0x24) +#define TGPIOEC63_32(n) (((n) * 0x40) + 0x28) + +#define TGPIOINTRCTL 0x0500 +#define TGPIORIS 0x0504 +#define TGPIOMSC 0x0508 +#define TGPIOMIS 0x050c +#define TGPIOICR 0x0510 +#define TGPIO_CLK_SEL 0x0514 +#define TGPIO_TS_SEL_0 0x0520 +#define TGPIO_TS_SEL_1 0x0524 +#define TMT_CLK_SEL 0x0528 +#define TGPIO_TSC_CTL 0x0530 +#define TGPIO_TSC_STATUS 0x0534 +#define TMTCTL_TSG 0x0600 +#define TMTR_TSG 0x0604 +#define TMTL_TSG 0x0608 +#define TMTH_TSG 0x060C +#define TIMINCA_TSG 0x0610 +#define TIMADJ_TSG 0x0614 +#define LXTS_TMT_LOW_TSG 0x0618 +#define LXTS_TMT_HIGH_TSG 0x061C +#define LXTS_ART_LOW_TSG 0x0620 +#define LXTS_ART_HIGH_TSG 0x0624 +#define RXTS_TMT_LOW_TSG 0x0628 +#define RXTS_TMT_HIGH_TSG 0x062C +#define TMTCTL_GLOBAL 0x0640 +#define TMTR_GLOBAL 0x0644 +#define TMTL_GLOBAL 0x0648 +#define TMTH_GLOBAL 0x064C +#define TIMINCA_GLOBAL 0x0650 +#define TIMADJ_GLOBAL 0x0654 +#define LXTS_TMT_LOW_GLOBAL 0x0658 +#define LXTS_TMT_HIGH_GLOBAL 0x065C +#define LXTS_ART_LOW_GLOBAL 0x0660 +#define LXTS_ART_HIGH_GLOBAL 0x0664 +#define RXTS_TMT_LOW_GLOBAL 0x0668 +#define RXTS_TMT_HIGH_GLOBAL 0x066C +#define TMTCTL_WORKING 0x0680 +#define TMTR_WORKING 0x0684 +#define TMTL_WORKING 0x0688 +#define TMTH_WORKING 0x068C +#define TIMINCA_WORKING 0x0690 +#define TIMADJ_WORKING 0x0694 +#define LXTS_TMT_LOW_WORKING 0x0698 +#define LXTS_TMT_HIGH_WORKING 0x069C +#define LXTS_ART_LOW_WORKING 0x06A0 +#define LXTS_ART_HIGH_WORKING 0x06A4 +#define RXTS_TMT_LOW_WORKING 0x06A8 +#define RXTS_TMT_HIGH_WORKING 0x06AC + +/* Control Register */ +#define TGPIOCTL_EN BIT(0) +#define TGPIOCTL_DIR BIT(1) +#define TGPIOCTL_EP GENMASK(3, 2) +#define TGPIOCTL_EP_RISING_EDGE (0 << 2) +#define TGPIOCTL_EP_FALLING_EDGE (1 << 2) +#define TGPIOCTL_EP_TOGGLE_EDGE (2 << 2) +#define TGPIOCTL_PM BIT(4) +#define TGPIOCTL_PWS GENMASK(8, 5) +#define TGPIOCTL_PWS_N(n) (((n) & 0xf) << 5) +#define TGPIOCTL_ICS BIT(9) +#define TGPIOCTL_TSCS BIT(10) +#define TGPIOCTL_OEC BIT(12) +#define TGPIOCTL_FIT BIT(13) +#define TGPIOCTL_IEC GENMASK(15, 14) +#define TGPIOCTL_IEC_EC BIT(14) +#define TGPIOCTL_ECC BIT(16) +#define TGPIOCTL_PSL GENMASK(24, 17) +#define TGPIOCTL_TS GENMASK(29, 28) +#define TGPIOCTL_TS_TMT0 (0 << 28) +#define TGPIOCTL_TS_TMT1 (1 << 28) +#define TGPIOCTL_TS_TMT2 (2 << 28) +#define TGPIOCTL_TS_LART (3 << 28) + +/* Timed GPIO Interrupt Status/Mask/Clear registers */ +#define TGPIOINT_TMT_NSEC_WRAP_GLOBAL BIT(25) +#define TGPIOINT_TMT_NSEC_WRAP_WORKING BIT(24) +#define TGPIOINT_TMT_NSEC_WRAP_TSG BIT(23) +#define TGPIOINT_TADJ_TMT_GLOBAL_CMPLT BIT(22) +#define TGPIOINT_TADJ_TMT_WORKING_CMPLT BIT(21) +#define TGPIOINT_TADJ_TMT_TSG_CMPLT BIT(20) +#define TGPIOINT_EVENT_INTERRUPT(n) BIT((n)) + +/* Tunable Monotonous Timer Control Register */ +#define TMTCTL_TMT_ENABLE BIT(0) + +#define NSECS_PER_SEC 1000000000 +#define TGPIO_MAX_ADJ_TIME 999999900 + +#define TGPIO_MAX_PIN 20 + +#define TGPIO_D0I3C 1000 +#define TGPIO_CGSR 1004 +#define TGPIO_D0I3_CIP BIT(0) +#define TGPIO_D0I3_IR BIT(1) +#define TGPIO_D0I3_EN BIT(2) +#define TGPIO_D0I3_RR BIT(3) +#define TGPIO_CGSR_CG BIT(16) + +struct intel_tgpio { + struct rt_ptp_clock_info info; + struct rt_ptp_clock *clock; + + rtdm_lock_t lock; + struct device *dev; + void __iomem *base; + u32 irq_status; + u32 irq_mask; + u32 saved_ctl_regs[TGPIO_MAX_PIN]; + u64 saved_piv_regs[TGPIO_MAX_PIN]; + rtdm_irq_t handle; + bool is_interrupt; +}; + +#define to_intel_tgpio(i) (container_of((i), struct intel_tgpio, info)) + +#define ts64_to_ptp_clock_time(x) ((struct rt_ptp_clock_time){.sec = (x).tv_sec, \ + .nsec = (x).tv_nsec}) + +static inline u64 to_intel_tgpio_time(struct rt_ptp_clock_time *t) +{ + return t->sec * NSECS_PER_SEC + t->nsec; +} + +static inline u64 intel_tgpio_readq(void __iomem *base, u32 offset) +{ + return lo_hi_readq(base + offset); +} + +static inline void intel_tgpio_writeq(void __iomem *base, u32 offset, u64 v) +{ + return lo_hi_writeq(v, base + offset); +} + +static inline u32 intel_tgpio_readl(void __iomem *base, u32 offset) +{ + return readl(base + offset); +} + +static inline void intel_tgpio_writel(void __iomem *base, u32 offset, u32 value) +{ + writel(value, base + offset); +} + +static void intel_tgpio_get_time(struct intel_tgpio *tgpio, + struct timespec64 *ts) +{ + (void) intel_tgpio_readl(tgpio->base, TMTR_TSG); + ts->tv_nsec = intel_tgpio_readl(tgpio->base, TMTL_TSG); + ts->tv_sec = intel_tgpio_readl(tgpio->base, TMTH_TSG); +} + +#define TGPIO_PIN(n) \ + { \ + .name = "pin" __stringify((n)), \ + .index = (n), \ + .chan = (n), \ + .func = RT_PTP_PF_NONE, \ + } + +static struct rt_ptp_pin_desc intel_tgpio_pin_config[] = { + TGPIO_PIN(0), + TGPIO_PIN(1), + TGPIO_PIN(2), + TGPIO_PIN(3), + TGPIO_PIN(4), + TGPIO_PIN(5), + TGPIO_PIN(6), + TGPIO_PIN(7), + TGPIO_PIN(8), + TGPIO_PIN(9), + TGPIO_PIN(10), + TGPIO_PIN(11), + TGPIO_PIN(12), + TGPIO_PIN(13), + TGPIO_PIN(14), + TGPIO_PIN(15), + TGPIO_PIN(16), + TGPIO_PIN(17), + TGPIO_PIN(18), + TGPIO_PIN(19), +}; + +static int intel_tgpio_config_input(struct intel_tgpio *tgpio, + struct rt_ptp_extts_request *extts, int on) +{ + unsigned int index = extts->index; + u32 offset; + u32 ctrl; + + offset = TGPIOCTL(index); + ctrl = intel_tgpio_readl(tgpio->base, offset); + ctrl &= ~(TGPIOCTL_TS | TGPIOCTL_EP | TGPIOCTL_DIR | + TGPIOCTL_PWS | TGPIOCTL_IEC_EC | TGPIOCTL_ICS); + + if (on) { + int rising_cap, falling_cap; + + tgpio->irq_mask |= TGPIOINT_EVENT_INTERRUPT(index); + ctrl |= TGPIOCTL_DIR | TGPIOCTL_TS_TMT0; + + /* To enable for Input Event Counter & Input Event Control */ + /* TODO: temporarily using rsv0 to store the counter */ + if ((extts->flags & RT_PTP_EVENT_COUNTER_MODE) && extts->rsv[0]) + { + ctrl |= TGPIOCTL_IEC_EC; + ctrl |= TGPIOCTL_ICS; + + intel_tgpio_writel(tgpio->base, TGPIOCOMPV31_0(index), + extts->rsv[0]); + intel_tgpio_writel(tgpio->base, TGPIOCOMPV63_32(index), + 0); + } + + /* To enable Event Polarity for inout mode, */ + /* default to capture both rising & failling */ + rising_cap = extts->flags & RT_PTP_RISING_EDGE; + falling_cap = extts->flags & RT_PTP_FALLING_EDGE; + + if (rising_cap && !falling_cap) + ctrl |= TGPIOCTL_EP_RISING_EDGE; + else if (!rising_cap && falling_cap) + ctrl |= TGPIOCTL_EP_FALLING_EDGE; + else + ctrl |= TGPIOCTL_EP_TOGGLE_EDGE; + + /* gotta program all other bits before EN bit is set */ + intel_tgpio_writel(tgpio->base, offset, ctrl); + + ctrl |= TGPIOCTL_EN; + } else { + ctrl &= ~TGPIOCTL_EN; + intel_tgpio_writel(tgpio->base, offset, ctrl); + + intel_tgpio_writeq(tgpio->base, TGPIOCOMPV31_0(index), 0); + tgpio->irq_mask &= ~TGPIOINT_EVENT_INTERRUPT(index); + ctrl = 0x0; + } + + /* For everytime we mask the interrupt, we need to */ + /* flush the corresponding Raw Interrupt Status */ + intel_tgpio_writel(tgpio->base, TGPIOMSC, tgpio->irq_mask); + intel_tgpio_writel(tgpio->base, TGPIOICR, tgpio->irq_mask); + intel_tgpio_writel(tgpio->base, offset, ctrl); + + return 0; +} + +static int intel_tgpio_config_output(struct intel_tgpio *tgpio, + struct rt_ptp_perout_request *perout, int on) +{ + unsigned int index = perout->index; + u32 offset; + u32 ctrl; + + offset = TGPIOCTL(index); + ctrl = intel_tgpio_readl(tgpio->base, offset); + ctrl &= ~(TGPIOCTL_TS | TGPIOCTL_EP | TGPIOCTL_DIR | + TGPIOCTL_PWS | TGPIOCTL_IEC_EC | TGPIOCTL_ICS); + + if (on || (perout->flags & RT_PTP_PEROUT_ONE_SHOT)) { + struct rt_ptp_clock_time *period = &perout->period; + struct rt_ptp_clock_time *start = &perout->start; + + ctrl |= TGPIOCTL_TS_TMT0 | TGPIOCTL_ECC | TGPIOCTL_PWS_N(17); + + if (perout->flags & RT_PTP_PEROUT_ONE_SHOT) + ctrl &= ~TGPIOCTL_PM; + else + ctrl |= TGPIOCTL_PM; + + if (!(perout->flags & RT_PTP_PEROUT_FREQ_ADJ)) { + start->nsec = ((start->nsec) / 10) * 10; + intel_tgpio_writel(tgpio->base, TGPIOCOMPV31_0(index), + start->nsec); + intel_tgpio_writel(tgpio->base, TGPIOCOMPV63_32(index), + start->sec); + } + intel_tgpio_writeq(tgpio->base, TGPIOPIV31_0(index), + to_intel_tgpio_time(period)); + + /* gotta program all other bits before EN bit is set */ + intel_tgpio_writel(tgpio->base, offset, ctrl); + + ctrl |= TGPIOCTL_EN; + } else { + ctrl &= ~TGPIOCTL_EN; + intel_tgpio_writel(tgpio->base, offset, ctrl); + + intel_tgpio_writeq(tgpio->base, TGPIOCOMPV31_0(index), 0); + intel_tgpio_writeq(tgpio->base, TGPIOPIV31_0(index), 0); + ctrl = 0x0; + } + + intel_tgpio_writel(tgpio->base, offset, ctrl); + + return 0; +} + +static int intel_tgpio_enable(struct rt_ptp_clock_info *info, + struct rt_ptp_clock_request *req, int on) +{ + struct intel_tgpio *tgpio = to_intel_tgpio(info); + rtdm_lockctx_t flags; + int ret = -EOPNOTSUPP; + + switch (req->type) { + case RT_PTP_CLK_REQ_EXTTS: + { + rtdm_lock_get_irqsave(&tgpio->lock, flags); + ret = intel_tgpio_config_input(tgpio, &req->extts, on); + rtdm_lock_put_irqrestore(&tgpio->lock, flags); + break; + } + case RT_PTP_CLK_REQ_PEROUT: + { + rtdm_lock_get_irqsave(&tgpio->lock, flags); + ret = intel_tgpio_config_output(tgpio, &req->perout, on); + rtdm_lock_put_irqrestore(&tgpio->lock, flags); + break; + } + default: + break; + } + + return ret; +} + +static int intel_tgpio_get_time_fn(ktime_t *device_time, + struct system_counterval_t *system_counter, void *_tgpio) +{ + struct intel_tgpio *tgpio = _tgpio; + struct timespec64 ts; + u64 cycles; + + intel_tgpio_get_time(tgpio, &ts); + *device_time = timespec64_to_ktime(ts); + cycles = read_art(); + *system_counter = convert_art_to_tsc(cycles); + return 0; +} + +static int intel_tgpio_getcrosststamp(struct rt_ptp_clock_info *info, + struct system_device_crosststamp *cts) +{ + struct intel_tgpio *tgpio = to_intel_tgpio(info); + + return get_device_system_crosststamp(intel_tgpio_get_time_fn, tgpio, + NULL, cts); +} + +static int intel_tgpio_counttstamp(struct rt_ptp_clock_info *info, + struct rt_ptp_event_count_tstamp *count) +{ + struct intel_tgpio *tgpio = to_intel_tgpio(info); + rtdm_lockctx_t flags; + + rtdm_lock_get_irqsave(&tgpio->lock, flags); + /* Reading lower 32-bit word of Time Capture Value (TCV) loads */ + /* the event time and event count capture */ + count->device_time.nsec = intel_tgpio_readl(tgpio->base, + TGPIOTCV31_0(count->index)); + count->event_count = intel_tgpio_readl(tgpio->base, + TGPIOECCV63_32(count->index)); + count->event_count <<= 32; + count->event_count |= intel_tgpio_readl(tgpio->base, + TGPIOECCV31_0(count->index)); + count->device_time.sec = intel_tgpio_readl(tgpio->base, + TGPIOTCV63_32(count->index)); + rtdm_lock_put_irqrestore(&tgpio->lock, flags); + return 0; +} + +static int intel_tgpio_verify(struct rt_ptp_clock_info *ptp, unsigned int pin, + enum rt_ptp_pin_function func, unsigned int chan) +{ + return 0; +} + +static const struct rt_ptp_clock_info intel_tgpio_info = { + .owner = THIS_MODULE, + .name = "Intel PSE TGPIO", + .max_adj = 50000000, + .n_pins = 20, + .n_ext_ts = 20, + .n_per_out = 20, + .pin_config = intel_tgpio_pin_config, + .enable = intel_tgpio_enable, + .getcrosststamp = intel_tgpio_getcrosststamp, + .counttstamp = intel_tgpio_counttstamp, + .verify = intel_tgpio_verify, +}; + +static void intel_tgpio_irq_fn(void *_tgpio) +{ + struct intel_tgpio *tgpio = _tgpio; + unsigned long irq_status; + unsigned long pin; + + rtdm_lock_get(&tgpio->lock); + + irq_status = tgpio->irq_status; + for_each_set_bit(pin, &irq_status, BITS_PER_LONG) { + struct rt_ptp_event event; + + event.type = RT_PTP_CLK_REQ_EXTTS; + event.index = pin; + event.timestamp = intel_tgpio_readq(tgpio->base, + TGPIOTCV31_0(pin)); + + rt_ptp_clock_event(tgpio->clock, &event); + } + + /* + * Clear RIS (Raw Interrupt Status) after we mask the MIS for any + * pending interrupts in RIS through ICR register. + */ + intel_tgpio_writel(tgpio->base, TGPIOMSC, tgpio->irq_mask); + intel_tgpio_writel(tgpio->base, TGPIOICR, tgpio->irq_mask); + rtdm_lock_put(&tgpio->lock); +} + +static int intel_tgpio_irq(rtdm_irq_t *handle) +{ + struct intel_tgpio *tgpio = rtdm_irq_get_arg(handle, + struct intel_tgpio); + u32 intr; + + intr = intel_tgpio_readl(tgpio->base, TGPIOMIS); + if (intr) { + tgpio->irq_status = intr; + intel_tgpio_writel(tgpio->base, TGPIOMSC, 0x00); + intel_tgpio_writel(tgpio->base, TGPIOICR, intr); + intel_tgpio_irq_fn(tgpio); + + } + return RTDM_IRQ_HANDLED; +} + +static void intel_tgpio_disable_all_pins(struct intel_tgpio *tgpio) +{ + int i; + + for (i = 0; i < 20; i++) + intel_tgpio_writel(tgpio->base, TGPIOCTL(i), 0); +} + +static int intel_tgpio_probe(struct platform_device *pdev) +{ + struct intel_tgpio *tgpio; + struct resource *res; + int ret; + int irq; + + tgpio = devm_kzalloc(&pdev->dev, sizeof(*tgpio), GFP_KERNEL); + if (!tgpio) + return -ENOMEM; + + tgpio->dev = &pdev->dev; + tgpio->info = intel_tgpio_info; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -ENODEV; + + tgpio->base = devm_ioremap_resource(&pdev->dev, res); + + /* make sure all pins are disabled */ + intel_tgpio_disable_all_pins(tgpio); + + /* mask all interrupt events */ + intel_tgpio_writel(tgpio->base, TGPIOMIS, 0x00); + + /* enable TMT0 */ + intel_tgpio_writel(tgpio->base, TMTCTL_TSG, TMTCTL_TMT_ENABLE); + + rtdm_lock_init(&tgpio->lock); + + platform_set_drvdata(pdev, tgpio); + + tgpio->clock = rt_ptp_clock_register(&tgpio->info, "pse-tgpio"); + if (IS_ERR(tgpio->clock)) + return PTR_ERR(tgpio->clock); + + ret = rtdm_irq_request(&tgpio->handle, irq, intel_tgpio_irq, + IRQF_TRIGGER_RISING | IRQF_SHARED, + dev_name(&pdev->dev), tgpio); + if (ret) + goto err0; + + tgpio->is_interrupt = true; + pm_runtime_get_sync(tgpio->dev->parent); + return 0; + +err0: + rt_ptp_clock_unregister(tgpio->clock); + return ret; +} + +static int intel_tgpio_remove(struct platform_device *pdev) +{ + struct intel_tgpio *tgpio = platform_get_drvdata(pdev); + + pm_runtime_put(tgpio->dev->parent); + if (tgpio->is_interrupt) + rtdm_irq_free(&tgpio->handle); + rt_ptp_clock_unregister(tgpio->clock); + + return 0; +} + +static struct platform_driver intel_tgpio_driver = { + .driver = { + .name = "intel-ehl-tgpio", + }, + .probe = intel_tgpio_probe, + .remove = intel_tgpio_remove, +}; + +module_platform_driver(intel_tgpio_driver); -- 2.25.1
