162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * MPC5200 General Purpose Timer device driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2009 Secret Lab Technologies Ltd. 662306a36Sopenharmony_ci * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This file is a driver for the General Purpose Timer (gpt) devices 962306a36Sopenharmony_ci * found on the MPC5200 SoC. Each timer has an IO pin which can be used 1062306a36Sopenharmony_ci * for GPIO or can be used to raise interrupts. The timer function can 1162306a36Sopenharmony_ci * be used independently from the IO pin, or it can be used to control 1262306a36Sopenharmony_ci * output signals or measure input signals. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * This driver supports the GPIO and IRQ controller functions of the GPT 1562306a36Sopenharmony_ci * device. Timer functions are not yet supported. 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci * The timer gpt0 can be used as watchdog (wdt). If the wdt mode is used, 1862306a36Sopenharmony_ci * this prevents the use of any gpt0 gpt function (i.e. they will fail with 1962306a36Sopenharmony_ci * -EBUSY). Thus, the safety wdt function always has precedence over the gpt 2062306a36Sopenharmony_ci * function. If the kernel has been compiled with CONFIG_WATCHDOG_NOWAYOUT, 2162306a36Sopenharmony_ci * this means that gpt0 is locked in wdt mode until the next reboot - this 2262306a36Sopenharmony_ci * may be a requirement in safety applications. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * To use the GPIO function, the following two properties must be added 2562306a36Sopenharmony_ci * to the device tree node for the gpt device (typically in the .dts file 2662306a36Sopenharmony_ci * for the board): 2762306a36Sopenharmony_ci * gpio-controller; 2862306a36Sopenharmony_ci * #gpio-cells = < 2 >; 2962306a36Sopenharmony_ci * This driver will register the GPIO pin if it finds the gpio-controller 3062306a36Sopenharmony_ci * property in the device tree. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * To use the IRQ controller function, the following two properties must 3362306a36Sopenharmony_ci * be added to the device tree node for the gpt device: 3462306a36Sopenharmony_ci * interrupt-controller; 3562306a36Sopenharmony_ci * #interrupt-cells = < 1 >; 3662306a36Sopenharmony_ci * The IRQ controller binding only uses one cell to specify the interrupt, 3762306a36Sopenharmony_ci * and the IRQ flags are encoded in the cell. A cell is not used to encode 3862306a36Sopenharmony_ci * the IRQ number because the GPT only has a single IRQ source. For flags, 3962306a36Sopenharmony_ci * a value of '1' means rising edge sensitive and '2' means falling edge. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * The GPIO and the IRQ controller functions can be used at the same time, 4262306a36Sopenharmony_ci * but in this use case the IO line will only work as an input. Trying to 4362306a36Sopenharmony_ci * use it as a GPIO output will not work. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * When using the GPIO line as an output, it can either be driven as normal 4662306a36Sopenharmony_ci * IO, or it can be an Open Collector (OC) output. At the moment it is the 4762306a36Sopenharmony_ci * responsibility of either the bootloader or the platform setup code to set 4862306a36Sopenharmony_ci * the output mode. This driver does not change the output mode setting. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#include <linux/irq.h> 5262306a36Sopenharmony_ci#include <linux/interrupt.h> 5362306a36Sopenharmony_ci#include <linux/io.h> 5462306a36Sopenharmony_ci#include <linux/list.h> 5562306a36Sopenharmony_ci#include <linux/mutex.h> 5662306a36Sopenharmony_ci#include <linux/of.h> 5762306a36Sopenharmony_ci#include <linux/of_address.h> 5862306a36Sopenharmony_ci#include <linux/of_irq.h> 5962306a36Sopenharmony_ci#include <linux/of_gpio.h> 6062306a36Sopenharmony_ci#include <linux/platform_device.h> 6162306a36Sopenharmony_ci#include <linux/kernel.h> 6262306a36Sopenharmony_ci#include <linux/property.h> 6362306a36Sopenharmony_ci#include <linux/slab.h> 6462306a36Sopenharmony_ci#include <linux/fs.h> 6562306a36Sopenharmony_ci#include <linux/watchdog.h> 6662306a36Sopenharmony_ci#include <linux/miscdevice.h> 6762306a36Sopenharmony_ci#include <linux/uaccess.h> 6862306a36Sopenharmony_ci#include <linux/module.h> 6962306a36Sopenharmony_ci#include <asm/div64.h> 7062306a36Sopenharmony_ci#include <asm/mpc52xx.h> 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ciMODULE_DESCRIPTION("Freescale MPC52xx gpt driver"); 7362306a36Sopenharmony_ciMODULE_AUTHOR("Sascha Hauer, Grant Likely, Albrecht Dreß"); 7462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/** 7762306a36Sopenharmony_ci * struct mpc52xx_gpt - Private data structure for MPC52xx GPT driver 7862306a36Sopenharmony_ci * @dev: pointer to device structure 7962306a36Sopenharmony_ci * @regs: virtual address of GPT registers 8062306a36Sopenharmony_ci * @lock: spinlock to coordinate between different functions. 8162306a36Sopenharmony_ci * @gc: gpio_chip instance structure; used when GPIO is enabled 8262306a36Sopenharmony_ci * @irqhost: Pointer to irq_domain instance; used when IRQ mode is supported 8362306a36Sopenharmony_ci * @wdt_mode: only relevant for gpt0: bit 0 (MPC52xx_GPT_CAN_WDT) indicates 8462306a36Sopenharmony_ci * if the gpt may be used as wdt, bit 1 (MPC52xx_GPT_IS_WDT) indicates 8562306a36Sopenharmony_ci * if the timer is actively used as wdt which blocks gpt functions 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_cistruct mpc52xx_gpt_priv { 8862306a36Sopenharmony_ci struct list_head list; /* List of all GPT devices */ 8962306a36Sopenharmony_ci struct device *dev; 9062306a36Sopenharmony_ci struct mpc52xx_gpt __iomem *regs; 9162306a36Sopenharmony_ci raw_spinlock_t lock; 9262306a36Sopenharmony_ci struct irq_domain *irqhost; 9362306a36Sopenharmony_ci u32 ipb_freq; 9462306a36Sopenharmony_ci u8 wdt_mode; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#if defined(CONFIG_GPIOLIB) 9762306a36Sopenharmony_ci struct gpio_chip gc; 9862306a36Sopenharmony_ci#endif 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ciLIST_HEAD(mpc52xx_gpt_list); 10262306a36Sopenharmony_ciDEFINE_MUTEX(mpc52xx_gpt_list_mutex); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_MS_MASK (0x07) 10562306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_MS_IC (0x01) 10662306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_MS_OC (0x02) 10762306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_MS_PWM (0x03) 10862306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_MS_GPIO (0x04) 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_GPIO_MASK (0x30) 11162306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_GPIO_OUT_LOW (0x20) 11262306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_GPIO_OUT_HIGH (0x30) 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_COUNTER_ENABLE (0x1000) 11562306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_CONTINUOUS (0x0400) 11662306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_OPEN_DRAIN (0x0200) 11762306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_IRQ_EN (0x0100) 11862306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_WDT_EN (0x8000) 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_ICT_MASK (0x030000) 12162306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_ICT_RISING (0x010000) 12262306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_ICT_FALLING (0x020000) 12362306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_ICT_TOGGLE (0x030000) 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci#define MPC52xx_GPT_MODE_WDT_PING (0xa5) 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define MPC52xx_GPT_STATUS_IRQMASK (0x000f) 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci#define MPC52xx_GPT_CAN_WDT (1 << 0) 13062306a36Sopenharmony_ci#define MPC52xx_GPT_IS_WDT (1 << 1) 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* --------------------------------------------------------------------- 13462306a36Sopenharmony_ci * Cascaded interrupt controller hooks 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void mpc52xx_gpt_irq_unmask(struct irq_data *d) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt = irq_data_get_irq_chip_data(d); 14062306a36Sopenharmony_ci unsigned long flags; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpt->lock, flags); 14362306a36Sopenharmony_ci setbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_IRQ_EN); 14462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpt->lock, flags); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic void mpc52xx_gpt_irq_mask(struct irq_data *d) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt = irq_data_get_irq_chip_data(d); 15062306a36Sopenharmony_ci unsigned long flags; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpt->lock, flags); 15362306a36Sopenharmony_ci clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_IRQ_EN); 15462306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpt->lock, flags); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic void mpc52xx_gpt_irq_ack(struct irq_data *d) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt = irq_data_get_irq_chip_data(d); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci out_be32(&gpt->regs->status, MPC52xx_GPT_STATUS_IRQMASK); 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic int mpc52xx_gpt_irq_set_type(struct irq_data *d, unsigned int flow_type) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt = irq_data_get_irq_chip_data(d); 16762306a36Sopenharmony_ci unsigned long flags; 16862306a36Sopenharmony_ci u32 reg; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci dev_dbg(gpt->dev, "%s: virq=%i type=%x\n", __func__, d->irq, flow_type); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpt->lock, flags); 17362306a36Sopenharmony_ci reg = in_be32(&gpt->regs->mode) & ~MPC52xx_GPT_MODE_ICT_MASK; 17462306a36Sopenharmony_ci if (flow_type & IRQF_TRIGGER_RISING) 17562306a36Sopenharmony_ci reg |= MPC52xx_GPT_MODE_ICT_RISING; 17662306a36Sopenharmony_ci if (flow_type & IRQF_TRIGGER_FALLING) 17762306a36Sopenharmony_ci reg |= MPC52xx_GPT_MODE_ICT_FALLING; 17862306a36Sopenharmony_ci out_be32(&gpt->regs->mode, reg); 17962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpt->lock, flags); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return 0; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic struct irq_chip mpc52xx_gpt_irq_chip = { 18562306a36Sopenharmony_ci .name = "MPC52xx GPT", 18662306a36Sopenharmony_ci .irq_unmask = mpc52xx_gpt_irq_unmask, 18762306a36Sopenharmony_ci .irq_mask = mpc52xx_gpt_irq_mask, 18862306a36Sopenharmony_ci .irq_ack = mpc52xx_gpt_irq_ack, 18962306a36Sopenharmony_ci .irq_set_type = mpc52xx_gpt_irq_set_type, 19062306a36Sopenharmony_ci}; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic void mpc52xx_gpt_irq_cascade(struct irq_desc *desc) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt = irq_desc_get_handler_data(desc); 19562306a36Sopenharmony_ci u32 status; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci status = in_be32(&gpt->regs->status) & MPC52xx_GPT_STATUS_IRQMASK; 19862306a36Sopenharmony_ci if (status) 19962306a36Sopenharmony_ci generic_handle_domain_irq(gpt->irqhost, 0); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int mpc52xx_gpt_irq_map(struct irq_domain *h, unsigned int virq, 20362306a36Sopenharmony_ci irq_hw_number_t hw) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt = h->host_data; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci dev_dbg(gpt->dev, "%s: h=%p, virq=%i\n", __func__, h, virq); 20862306a36Sopenharmony_ci irq_set_chip_data(virq, gpt); 20962306a36Sopenharmony_ci irq_set_chip_and_handler(virq, &mpc52xx_gpt_irq_chip, handle_edge_irq); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci return 0; 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic int mpc52xx_gpt_irq_xlate(struct irq_domain *h, struct device_node *ct, 21562306a36Sopenharmony_ci const u32 *intspec, unsigned int intsize, 21662306a36Sopenharmony_ci irq_hw_number_t *out_hwirq, 21762306a36Sopenharmony_ci unsigned int *out_flags) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt = h->host_data; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci dev_dbg(gpt->dev, "%s: flags=%i\n", __func__, intspec[0]); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if ((intsize < 1) || (intspec[0] > 3)) { 22462306a36Sopenharmony_ci dev_err(gpt->dev, "bad irq specifier in %pOF\n", ct); 22562306a36Sopenharmony_ci return -EINVAL; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci *out_hwirq = 0; /* The GPT only has 1 IRQ line */ 22962306a36Sopenharmony_ci *out_flags = intspec[0]; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci return 0; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic const struct irq_domain_ops mpc52xx_gpt_irq_ops = { 23562306a36Sopenharmony_ci .map = mpc52xx_gpt_irq_map, 23662306a36Sopenharmony_ci .xlate = mpc52xx_gpt_irq_xlate, 23762306a36Sopenharmony_ci}; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic void 24062306a36Sopenharmony_cimpc52xx_gpt_irq_setup(struct mpc52xx_gpt_priv *gpt, struct device_node *node) 24162306a36Sopenharmony_ci{ 24262306a36Sopenharmony_ci int cascade_virq; 24362306a36Sopenharmony_ci unsigned long flags; 24462306a36Sopenharmony_ci u32 mode; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci cascade_virq = irq_of_parse_and_map(node, 0); 24762306a36Sopenharmony_ci if (!cascade_virq) 24862306a36Sopenharmony_ci return; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci gpt->irqhost = irq_domain_add_linear(node, 1, &mpc52xx_gpt_irq_ops, gpt); 25162306a36Sopenharmony_ci if (!gpt->irqhost) { 25262306a36Sopenharmony_ci dev_err(gpt->dev, "irq_domain_add_linear() failed\n"); 25362306a36Sopenharmony_ci return; 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci irq_set_handler_data(cascade_virq, gpt); 25762306a36Sopenharmony_ci irq_set_chained_handler(cascade_virq, mpc52xx_gpt_irq_cascade); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* If the GPT is currently disabled, then change it to be in Input 26062306a36Sopenharmony_ci * Capture mode. If the mode is non-zero, then the pin could be 26162306a36Sopenharmony_ci * already in use for something. */ 26262306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpt->lock, flags); 26362306a36Sopenharmony_ci mode = in_be32(&gpt->regs->mode); 26462306a36Sopenharmony_ci if ((mode & MPC52xx_GPT_MODE_MS_MASK) == 0) 26562306a36Sopenharmony_ci out_be32(&gpt->regs->mode, mode | MPC52xx_GPT_MODE_MS_IC); 26662306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpt->lock, flags); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci dev_dbg(gpt->dev, "%s() complete. virq=%i\n", __func__, cascade_virq); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci/* --------------------------------------------------------------------- 27362306a36Sopenharmony_ci * GPIOLIB hooks 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci#if defined(CONFIG_GPIOLIB) 27662306a36Sopenharmony_cistatic int mpc52xx_gpt_gpio_get(struct gpio_chip *gc, unsigned int gpio) 27762306a36Sopenharmony_ci{ 27862306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt = gpiochip_get_data(gc); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci return (in_be32(&gpt->regs->status) >> 8) & 1; 28162306a36Sopenharmony_ci} 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic void 28462306a36Sopenharmony_cimpc52xx_gpt_gpio_set(struct gpio_chip *gc, unsigned int gpio, int v) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt = gpiochip_get_data(gc); 28762306a36Sopenharmony_ci unsigned long flags; 28862306a36Sopenharmony_ci u32 r; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci dev_dbg(gpt->dev, "%s: gpio:%d v:%d\n", __func__, gpio, v); 29162306a36Sopenharmony_ci r = v ? MPC52xx_GPT_MODE_GPIO_OUT_HIGH : MPC52xx_GPT_MODE_GPIO_OUT_LOW; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpt->lock, flags); 29462306a36Sopenharmony_ci clrsetbits_be32(&gpt->regs->mode, MPC52xx_GPT_MODE_GPIO_MASK, r); 29562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpt->lock, flags); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int mpc52xx_gpt_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt = gpiochip_get_data(gc); 30162306a36Sopenharmony_ci unsigned long flags; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci dev_dbg(gpt->dev, "%s: gpio:%d\n", __func__, gpio); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpt->lock, flags); 30662306a36Sopenharmony_ci clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_GPIO_MASK); 30762306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpt->lock, flags); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int 31362306a36Sopenharmony_cimpc52xx_gpt_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci mpc52xx_gpt_gpio_set(gc, gpio, val); 31662306a36Sopenharmony_ci return 0; 31762306a36Sopenharmony_ci} 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistatic void mpc52xx_gpt_gpio_setup(struct mpc52xx_gpt_priv *gpt) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci int rc; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Only setup GPIO if the device claims the GPT is a GPIO controller */ 32462306a36Sopenharmony_ci if (!device_property_present(gpt->dev, "gpio-controller")) 32562306a36Sopenharmony_ci return; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci gpt->gc.label = kasprintf(GFP_KERNEL, "%pfw", dev_fwnode(gpt->dev)); 32862306a36Sopenharmony_ci if (!gpt->gc.label) { 32962306a36Sopenharmony_ci dev_err(gpt->dev, "out of memory\n"); 33062306a36Sopenharmony_ci return; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci gpt->gc.ngpio = 1; 33462306a36Sopenharmony_ci gpt->gc.direction_input = mpc52xx_gpt_gpio_dir_in; 33562306a36Sopenharmony_ci gpt->gc.direction_output = mpc52xx_gpt_gpio_dir_out; 33662306a36Sopenharmony_ci gpt->gc.get = mpc52xx_gpt_gpio_get; 33762306a36Sopenharmony_ci gpt->gc.set = mpc52xx_gpt_gpio_set; 33862306a36Sopenharmony_ci gpt->gc.base = -1; 33962306a36Sopenharmony_ci gpt->gc.parent = gpt->dev; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* Setup external pin in GPIO mode */ 34262306a36Sopenharmony_ci clrsetbits_be32(&gpt->regs->mode, MPC52xx_GPT_MODE_MS_MASK, 34362306a36Sopenharmony_ci MPC52xx_GPT_MODE_MS_GPIO); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci rc = gpiochip_add_data(&gpt->gc, gpt); 34662306a36Sopenharmony_ci if (rc) 34762306a36Sopenharmony_ci dev_err(gpt->dev, "gpiochip_add_data() failed; rc=%i\n", rc); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci dev_dbg(gpt->dev, "%s() complete.\n", __func__); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci#else /* defined(CONFIG_GPIOLIB) */ 35262306a36Sopenharmony_cistatic void mpc52xx_gpt_gpio_setup(struct mpc52xx_gpt_priv *gpt) { } 35362306a36Sopenharmony_ci#endif /* defined(CONFIG_GPIOLIB) */ 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci/*********************************************************************** 35662306a36Sopenharmony_ci * Timer API 35762306a36Sopenharmony_ci */ 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/** 36062306a36Sopenharmony_ci * mpc52xx_gpt_from_irq - Return the GPT device associated with an IRQ number 36162306a36Sopenharmony_ci * @irq: irq of timer. 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_cistruct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt; 36662306a36Sopenharmony_ci struct list_head *pos; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* Iterate over the list of timers looking for a matching device */ 36962306a36Sopenharmony_ci mutex_lock(&mpc52xx_gpt_list_mutex); 37062306a36Sopenharmony_ci list_for_each(pos, &mpc52xx_gpt_list) { 37162306a36Sopenharmony_ci gpt = container_of(pos, struct mpc52xx_gpt_priv, list); 37262306a36Sopenharmony_ci if (gpt->irqhost && irq == irq_linear_revmap(gpt->irqhost, 0)) { 37362306a36Sopenharmony_ci mutex_unlock(&mpc52xx_gpt_list_mutex); 37462306a36Sopenharmony_ci return gpt; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci mutex_unlock(&mpc52xx_gpt_list_mutex); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci return NULL; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ciEXPORT_SYMBOL(mpc52xx_gpt_from_irq); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int mpc52xx_gpt_do_start(struct mpc52xx_gpt_priv *gpt, u64 period, 38462306a36Sopenharmony_ci int continuous, int as_wdt) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci u32 clear, set; 38762306a36Sopenharmony_ci u64 clocks; 38862306a36Sopenharmony_ci u32 prescale; 38962306a36Sopenharmony_ci unsigned long flags; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci clear = MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTINUOUS; 39262306a36Sopenharmony_ci set = MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER_ENABLE; 39362306a36Sopenharmony_ci if (as_wdt) { 39462306a36Sopenharmony_ci clear |= MPC52xx_GPT_MODE_IRQ_EN; 39562306a36Sopenharmony_ci set |= MPC52xx_GPT_MODE_WDT_EN; 39662306a36Sopenharmony_ci } else if (continuous) 39762306a36Sopenharmony_ci set |= MPC52xx_GPT_MODE_CONTINUOUS; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* Determine the number of clocks in the requested period. 64 bit 40062306a36Sopenharmony_ci * arithmetic is done here to preserve the precision until the value 40162306a36Sopenharmony_ci * is scaled back down into the u32 range. Period is in 'ns', bus 40262306a36Sopenharmony_ci * frequency is in Hz. */ 40362306a36Sopenharmony_ci clocks = period * (u64)gpt->ipb_freq; 40462306a36Sopenharmony_ci do_div(clocks, 1000000000); /* Scale it down to ns range */ 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci /* This device cannot handle a clock count greater than 32 bits */ 40762306a36Sopenharmony_ci if (clocks > 0xffffffff) 40862306a36Sopenharmony_ci return -EINVAL; 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ci /* Calculate the prescaler and count values from the clocks value. 41162306a36Sopenharmony_ci * 'clocks' is the number of clock ticks in the period. The timer 41262306a36Sopenharmony_ci * has 16 bit precision and a 16 bit prescaler. Prescaler is 41362306a36Sopenharmony_ci * calculated by integer dividing the clocks by 0x10000 (shifting 41462306a36Sopenharmony_ci * down 16 bits) to obtain the smallest possible divisor for clocks 41562306a36Sopenharmony_ci * to get a 16 bit count value. 41662306a36Sopenharmony_ci * 41762306a36Sopenharmony_ci * Note: the prescale register is '1' based, not '0' based. ie. a 41862306a36Sopenharmony_ci * value of '1' means divide the clock by one. 0xffff divides the 41962306a36Sopenharmony_ci * clock by 0xffff. '0x0000' does not divide by zero, but wraps 42062306a36Sopenharmony_ci * around and divides by 0x10000. That is why prescale must be 42162306a36Sopenharmony_ci * a u32 variable, not a u16, for this calculation. */ 42262306a36Sopenharmony_ci prescale = (clocks >> 16) + 1; 42362306a36Sopenharmony_ci do_div(clocks, prescale); 42462306a36Sopenharmony_ci if (clocks > 0xffff) { 42562306a36Sopenharmony_ci pr_err("calculation error; prescale:%x clocks:%llx\n", 42662306a36Sopenharmony_ci prescale, clocks); 42762306a36Sopenharmony_ci return -EINVAL; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* Set and enable the timer, reject an attempt to use a wdt as gpt */ 43162306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpt->lock, flags); 43262306a36Sopenharmony_ci if (as_wdt) 43362306a36Sopenharmony_ci gpt->wdt_mode |= MPC52xx_GPT_IS_WDT; 43462306a36Sopenharmony_ci else if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT) != 0) { 43562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpt->lock, flags); 43662306a36Sopenharmony_ci return -EBUSY; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci out_be32(&gpt->regs->count, prescale << 16 | clocks); 43962306a36Sopenharmony_ci clrsetbits_be32(&gpt->regs->mode, clear, set); 44062306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpt->lock, flags); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return 0; 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci/** 44662306a36Sopenharmony_ci * mpc52xx_gpt_start_timer - Set and enable the GPT timer 44762306a36Sopenharmony_ci * @gpt: Pointer to gpt private data structure 44862306a36Sopenharmony_ci * @period: period of timer in ns; max. ~130s @ 33MHz IPB clock 44962306a36Sopenharmony_ci * @continuous: set to 1 to make timer continuous free running 45062306a36Sopenharmony_ci * 45162306a36Sopenharmony_ci * An interrupt will be generated every time the timer fires 45262306a36Sopenharmony_ci */ 45362306a36Sopenharmony_ciint mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period, 45462306a36Sopenharmony_ci int continuous) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci return mpc52xx_gpt_do_start(gpt, period, continuous, 0); 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ciEXPORT_SYMBOL(mpc52xx_gpt_start_timer); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci/** 46162306a36Sopenharmony_ci * mpc52xx_gpt_stop_timer - Stop a gpt 46262306a36Sopenharmony_ci * @gpt: Pointer to gpt private data structure 46362306a36Sopenharmony_ci * 46462306a36Sopenharmony_ci * Returns an error if attempting to stop a wdt 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ciint mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci unsigned long flags; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* reject the operation if the timer is used as watchdog (gpt 0 only) */ 47162306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpt->lock, flags); 47262306a36Sopenharmony_ci if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT) != 0) { 47362306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpt->lock, flags); 47462306a36Sopenharmony_ci return -EBUSY; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_COUNTER_ENABLE); 47862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpt->lock, flags); 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ciEXPORT_SYMBOL(mpc52xx_gpt_stop_timer); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci/** 48462306a36Sopenharmony_ci * mpc52xx_gpt_timer_period - Read the timer period 48562306a36Sopenharmony_ci * @gpt: Pointer to gpt private data structure 48662306a36Sopenharmony_ci * 48762306a36Sopenharmony_ci * Returns the timer period in ns 48862306a36Sopenharmony_ci */ 48962306a36Sopenharmony_ciu64 mpc52xx_gpt_timer_period(struct mpc52xx_gpt_priv *gpt) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci u64 period; 49262306a36Sopenharmony_ci u64 prescale; 49362306a36Sopenharmony_ci unsigned long flags; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpt->lock, flags); 49662306a36Sopenharmony_ci period = in_be32(&gpt->regs->count); 49762306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpt->lock, flags); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci prescale = period >> 16; 50062306a36Sopenharmony_ci period &= 0xffff; 50162306a36Sopenharmony_ci if (prescale == 0) 50262306a36Sopenharmony_ci prescale = 0x10000; 50362306a36Sopenharmony_ci period = period * prescale * 1000000000ULL; 50462306a36Sopenharmony_ci do_div(period, gpt->ipb_freq); 50562306a36Sopenharmony_ci return period; 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ciEXPORT_SYMBOL(mpc52xx_gpt_timer_period); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci#if defined(CONFIG_MPC5200_WDT) 51062306a36Sopenharmony_ci/*********************************************************************** 51162306a36Sopenharmony_ci * Watchdog API for gpt0 51262306a36Sopenharmony_ci */ 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci#define WDT_IDENTITY "mpc52xx watchdog on GPT0" 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci/* wdt_is_active stores whether or not the /dev/watchdog device is opened */ 51762306a36Sopenharmony_cistatic unsigned long wdt_is_active; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci/* wdt-capable gpt */ 52062306a36Sopenharmony_cistatic struct mpc52xx_gpt_priv *mpc52xx_gpt_wdt; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci/* low-level wdt functions */ 52362306a36Sopenharmony_cistatic inline void mpc52xx_gpt_wdt_ping(struct mpc52xx_gpt_priv *gpt_wdt) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci unsigned long flags; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpt_wdt->lock, flags); 52862306a36Sopenharmony_ci out_8((u8 *) &gpt_wdt->regs->mode, MPC52xx_GPT_MODE_WDT_PING); 52962306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpt_wdt->lock, flags); 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci/* wdt misc device api */ 53362306a36Sopenharmony_cistatic ssize_t mpc52xx_wdt_write(struct file *file, const char __user *data, 53462306a36Sopenharmony_ci size_t len, loff_t *ppos) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt_wdt = file->private_data; 53762306a36Sopenharmony_ci mpc52xx_gpt_wdt_ping(gpt_wdt); 53862306a36Sopenharmony_ci return 0; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_cistatic const struct watchdog_info mpc5200_wdt_info = { 54262306a36Sopenharmony_ci .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 54362306a36Sopenharmony_ci .identity = WDT_IDENTITY, 54462306a36Sopenharmony_ci}; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic long mpc52xx_wdt_ioctl(struct file *file, unsigned int cmd, 54762306a36Sopenharmony_ci unsigned long arg) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt_wdt = file->private_data; 55062306a36Sopenharmony_ci int __user *data = (int __user *)arg; 55162306a36Sopenharmony_ci int timeout; 55262306a36Sopenharmony_ci u64 real_timeout; 55362306a36Sopenharmony_ci int ret = 0; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci switch (cmd) { 55662306a36Sopenharmony_ci case WDIOC_GETSUPPORT: 55762306a36Sopenharmony_ci ret = copy_to_user(data, &mpc5200_wdt_info, 55862306a36Sopenharmony_ci sizeof(mpc5200_wdt_info)); 55962306a36Sopenharmony_ci if (ret) 56062306a36Sopenharmony_ci ret = -EFAULT; 56162306a36Sopenharmony_ci break; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci case WDIOC_GETSTATUS: 56462306a36Sopenharmony_ci case WDIOC_GETBOOTSTATUS: 56562306a36Sopenharmony_ci ret = put_user(0, data); 56662306a36Sopenharmony_ci break; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci case WDIOC_KEEPALIVE: 56962306a36Sopenharmony_ci mpc52xx_gpt_wdt_ping(gpt_wdt); 57062306a36Sopenharmony_ci break; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci case WDIOC_SETTIMEOUT: 57362306a36Sopenharmony_ci ret = get_user(timeout, data); 57462306a36Sopenharmony_ci if (ret) 57562306a36Sopenharmony_ci break; 57662306a36Sopenharmony_ci real_timeout = (u64) timeout * 1000000000ULL; 57762306a36Sopenharmony_ci ret = mpc52xx_gpt_do_start(gpt_wdt, real_timeout, 0, 1); 57862306a36Sopenharmony_ci if (ret) 57962306a36Sopenharmony_ci break; 58062306a36Sopenharmony_ci /* fall through and return the timeout */ 58162306a36Sopenharmony_ci fallthrough; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci case WDIOC_GETTIMEOUT: 58462306a36Sopenharmony_ci /* we need to round here as to avoid e.g. the following 58562306a36Sopenharmony_ci * situation: 58662306a36Sopenharmony_ci * - timeout requested is 1 second; 58762306a36Sopenharmony_ci * - real timeout @33MHz is 999997090ns 58862306a36Sopenharmony_ci * - the int divide by 10^9 will return 0. 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_ci real_timeout = 59162306a36Sopenharmony_ci mpc52xx_gpt_timer_period(gpt_wdt) + 500000000ULL; 59262306a36Sopenharmony_ci do_div(real_timeout, 1000000000ULL); 59362306a36Sopenharmony_ci timeout = (int) real_timeout; 59462306a36Sopenharmony_ci ret = put_user(timeout, data); 59562306a36Sopenharmony_ci break; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci default: 59862306a36Sopenharmony_ci ret = -ENOTTY; 59962306a36Sopenharmony_ci } 60062306a36Sopenharmony_ci return ret; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int mpc52xx_wdt_open(struct inode *inode, struct file *file) 60462306a36Sopenharmony_ci{ 60562306a36Sopenharmony_ci int ret; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* sanity check */ 60862306a36Sopenharmony_ci if (!mpc52xx_gpt_wdt) 60962306a36Sopenharmony_ci return -ENODEV; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci /* /dev/watchdog can only be opened once */ 61262306a36Sopenharmony_ci if (test_and_set_bit(0, &wdt_is_active)) 61362306a36Sopenharmony_ci return -EBUSY; 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* Set and activate the watchdog with 30 seconds timeout */ 61662306a36Sopenharmony_ci ret = mpc52xx_gpt_do_start(mpc52xx_gpt_wdt, 30ULL * 1000000000ULL, 61762306a36Sopenharmony_ci 0, 1); 61862306a36Sopenharmony_ci if (ret) { 61962306a36Sopenharmony_ci clear_bit(0, &wdt_is_active); 62062306a36Sopenharmony_ci return ret; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci file->private_data = mpc52xx_gpt_wdt; 62462306a36Sopenharmony_ci return stream_open(inode, file); 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_cistatic int mpc52xx_wdt_release(struct inode *inode, struct file *file) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci /* note: releasing the wdt in NOWAYOUT-mode does not stop it */ 63062306a36Sopenharmony_ci#if !defined(CONFIG_WATCHDOG_NOWAYOUT) 63162306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt_wdt = file->private_data; 63262306a36Sopenharmony_ci unsigned long flags; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci raw_spin_lock_irqsave(&gpt_wdt->lock, flags); 63562306a36Sopenharmony_ci clrbits32(&gpt_wdt->regs->mode, 63662306a36Sopenharmony_ci MPC52xx_GPT_MODE_COUNTER_ENABLE | MPC52xx_GPT_MODE_WDT_EN); 63762306a36Sopenharmony_ci gpt_wdt->wdt_mode &= ~MPC52xx_GPT_IS_WDT; 63862306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&gpt_wdt->lock, flags); 63962306a36Sopenharmony_ci#endif 64062306a36Sopenharmony_ci clear_bit(0, &wdt_is_active); 64162306a36Sopenharmony_ci return 0; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic const struct file_operations mpc52xx_wdt_fops = { 64662306a36Sopenharmony_ci .owner = THIS_MODULE, 64762306a36Sopenharmony_ci .llseek = no_llseek, 64862306a36Sopenharmony_ci .write = mpc52xx_wdt_write, 64962306a36Sopenharmony_ci .unlocked_ioctl = mpc52xx_wdt_ioctl, 65062306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 65162306a36Sopenharmony_ci .open = mpc52xx_wdt_open, 65262306a36Sopenharmony_ci .release = mpc52xx_wdt_release, 65362306a36Sopenharmony_ci}; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic struct miscdevice mpc52xx_wdt_miscdev = { 65662306a36Sopenharmony_ci .minor = WATCHDOG_MINOR, 65762306a36Sopenharmony_ci .name = "watchdog", 65862306a36Sopenharmony_ci .fops = &mpc52xx_wdt_fops, 65962306a36Sopenharmony_ci}; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic int mpc52xx_gpt_wdt_init(void) 66262306a36Sopenharmony_ci{ 66362306a36Sopenharmony_ci int err; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* try to register the watchdog misc device */ 66662306a36Sopenharmony_ci err = misc_register(&mpc52xx_wdt_miscdev); 66762306a36Sopenharmony_ci if (err) 66862306a36Sopenharmony_ci pr_err("%s: cannot register watchdog device\n", WDT_IDENTITY); 66962306a36Sopenharmony_ci else 67062306a36Sopenharmony_ci pr_info("%s: watchdog device registered\n", WDT_IDENTITY); 67162306a36Sopenharmony_ci return err; 67262306a36Sopenharmony_ci} 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_cistatic int mpc52xx_gpt_wdt_setup(struct mpc52xx_gpt_priv *gpt, 67562306a36Sopenharmony_ci const u32 *period) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci u64 real_timeout; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci /* remember the gpt for the wdt operation */ 68062306a36Sopenharmony_ci mpc52xx_gpt_wdt = gpt; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* configure the wdt if the device tree contained a timeout */ 68362306a36Sopenharmony_ci if (!period || *period == 0) 68462306a36Sopenharmony_ci return 0; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci real_timeout = (u64) *period * 1000000000ULL; 68762306a36Sopenharmony_ci if (mpc52xx_gpt_do_start(gpt, real_timeout, 0, 1)) 68862306a36Sopenharmony_ci dev_warn(gpt->dev, "starting as wdt failed\n"); 68962306a36Sopenharmony_ci else 69062306a36Sopenharmony_ci dev_info(gpt->dev, "watchdog set to %us timeout\n", *period); 69162306a36Sopenharmony_ci return 0; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci#else 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_cistatic int mpc52xx_gpt_wdt_init(void) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci return 0; 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic inline int mpc52xx_gpt_wdt_setup(struct mpc52xx_gpt_priv *gpt, 70262306a36Sopenharmony_ci const u32 *period) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci return 0; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci#endif /* CONFIG_MPC5200_WDT */ 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci/* --------------------------------------------------------------------- 71062306a36Sopenharmony_ci * of_platform bus binding code 71162306a36Sopenharmony_ci */ 71262306a36Sopenharmony_cistatic int mpc52xx_gpt_probe(struct platform_device *ofdev) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci struct mpc52xx_gpt_priv *gpt; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci gpt = devm_kzalloc(&ofdev->dev, sizeof *gpt, GFP_KERNEL); 71762306a36Sopenharmony_ci if (!gpt) 71862306a36Sopenharmony_ci return -ENOMEM; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci raw_spin_lock_init(&gpt->lock); 72162306a36Sopenharmony_ci gpt->dev = &ofdev->dev; 72262306a36Sopenharmony_ci gpt->ipb_freq = mpc5xxx_get_bus_frequency(&ofdev->dev); 72362306a36Sopenharmony_ci gpt->regs = of_iomap(ofdev->dev.of_node, 0); 72462306a36Sopenharmony_ci if (!gpt->regs) 72562306a36Sopenharmony_ci return -ENOMEM; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci dev_set_drvdata(&ofdev->dev, gpt); 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci mpc52xx_gpt_gpio_setup(gpt); 73062306a36Sopenharmony_ci mpc52xx_gpt_irq_setup(gpt, ofdev->dev.of_node); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci mutex_lock(&mpc52xx_gpt_list_mutex); 73362306a36Sopenharmony_ci list_add(&gpt->list, &mpc52xx_gpt_list); 73462306a36Sopenharmony_ci mutex_unlock(&mpc52xx_gpt_list_mutex); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* check if this device could be a watchdog */ 73762306a36Sopenharmony_ci if (of_property_read_bool(ofdev->dev.of_node, "fsl,has-wdt") || 73862306a36Sopenharmony_ci of_property_read_bool(ofdev->dev.of_node, "has-wdt")) { 73962306a36Sopenharmony_ci const u32 *on_boot_wdt; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci gpt->wdt_mode = MPC52xx_GPT_CAN_WDT; 74262306a36Sopenharmony_ci on_boot_wdt = of_get_property(ofdev->dev.of_node, 74362306a36Sopenharmony_ci "fsl,wdt-on-boot", NULL); 74462306a36Sopenharmony_ci if (on_boot_wdt) { 74562306a36Sopenharmony_ci dev_info(gpt->dev, "used as watchdog\n"); 74662306a36Sopenharmony_ci gpt->wdt_mode |= MPC52xx_GPT_IS_WDT; 74762306a36Sopenharmony_ci } else 74862306a36Sopenharmony_ci dev_info(gpt->dev, "can function as watchdog\n"); 74962306a36Sopenharmony_ci mpc52xx_gpt_wdt_setup(gpt, on_boot_wdt); 75062306a36Sopenharmony_ci } 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci return 0; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic const struct of_device_id mpc52xx_gpt_match[] = { 75662306a36Sopenharmony_ci { .compatible = "fsl,mpc5200-gpt", }, 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* Depreciated compatible values; don't use for new dts files */ 75962306a36Sopenharmony_ci { .compatible = "fsl,mpc5200-gpt-gpio", }, 76062306a36Sopenharmony_ci { .compatible = "mpc5200-gpt", }, 76162306a36Sopenharmony_ci {} 76262306a36Sopenharmony_ci}; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cistatic struct platform_driver mpc52xx_gpt_driver = { 76562306a36Sopenharmony_ci .driver = { 76662306a36Sopenharmony_ci .name = "mpc52xx-gpt", 76762306a36Sopenharmony_ci .suppress_bind_attrs = true, 76862306a36Sopenharmony_ci .of_match_table = mpc52xx_gpt_match, 76962306a36Sopenharmony_ci }, 77062306a36Sopenharmony_ci .probe = mpc52xx_gpt_probe, 77162306a36Sopenharmony_ci}; 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic int __init mpc52xx_gpt_init(void) 77462306a36Sopenharmony_ci{ 77562306a36Sopenharmony_ci return platform_driver_register(&mpc52xx_gpt_driver); 77662306a36Sopenharmony_ci} 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci/* Make sure GPIOs and IRQs get set up before anyone tries to use them */ 77962306a36Sopenharmony_cisubsys_initcall(mpc52xx_gpt_init); 78062306a36Sopenharmony_cidevice_initcall(mpc52xx_gpt_wdt_init); 781