xref: /OK3568_Linux_fs/kernel/arch/powerpc/platforms/52xx/mpc52xx_gpt.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * MPC5200 General Purpose Timer device driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2009 Secret Lab Technologies Ltd.
6*4882a593Smuzhiyun  * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * This file is a driver for the the General Purpose Timer (gpt) devices
9*4882a593Smuzhiyun  * found on the MPC5200 SoC.  Each timer has an IO pin which can be used
10*4882a593Smuzhiyun  * for GPIO or can be used to raise interrupts.  The timer function can
11*4882a593Smuzhiyun  * be used independently from the IO pin, or it can be used to control
12*4882a593Smuzhiyun  * output signals or measure input signals.
13*4882a593Smuzhiyun  *
14*4882a593Smuzhiyun  * This driver supports the GPIO and IRQ controller functions of the GPT
15*4882a593Smuzhiyun  * device.  Timer functions are not yet supported.
16*4882a593Smuzhiyun  *
17*4882a593Smuzhiyun  * The timer gpt0 can be used as watchdog (wdt).  If the wdt mode is used,
18*4882a593Smuzhiyun  * this prevents the use of any gpt0 gpt function (i.e. they will fail with
19*4882a593Smuzhiyun  * -EBUSY).  Thus, the safety wdt function always has precedence over the gpt
20*4882a593Smuzhiyun  * function.  If the kernel has been compiled with CONFIG_WATCHDOG_NOWAYOUT,
21*4882a593Smuzhiyun  * this means that gpt0 is locked in wdt mode until the next reboot - this
22*4882a593Smuzhiyun  * may be a requirement in safety applications.
23*4882a593Smuzhiyun  *
24*4882a593Smuzhiyun  * To use the GPIO function, the following two properties must be added
25*4882a593Smuzhiyun  * to the device tree node for the gpt device (typically in the .dts file
26*4882a593Smuzhiyun  * for the board):
27*4882a593Smuzhiyun  * 	gpio-controller;
28*4882a593Smuzhiyun  * 	#gpio-cells = < 2 >;
29*4882a593Smuzhiyun  * This driver will register the GPIO pin if it finds the gpio-controller
30*4882a593Smuzhiyun  * property in the device tree.
31*4882a593Smuzhiyun  *
32*4882a593Smuzhiyun  * To use the IRQ controller function, the following two properties must
33*4882a593Smuzhiyun  * be added to the device tree node for the gpt device:
34*4882a593Smuzhiyun  * 	interrupt-controller;
35*4882a593Smuzhiyun  * 	#interrupt-cells = < 1 >;
36*4882a593Smuzhiyun  * The IRQ controller binding only uses one cell to specify the interrupt,
37*4882a593Smuzhiyun  * and the IRQ flags are encoded in the cell.  A cell is not used to encode
38*4882a593Smuzhiyun  * the IRQ number because the GPT only has a single IRQ source.  For flags,
39*4882a593Smuzhiyun  * a value of '1' means rising edge sensitive and '2' means falling edge.
40*4882a593Smuzhiyun  *
41*4882a593Smuzhiyun  * The GPIO and the IRQ controller functions can be used at the same time,
42*4882a593Smuzhiyun  * but in this use case the IO line will only work as an input.  Trying to
43*4882a593Smuzhiyun  * use it as a GPIO output will not work.
44*4882a593Smuzhiyun  *
45*4882a593Smuzhiyun  * When using the GPIO line as an output, it can either be driven as normal
46*4882a593Smuzhiyun  * IO, or it can be an Open Collector (OC) output.  At the moment it is the
47*4882a593Smuzhiyun  * responsibility of either the bootloader or the platform setup code to set
48*4882a593Smuzhiyun  * the output mode.  This driver does not change the output mode setting.
49*4882a593Smuzhiyun  */
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun #include <linux/device.h>
52*4882a593Smuzhiyun #include <linux/irq.h>
53*4882a593Smuzhiyun #include <linux/interrupt.h>
54*4882a593Smuzhiyun #include <linux/io.h>
55*4882a593Smuzhiyun #include <linux/list.h>
56*4882a593Smuzhiyun #include <linux/mutex.h>
57*4882a593Smuzhiyun #include <linux/of.h>
58*4882a593Smuzhiyun #include <linux/of_platform.h>
59*4882a593Smuzhiyun #include <linux/of_gpio.h>
60*4882a593Smuzhiyun #include <linux/kernel.h>
61*4882a593Smuzhiyun #include <linux/slab.h>
62*4882a593Smuzhiyun #include <linux/fs.h>
63*4882a593Smuzhiyun #include <linux/watchdog.h>
64*4882a593Smuzhiyun #include <linux/miscdevice.h>
65*4882a593Smuzhiyun #include <linux/uaccess.h>
66*4882a593Smuzhiyun #include <linux/module.h>
67*4882a593Smuzhiyun #include <asm/div64.h>
68*4882a593Smuzhiyun #include <asm/mpc52xx.h>
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun MODULE_DESCRIPTION("Freescale MPC52xx gpt driver");
71*4882a593Smuzhiyun MODULE_AUTHOR("Sascha Hauer, Grant Likely, Albrecht Dreß");
72*4882a593Smuzhiyun MODULE_LICENSE("GPL");
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun /**
75*4882a593Smuzhiyun  * struct mpc52xx_gpt - Private data structure for MPC52xx GPT driver
76*4882a593Smuzhiyun  * @dev: pointer to device structure
77*4882a593Smuzhiyun  * @regs: virtual address of GPT registers
78*4882a593Smuzhiyun  * @lock: spinlock to coordinate between different functions.
79*4882a593Smuzhiyun  * @gc: gpio_chip instance structure; used when GPIO is enabled
80*4882a593Smuzhiyun  * @irqhost: Pointer to irq_domain instance; used when IRQ mode is supported
81*4882a593Smuzhiyun  * @wdt_mode: only relevant for gpt0: bit 0 (MPC52xx_GPT_CAN_WDT) indicates
82*4882a593Smuzhiyun  *   if the gpt may be used as wdt, bit 1 (MPC52xx_GPT_IS_WDT) indicates
83*4882a593Smuzhiyun  *   if the timer is actively used as wdt which blocks gpt functions
84*4882a593Smuzhiyun  */
85*4882a593Smuzhiyun struct mpc52xx_gpt_priv {
86*4882a593Smuzhiyun 	struct list_head list;		/* List of all GPT devices */
87*4882a593Smuzhiyun 	struct device *dev;
88*4882a593Smuzhiyun 	struct mpc52xx_gpt __iomem *regs;
89*4882a593Smuzhiyun 	raw_spinlock_t lock;
90*4882a593Smuzhiyun 	struct irq_domain *irqhost;
91*4882a593Smuzhiyun 	u32 ipb_freq;
92*4882a593Smuzhiyun 	u8 wdt_mode;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun #if defined(CONFIG_GPIOLIB)
95*4882a593Smuzhiyun 	struct gpio_chip gc;
96*4882a593Smuzhiyun #endif
97*4882a593Smuzhiyun };
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun LIST_HEAD(mpc52xx_gpt_list);
100*4882a593Smuzhiyun DEFINE_MUTEX(mpc52xx_gpt_list_mutex);
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_MS_MASK	(0x07)
103*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_MS_IC		(0x01)
104*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_MS_OC		(0x02)
105*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_MS_PWM		(0x03)
106*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_MS_GPIO	(0x04)
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_GPIO_MASK	(0x30)
109*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_GPIO_OUT_LOW	(0x20)
110*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_GPIO_OUT_HIGH	(0x30)
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_COUNTER_ENABLE	(0x1000)
113*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_CONTINUOUS	(0x0400)
114*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_OPEN_DRAIN	(0x0200)
115*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_IRQ_EN		(0x0100)
116*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_WDT_EN		(0x8000)
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_ICT_MASK	(0x030000)
119*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_ICT_RISING	(0x010000)
120*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_ICT_FALLING	(0x020000)
121*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_ICT_TOGGLE	(0x030000)
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun #define MPC52xx_GPT_MODE_WDT_PING	(0xa5)
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun #define MPC52xx_GPT_STATUS_IRQMASK	(0x000f)
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun #define MPC52xx_GPT_CAN_WDT		(1 << 0)
128*4882a593Smuzhiyun #define MPC52xx_GPT_IS_WDT		(1 << 1)
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun /* ---------------------------------------------------------------------
132*4882a593Smuzhiyun  * Cascaded interrupt controller hooks
133*4882a593Smuzhiyun  */
134*4882a593Smuzhiyun 
mpc52xx_gpt_irq_unmask(struct irq_data * d)135*4882a593Smuzhiyun static void mpc52xx_gpt_irq_unmask(struct irq_data *d)
136*4882a593Smuzhiyun {
137*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt = irq_data_get_irq_chip_data(d);
138*4882a593Smuzhiyun 	unsigned long flags;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	raw_spin_lock_irqsave(&gpt->lock, flags);
141*4882a593Smuzhiyun 	setbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_IRQ_EN);
142*4882a593Smuzhiyun 	raw_spin_unlock_irqrestore(&gpt->lock, flags);
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun 
mpc52xx_gpt_irq_mask(struct irq_data * d)145*4882a593Smuzhiyun static void mpc52xx_gpt_irq_mask(struct irq_data *d)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt = irq_data_get_irq_chip_data(d);
148*4882a593Smuzhiyun 	unsigned long flags;
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	raw_spin_lock_irqsave(&gpt->lock, flags);
151*4882a593Smuzhiyun 	clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_IRQ_EN);
152*4882a593Smuzhiyun 	raw_spin_unlock_irqrestore(&gpt->lock, flags);
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
mpc52xx_gpt_irq_ack(struct irq_data * d)155*4882a593Smuzhiyun static void mpc52xx_gpt_irq_ack(struct irq_data *d)
156*4882a593Smuzhiyun {
157*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt = irq_data_get_irq_chip_data(d);
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	out_be32(&gpt->regs->status, MPC52xx_GPT_STATUS_IRQMASK);
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun 
mpc52xx_gpt_irq_set_type(struct irq_data * d,unsigned int flow_type)162*4882a593Smuzhiyun static int mpc52xx_gpt_irq_set_type(struct irq_data *d, unsigned int flow_type)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt = irq_data_get_irq_chip_data(d);
165*4882a593Smuzhiyun 	unsigned long flags;
166*4882a593Smuzhiyun 	u32 reg;
167*4882a593Smuzhiyun 
168*4882a593Smuzhiyun 	dev_dbg(gpt->dev, "%s: virq=%i type=%x\n", __func__, d->irq, flow_type);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	raw_spin_lock_irqsave(&gpt->lock, flags);
171*4882a593Smuzhiyun 	reg = in_be32(&gpt->regs->mode) & ~MPC52xx_GPT_MODE_ICT_MASK;
172*4882a593Smuzhiyun 	if (flow_type & IRQF_TRIGGER_RISING)
173*4882a593Smuzhiyun 		reg |= MPC52xx_GPT_MODE_ICT_RISING;
174*4882a593Smuzhiyun 	if (flow_type & IRQF_TRIGGER_FALLING)
175*4882a593Smuzhiyun 		reg |= MPC52xx_GPT_MODE_ICT_FALLING;
176*4882a593Smuzhiyun 	out_be32(&gpt->regs->mode, reg);
177*4882a593Smuzhiyun 	raw_spin_unlock_irqrestore(&gpt->lock, flags);
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	return 0;
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun static struct irq_chip mpc52xx_gpt_irq_chip = {
183*4882a593Smuzhiyun 	.name = "MPC52xx GPT",
184*4882a593Smuzhiyun 	.irq_unmask = mpc52xx_gpt_irq_unmask,
185*4882a593Smuzhiyun 	.irq_mask = mpc52xx_gpt_irq_mask,
186*4882a593Smuzhiyun 	.irq_ack = mpc52xx_gpt_irq_ack,
187*4882a593Smuzhiyun 	.irq_set_type = mpc52xx_gpt_irq_set_type,
188*4882a593Smuzhiyun };
189*4882a593Smuzhiyun 
mpc52xx_gpt_irq_cascade(struct irq_desc * desc)190*4882a593Smuzhiyun static void mpc52xx_gpt_irq_cascade(struct irq_desc *desc)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt = irq_desc_get_handler_data(desc);
193*4882a593Smuzhiyun 	int sub_virq;
194*4882a593Smuzhiyun 	u32 status;
195*4882a593Smuzhiyun 
196*4882a593Smuzhiyun 	status = in_be32(&gpt->regs->status) & MPC52xx_GPT_STATUS_IRQMASK;
197*4882a593Smuzhiyun 	if (status) {
198*4882a593Smuzhiyun 		sub_virq = irq_linear_revmap(gpt->irqhost, 0);
199*4882a593Smuzhiyun 		generic_handle_irq(sub_virq);
200*4882a593Smuzhiyun 	}
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun 
mpc52xx_gpt_irq_map(struct irq_domain * h,unsigned int virq,irq_hw_number_t hw)203*4882a593Smuzhiyun static int mpc52xx_gpt_irq_map(struct irq_domain *h, unsigned int virq,
204*4882a593Smuzhiyun 			       irq_hw_number_t hw)
205*4882a593Smuzhiyun {
206*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt = h->host_data;
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun 	dev_dbg(gpt->dev, "%s: h=%p, virq=%i\n", __func__, h, virq);
209*4882a593Smuzhiyun 	irq_set_chip_data(virq, gpt);
210*4882a593Smuzhiyun 	irq_set_chip_and_handler(virq, &mpc52xx_gpt_irq_chip, handle_edge_irq);
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	return 0;
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun 
mpc52xx_gpt_irq_xlate(struct irq_domain * h,struct device_node * ct,const u32 * intspec,unsigned int intsize,irq_hw_number_t * out_hwirq,unsigned int * out_flags)215*4882a593Smuzhiyun static int mpc52xx_gpt_irq_xlate(struct irq_domain *h, struct device_node *ct,
216*4882a593Smuzhiyun 				 const u32 *intspec, unsigned int intsize,
217*4882a593Smuzhiyun 				 irq_hw_number_t *out_hwirq,
218*4882a593Smuzhiyun 				 unsigned int *out_flags)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt = h->host_data;
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun 	dev_dbg(gpt->dev, "%s: flags=%i\n", __func__, intspec[0]);
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	if ((intsize < 1) || (intspec[0] > 3)) {
225*4882a593Smuzhiyun 		dev_err(gpt->dev, "bad irq specifier in %pOF\n", ct);
226*4882a593Smuzhiyun 		return -EINVAL;
227*4882a593Smuzhiyun 	}
228*4882a593Smuzhiyun 
229*4882a593Smuzhiyun 	*out_hwirq = 0; /* The GPT only has 1 IRQ line */
230*4882a593Smuzhiyun 	*out_flags = intspec[0];
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 	return 0;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun static const struct irq_domain_ops mpc52xx_gpt_irq_ops = {
236*4882a593Smuzhiyun 	.map = mpc52xx_gpt_irq_map,
237*4882a593Smuzhiyun 	.xlate = mpc52xx_gpt_irq_xlate,
238*4882a593Smuzhiyun };
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun static void
mpc52xx_gpt_irq_setup(struct mpc52xx_gpt_priv * gpt,struct device_node * node)241*4882a593Smuzhiyun mpc52xx_gpt_irq_setup(struct mpc52xx_gpt_priv *gpt, struct device_node *node)
242*4882a593Smuzhiyun {
243*4882a593Smuzhiyun 	int cascade_virq;
244*4882a593Smuzhiyun 	unsigned long flags;
245*4882a593Smuzhiyun 	u32 mode;
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	cascade_virq = irq_of_parse_and_map(node, 0);
248*4882a593Smuzhiyun 	if (!cascade_virq)
249*4882a593Smuzhiyun 		return;
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	gpt->irqhost = irq_domain_add_linear(node, 1, &mpc52xx_gpt_irq_ops, gpt);
252*4882a593Smuzhiyun 	if (!gpt->irqhost) {
253*4882a593Smuzhiyun 		dev_err(gpt->dev, "irq_domain_add_linear() failed\n");
254*4882a593Smuzhiyun 		return;
255*4882a593Smuzhiyun 	}
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	irq_set_handler_data(cascade_virq, gpt);
258*4882a593Smuzhiyun 	irq_set_chained_handler(cascade_virq, mpc52xx_gpt_irq_cascade);
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	/* If the GPT is currently disabled, then change it to be in Input
261*4882a593Smuzhiyun 	 * Capture mode.  If the mode is non-zero, then the pin could be
262*4882a593Smuzhiyun 	 * already in use for something. */
263*4882a593Smuzhiyun 	raw_spin_lock_irqsave(&gpt->lock, flags);
264*4882a593Smuzhiyun 	mode = in_be32(&gpt->regs->mode);
265*4882a593Smuzhiyun 	if ((mode & MPC52xx_GPT_MODE_MS_MASK) == 0)
266*4882a593Smuzhiyun 		out_be32(&gpt->regs->mode, mode | MPC52xx_GPT_MODE_MS_IC);
267*4882a593Smuzhiyun 	raw_spin_unlock_irqrestore(&gpt->lock, flags);
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	dev_dbg(gpt->dev, "%s() complete. virq=%i\n", __func__, cascade_virq);
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 
273*4882a593Smuzhiyun /* ---------------------------------------------------------------------
274*4882a593Smuzhiyun  * GPIOLIB hooks
275*4882a593Smuzhiyun  */
276*4882a593Smuzhiyun #if defined(CONFIG_GPIOLIB)
mpc52xx_gpt_gpio_get(struct gpio_chip * gc,unsigned int gpio)277*4882a593Smuzhiyun static int mpc52xx_gpt_gpio_get(struct gpio_chip *gc, unsigned int gpio)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt = gpiochip_get_data(gc);
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	return (in_be32(&gpt->regs->status) >> 8) & 1;
282*4882a593Smuzhiyun }
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun static void
mpc52xx_gpt_gpio_set(struct gpio_chip * gc,unsigned int gpio,int v)285*4882a593Smuzhiyun mpc52xx_gpt_gpio_set(struct gpio_chip *gc, unsigned int gpio, int v)
286*4882a593Smuzhiyun {
287*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt = gpiochip_get_data(gc);
288*4882a593Smuzhiyun 	unsigned long flags;
289*4882a593Smuzhiyun 	u32 r;
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	dev_dbg(gpt->dev, "%s: gpio:%d v:%d\n", __func__, gpio, v);
292*4882a593Smuzhiyun 	r = v ? MPC52xx_GPT_MODE_GPIO_OUT_HIGH : MPC52xx_GPT_MODE_GPIO_OUT_LOW;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	raw_spin_lock_irqsave(&gpt->lock, flags);
295*4882a593Smuzhiyun 	clrsetbits_be32(&gpt->regs->mode, MPC52xx_GPT_MODE_GPIO_MASK, r);
296*4882a593Smuzhiyun 	raw_spin_unlock_irqrestore(&gpt->lock, flags);
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun 
mpc52xx_gpt_gpio_dir_in(struct gpio_chip * gc,unsigned int gpio)299*4882a593Smuzhiyun static int mpc52xx_gpt_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio)
300*4882a593Smuzhiyun {
301*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt = gpiochip_get_data(gc);
302*4882a593Smuzhiyun 	unsigned long flags;
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	dev_dbg(gpt->dev, "%s: gpio:%d\n", __func__, gpio);
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun 	raw_spin_lock_irqsave(&gpt->lock, flags);
307*4882a593Smuzhiyun 	clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_GPIO_MASK);
308*4882a593Smuzhiyun 	raw_spin_unlock_irqrestore(&gpt->lock, flags);
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	return 0;
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun static int
mpc52xx_gpt_gpio_dir_out(struct gpio_chip * gc,unsigned int gpio,int val)314*4882a593Smuzhiyun mpc52xx_gpt_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
315*4882a593Smuzhiyun {
316*4882a593Smuzhiyun 	mpc52xx_gpt_gpio_set(gc, gpio, val);
317*4882a593Smuzhiyun 	return 0;
318*4882a593Smuzhiyun }
319*4882a593Smuzhiyun 
320*4882a593Smuzhiyun static void
mpc52xx_gpt_gpio_setup(struct mpc52xx_gpt_priv * gpt,struct device_node * node)321*4882a593Smuzhiyun mpc52xx_gpt_gpio_setup(struct mpc52xx_gpt_priv *gpt, struct device_node *node)
322*4882a593Smuzhiyun {
323*4882a593Smuzhiyun 	int rc;
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun 	/* Only setup GPIO if the device tree claims the GPT is
326*4882a593Smuzhiyun 	 * a GPIO controller */
327*4882a593Smuzhiyun 	if (!of_find_property(node, "gpio-controller", NULL))
328*4882a593Smuzhiyun 		return;
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	gpt->gc.label = kasprintf(GFP_KERNEL, "%pOF", node);
331*4882a593Smuzhiyun 	if (!gpt->gc.label) {
332*4882a593Smuzhiyun 		dev_err(gpt->dev, "out of memory\n");
333*4882a593Smuzhiyun 		return;
334*4882a593Smuzhiyun 	}
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	gpt->gc.ngpio = 1;
337*4882a593Smuzhiyun 	gpt->gc.direction_input  = mpc52xx_gpt_gpio_dir_in;
338*4882a593Smuzhiyun 	gpt->gc.direction_output = mpc52xx_gpt_gpio_dir_out;
339*4882a593Smuzhiyun 	gpt->gc.get = mpc52xx_gpt_gpio_get;
340*4882a593Smuzhiyun 	gpt->gc.set = mpc52xx_gpt_gpio_set;
341*4882a593Smuzhiyun 	gpt->gc.base = -1;
342*4882a593Smuzhiyun 	gpt->gc.of_node = node;
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	/* Setup external pin in GPIO mode */
345*4882a593Smuzhiyun 	clrsetbits_be32(&gpt->regs->mode, MPC52xx_GPT_MODE_MS_MASK,
346*4882a593Smuzhiyun 			MPC52xx_GPT_MODE_MS_GPIO);
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	rc = gpiochip_add_data(&gpt->gc, gpt);
349*4882a593Smuzhiyun 	if (rc)
350*4882a593Smuzhiyun 		dev_err(gpt->dev, "gpiochip_add_data() failed; rc=%i\n", rc);
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 	dev_dbg(gpt->dev, "%s() complete.\n", __func__);
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun #else /* defined(CONFIG_GPIOLIB) */
355*4882a593Smuzhiyun static void
mpc52xx_gpt_gpio_setup(struct mpc52xx_gpt_priv * p,struct device_node * np)356*4882a593Smuzhiyun mpc52xx_gpt_gpio_setup(struct mpc52xx_gpt_priv *p, struct device_node *np) { }
357*4882a593Smuzhiyun #endif /* defined(CONFIG_GPIOLIB) */
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun /***********************************************************************
360*4882a593Smuzhiyun  * Timer API
361*4882a593Smuzhiyun  */
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun /**
364*4882a593Smuzhiyun  * mpc52xx_gpt_from_irq - Return the GPT device associated with an IRQ number
365*4882a593Smuzhiyun  * @irq: irq of timer.
366*4882a593Smuzhiyun  */
mpc52xx_gpt_from_irq(int irq)367*4882a593Smuzhiyun struct mpc52xx_gpt_priv *mpc52xx_gpt_from_irq(int irq)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt;
370*4882a593Smuzhiyun 	struct list_head *pos;
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	/* Iterate over the list of timers looking for a matching device */
373*4882a593Smuzhiyun 	mutex_lock(&mpc52xx_gpt_list_mutex);
374*4882a593Smuzhiyun 	list_for_each(pos, &mpc52xx_gpt_list) {
375*4882a593Smuzhiyun 		gpt = container_of(pos, struct mpc52xx_gpt_priv, list);
376*4882a593Smuzhiyun 		if (gpt->irqhost && irq == irq_linear_revmap(gpt->irqhost, 0)) {
377*4882a593Smuzhiyun 			mutex_unlock(&mpc52xx_gpt_list_mutex);
378*4882a593Smuzhiyun 			return gpt;
379*4882a593Smuzhiyun 		}
380*4882a593Smuzhiyun 	}
381*4882a593Smuzhiyun 	mutex_unlock(&mpc52xx_gpt_list_mutex);
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	return NULL;
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun EXPORT_SYMBOL(mpc52xx_gpt_from_irq);
386*4882a593Smuzhiyun 
mpc52xx_gpt_do_start(struct mpc52xx_gpt_priv * gpt,u64 period,int continuous,int as_wdt)387*4882a593Smuzhiyun static int mpc52xx_gpt_do_start(struct mpc52xx_gpt_priv *gpt, u64 period,
388*4882a593Smuzhiyun 				int continuous, int as_wdt)
389*4882a593Smuzhiyun {
390*4882a593Smuzhiyun 	u32 clear, set;
391*4882a593Smuzhiyun 	u64 clocks;
392*4882a593Smuzhiyun 	u32 prescale;
393*4882a593Smuzhiyun 	unsigned long flags;
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	clear = MPC52xx_GPT_MODE_MS_MASK | MPC52xx_GPT_MODE_CONTINUOUS;
396*4882a593Smuzhiyun 	set = MPC52xx_GPT_MODE_MS_GPIO | MPC52xx_GPT_MODE_COUNTER_ENABLE;
397*4882a593Smuzhiyun 	if (as_wdt) {
398*4882a593Smuzhiyun 		clear |= MPC52xx_GPT_MODE_IRQ_EN;
399*4882a593Smuzhiyun 		set |= MPC52xx_GPT_MODE_WDT_EN;
400*4882a593Smuzhiyun 	} else if (continuous)
401*4882a593Smuzhiyun 		set |= MPC52xx_GPT_MODE_CONTINUOUS;
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	/* Determine the number of clocks in the requested period.  64 bit
404*4882a593Smuzhiyun 	 * arithmatic is done here to preserve the precision until the value
405*4882a593Smuzhiyun 	 * is scaled back down into the u32 range.  Period is in 'ns', bus
406*4882a593Smuzhiyun 	 * frequency is in Hz. */
407*4882a593Smuzhiyun 	clocks = period * (u64)gpt->ipb_freq;
408*4882a593Smuzhiyun 	do_div(clocks, 1000000000); /* Scale it down to ns range */
409*4882a593Smuzhiyun 
410*4882a593Smuzhiyun 	/* This device cannot handle a clock count greater than 32 bits */
411*4882a593Smuzhiyun 	if (clocks > 0xffffffff)
412*4882a593Smuzhiyun 		return -EINVAL;
413*4882a593Smuzhiyun 
414*4882a593Smuzhiyun 	/* Calculate the prescaler and count values from the clocks value.
415*4882a593Smuzhiyun 	 * 'clocks' is the number of clock ticks in the period.  The timer
416*4882a593Smuzhiyun 	 * has 16 bit precision and a 16 bit prescaler.  Prescaler is
417*4882a593Smuzhiyun 	 * calculated by integer dividing the clocks by 0x10000 (shifting
418*4882a593Smuzhiyun 	 * down 16 bits) to obtain the smallest possible divisor for clocks
419*4882a593Smuzhiyun 	 * to get a 16 bit count value.
420*4882a593Smuzhiyun 	 *
421*4882a593Smuzhiyun 	 * Note: the prescale register is '1' based, not '0' based.  ie. a
422*4882a593Smuzhiyun 	 * value of '1' means divide the clock by one.  0xffff divides the
423*4882a593Smuzhiyun 	 * clock by 0xffff.  '0x0000' does not divide by zero, but wraps
424*4882a593Smuzhiyun 	 * around and divides by 0x10000.  That is why prescale must be
425*4882a593Smuzhiyun 	 * a u32 variable, not a u16, for this calculation. */
426*4882a593Smuzhiyun 	prescale = (clocks >> 16) + 1;
427*4882a593Smuzhiyun 	do_div(clocks, prescale);
428*4882a593Smuzhiyun 	if (clocks > 0xffff) {
429*4882a593Smuzhiyun 		pr_err("calculation error; prescale:%x clocks:%llx\n",
430*4882a593Smuzhiyun 		       prescale, clocks);
431*4882a593Smuzhiyun 		return -EINVAL;
432*4882a593Smuzhiyun 	}
433*4882a593Smuzhiyun 
434*4882a593Smuzhiyun 	/* Set and enable the timer, reject an attempt to use a wdt as gpt */
435*4882a593Smuzhiyun 	raw_spin_lock_irqsave(&gpt->lock, flags);
436*4882a593Smuzhiyun 	if (as_wdt)
437*4882a593Smuzhiyun 		gpt->wdt_mode |= MPC52xx_GPT_IS_WDT;
438*4882a593Smuzhiyun 	else if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT) != 0) {
439*4882a593Smuzhiyun 		raw_spin_unlock_irqrestore(&gpt->lock, flags);
440*4882a593Smuzhiyun 		return -EBUSY;
441*4882a593Smuzhiyun 	}
442*4882a593Smuzhiyun 	out_be32(&gpt->regs->count, prescale << 16 | clocks);
443*4882a593Smuzhiyun 	clrsetbits_be32(&gpt->regs->mode, clear, set);
444*4882a593Smuzhiyun 	raw_spin_unlock_irqrestore(&gpt->lock, flags);
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun 	return 0;
447*4882a593Smuzhiyun }
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun /**
450*4882a593Smuzhiyun  * mpc52xx_gpt_start_timer - Set and enable the GPT timer
451*4882a593Smuzhiyun  * @gpt: Pointer to gpt private data structure
452*4882a593Smuzhiyun  * @period: period of timer in ns; max. ~130s @ 33MHz IPB clock
453*4882a593Smuzhiyun  * @continuous: set to 1 to make timer continuous free running
454*4882a593Smuzhiyun  *
455*4882a593Smuzhiyun  * An interrupt will be generated every time the timer fires
456*4882a593Smuzhiyun  */
mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv * gpt,u64 period,int continuous)457*4882a593Smuzhiyun int mpc52xx_gpt_start_timer(struct mpc52xx_gpt_priv *gpt, u64 period,
458*4882a593Smuzhiyun                             int continuous)
459*4882a593Smuzhiyun {
460*4882a593Smuzhiyun 	return mpc52xx_gpt_do_start(gpt, period, continuous, 0);
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun EXPORT_SYMBOL(mpc52xx_gpt_start_timer);
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun /**
465*4882a593Smuzhiyun  * mpc52xx_gpt_stop_timer - Stop a gpt
466*4882a593Smuzhiyun  * @gpt: Pointer to gpt private data structure
467*4882a593Smuzhiyun  *
468*4882a593Smuzhiyun  * Returns an error if attempting to stop a wdt
469*4882a593Smuzhiyun  */
mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv * gpt)470*4882a593Smuzhiyun int mpc52xx_gpt_stop_timer(struct mpc52xx_gpt_priv *gpt)
471*4882a593Smuzhiyun {
472*4882a593Smuzhiyun 	unsigned long flags;
473*4882a593Smuzhiyun 
474*4882a593Smuzhiyun 	/* reject the operation if the timer is used as watchdog (gpt 0 only) */
475*4882a593Smuzhiyun 	raw_spin_lock_irqsave(&gpt->lock, flags);
476*4882a593Smuzhiyun 	if ((gpt->wdt_mode & MPC52xx_GPT_IS_WDT) != 0) {
477*4882a593Smuzhiyun 		raw_spin_unlock_irqrestore(&gpt->lock, flags);
478*4882a593Smuzhiyun 		return -EBUSY;
479*4882a593Smuzhiyun 	}
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 	clrbits32(&gpt->regs->mode, MPC52xx_GPT_MODE_COUNTER_ENABLE);
482*4882a593Smuzhiyun 	raw_spin_unlock_irqrestore(&gpt->lock, flags);
483*4882a593Smuzhiyun 	return 0;
484*4882a593Smuzhiyun }
485*4882a593Smuzhiyun EXPORT_SYMBOL(mpc52xx_gpt_stop_timer);
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun /**
488*4882a593Smuzhiyun  * mpc52xx_gpt_timer_period - Read the timer period
489*4882a593Smuzhiyun  * @gpt: Pointer to gpt private data structure
490*4882a593Smuzhiyun  *
491*4882a593Smuzhiyun  * Returns the timer period in ns
492*4882a593Smuzhiyun  */
mpc52xx_gpt_timer_period(struct mpc52xx_gpt_priv * gpt)493*4882a593Smuzhiyun u64 mpc52xx_gpt_timer_period(struct mpc52xx_gpt_priv *gpt)
494*4882a593Smuzhiyun {
495*4882a593Smuzhiyun 	u64 period;
496*4882a593Smuzhiyun 	u64 prescale;
497*4882a593Smuzhiyun 	unsigned long flags;
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	raw_spin_lock_irqsave(&gpt->lock, flags);
500*4882a593Smuzhiyun 	period = in_be32(&gpt->regs->count);
501*4882a593Smuzhiyun 	raw_spin_unlock_irqrestore(&gpt->lock, flags);
502*4882a593Smuzhiyun 
503*4882a593Smuzhiyun 	prescale = period >> 16;
504*4882a593Smuzhiyun 	period &= 0xffff;
505*4882a593Smuzhiyun 	if (prescale == 0)
506*4882a593Smuzhiyun 		prescale = 0x10000;
507*4882a593Smuzhiyun 	period = period * prescale * 1000000000ULL;
508*4882a593Smuzhiyun 	do_div(period, (u64)gpt->ipb_freq);
509*4882a593Smuzhiyun 	return period;
510*4882a593Smuzhiyun }
511*4882a593Smuzhiyun EXPORT_SYMBOL(mpc52xx_gpt_timer_period);
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun #if defined(CONFIG_MPC5200_WDT)
514*4882a593Smuzhiyun /***********************************************************************
515*4882a593Smuzhiyun  * Watchdog API for gpt0
516*4882a593Smuzhiyun  */
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun #define WDT_IDENTITY	    "mpc52xx watchdog on GPT0"
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun /* wdt_is_active stores whether or not the /dev/watchdog device is opened */
521*4882a593Smuzhiyun static unsigned long wdt_is_active;
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun /* wdt-capable gpt */
524*4882a593Smuzhiyun static struct mpc52xx_gpt_priv *mpc52xx_gpt_wdt;
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun /* low-level wdt functions */
mpc52xx_gpt_wdt_ping(struct mpc52xx_gpt_priv * gpt_wdt)527*4882a593Smuzhiyun static inline void mpc52xx_gpt_wdt_ping(struct mpc52xx_gpt_priv *gpt_wdt)
528*4882a593Smuzhiyun {
529*4882a593Smuzhiyun 	unsigned long flags;
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun 	raw_spin_lock_irqsave(&gpt_wdt->lock, flags);
532*4882a593Smuzhiyun 	out_8((u8 *) &gpt_wdt->regs->mode, MPC52xx_GPT_MODE_WDT_PING);
533*4882a593Smuzhiyun 	raw_spin_unlock_irqrestore(&gpt_wdt->lock, flags);
534*4882a593Smuzhiyun }
535*4882a593Smuzhiyun 
536*4882a593Smuzhiyun /* wdt misc device api */
mpc52xx_wdt_write(struct file * file,const char __user * data,size_t len,loff_t * ppos)537*4882a593Smuzhiyun static ssize_t mpc52xx_wdt_write(struct file *file, const char __user *data,
538*4882a593Smuzhiyun 				 size_t len, loff_t *ppos)
539*4882a593Smuzhiyun {
540*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt_wdt = file->private_data;
541*4882a593Smuzhiyun 	mpc52xx_gpt_wdt_ping(gpt_wdt);
542*4882a593Smuzhiyun 	return 0;
543*4882a593Smuzhiyun }
544*4882a593Smuzhiyun 
545*4882a593Smuzhiyun static const struct watchdog_info mpc5200_wdt_info = {
546*4882a593Smuzhiyun 	.options	= WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
547*4882a593Smuzhiyun 	.identity	= WDT_IDENTITY,
548*4882a593Smuzhiyun };
549*4882a593Smuzhiyun 
mpc52xx_wdt_ioctl(struct file * file,unsigned int cmd,unsigned long arg)550*4882a593Smuzhiyun static long mpc52xx_wdt_ioctl(struct file *file, unsigned int cmd,
551*4882a593Smuzhiyun 			      unsigned long arg)
552*4882a593Smuzhiyun {
553*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt_wdt = file->private_data;
554*4882a593Smuzhiyun 	int __user *data = (int __user *)arg;
555*4882a593Smuzhiyun 	int timeout;
556*4882a593Smuzhiyun 	u64 real_timeout;
557*4882a593Smuzhiyun 	int ret = 0;
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 	switch (cmd) {
560*4882a593Smuzhiyun 	case WDIOC_GETSUPPORT:
561*4882a593Smuzhiyun 		ret = copy_to_user(data, &mpc5200_wdt_info,
562*4882a593Smuzhiyun 				   sizeof(mpc5200_wdt_info));
563*4882a593Smuzhiyun 		if (ret)
564*4882a593Smuzhiyun 			ret = -EFAULT;
565*4882a593Smuzhiyun 		break;
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	case WDIOC_GETSTATUS:
568*4882a593Smuzhiyun 	case WDIOC_GETBOOTSTATUS:
569*4882a593Smuzhiyun 		ret = put_user(0, data);
570*4882a593Smuzhiyun 		break;
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	case WDIOC_KEEPALIVE:
573*4882a593Smuzhiyun 		mpc52xx_gpt_wdt_ping(gpt_wdt);
574*4882a593Smuzhiyun 		break;
575*4882a593Smuzhiyun 
576*4882a593Smuzhiyun 	case WDIOC_SETTIMEOUT:
577*4882a593Smuzhiyun 		ret = get_user(timeout, data);
578*4882a593Smuzhiyun 		if (ret)
579*4882a593Smuzhiyun 			break;
580*4882a593Smuzhiyun 		real_timeout = (u64) timeout * 1000000000ULL;
581*4882a593Smuzhiyun 		ret = mpc52xx_gpt_do_start(gpt_wdt, real_timeout, 0, 1);
582*4882a593Smuzhiyun 		if (ret)
583*4882a593Smuzhiyun 			break;
584*4882a593Smuzhiyun 		/* fall through and return the timeout */
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 	case WDIOC_GETTIMEOUT:
587*4882a593Smuzhiyun 		/* we need to round here as to avoid e.g. the following
588*4882a593Smuzhiyun 		 * situation:
589*4882a593Smuzhiyun 		 * - timeout requested is 1 second;
590*4882a593Smuzhiyun 		 * - real timeout @33MHz is 999997090ns
591*4882a593Smuzhiyun 		 * - the int divide by 10^9 will return 0.
592*4882a593Smuzhiyun 		 */
593*4882a593Smuzhiyun 		real_timeout =
594*4882a593Smuzhiyun 			mpc52xx_gpt_timer_period(gpt_wdt) + 500000000ULL;
595*4882a593Smuzhiyun 		do_div(real_timeout, 1000000000ULL);
596*4882a593Smuzhiyun 		timeout = (int) real_timeout;
597*4882a593Smuzhiyun 		ret = put_user(timeout, data);
598*4882a593Smuzhiyun 		break;
599*4882a593Smuzhiyun 
600*4882a593Smuzhiyun 	default:
601*4882a593Smuzhiyun 		ret = -ENOTTY;
602*4882a593Smuzhiyun 	}
603*4882a593Smuzhiyun 	return ret;
604*4882a593Smuzhiyun }
605*4882a593Smuzhiyun 
mpc52xx_wdt_open(struct inode * inode,struct file * file)606*4882a593Smuzhiyun static int mpc52xx_wdt_open(struct inode *inode, struct file *file)
607*4882a593Smuzhiyun {
608*4882a593Smuzhiyun 	int ret;
609*4882a593Smuzhiyun 
610*4882a593Smuzhiyun 	/* sanity check */
611*4882a593Smuzhiyun 	if (!mpc52xx_gpt_wdt)
612*4882a593Smuzhiyun 		return -ENODEV;
613*4882a593Smuzhiyun 
614*4882a593Smuzhiyun 	/* /dev/watchdog can only be opened once */
615*4882a593Smuzhiyun 	if (test_and_set_bit(0, &wdt_is_active))
616*4882a593Smuzhiyun 		return -EBUSY;
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun 	/* Set and activate the watchdog with 30 seconds timeout */
619*4882a593Smuzhiyun 	ret = mpc52xx_gpt_do_start(mpc52xx_gpt_wdt, 30ULL * 1000000000ULL,
620*4882a593Smuzhiyun 				   0, 1);
621*4882a593Smuzhiyun 	if (ret) {
622*4882a593Smuzhiyun 		clear_bit(0, &wdt_is_active);
623*4882a593Smuzhiyun 		return ret;
624*4882a593Smuzhiyun 	}
625*4882a593Smuzhiyun 
626*4882a593Smuzhiyun 	file->private_data = mpc52xx_gpt_wdt;
627*4882a593Smuzhiyun 	return stream_open(inode, file);
628*4882a593Smuzhiyun }
629*4882a593Smuzhiyun 
mpc52xx_wdt_release(struct inode * inode,struct file * file)630*4882a593Smuzhiyun static int mpc52xx_wdt_release(struct inode *inode, struct file *file)
631*4882a593Smuzhiyun {
632*4882a593Smuzhiyun 	/* note: releasing the wdt in NOWAYOUT-mode does not stop it */
633*4882a593Smuzhiyun #if !defined(CONFIG_WATCHDOG_NOWAYOUT)
634*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt_wdt = file->private_data;
635*4882a593Smuzhiyun 	unsigned long flags;
636*4882a593Smuzhiyun 
637*4882a593Smuzhiyun 	raw_spin_lock_irqsave(&gpt_wdt->lock, flags);
638*4882a593Smuzhiyun 	clrbits32(&gpt_wdt->regs->mode,
639*4882a593Smuzhiyun 		  MPC52xx_GPT_MODE_COUNTER_ENABLE | MPC52xx_GPT_MODE_WDT_EN);
640*4882a593Smuzhiyun 	gpt_wdt->wdt_mode &= ~MPC52xx_GPT_IS_WDT;
641*4882a593Smuzhiyun 	raw_spin_unlock_irqrestore(&gpt_wdt->lock, flags);
642*4882a593Smuzhiyun #endif
643*4882a593Smuzhiyun 	clear_bit(0, &wdt_is_active);
644*4882a593Smuzhiyun 	return 0;
645*4882a593Smuzhiyun }
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun 
648*4882a593Smuzhiyun static const struct file_operations mpc52xx_wdt_fops = {
649*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
650*4882a593Smuzhiyun 	.llseek		= no_llseek,
651*4882a593Smuzhiyun 	.write		= mpc52xx_wdt_write,
652*4882a593Smuzhiyun 	.unlocked_ioctl = mpc52xx_wdt_ioctl,
653*4882a593Smuzhiyun 	.compat_ioctl	= compat_ptr_ioctl,
654*4882a593Smuzhiyun 	.open		= mpc52xx_wdt_open,
655*4882a593Smuzhiyun 	.release	= mpc52xx_wdt_release,
656*4882a593Smuzhiyun };
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun static struct miscdevice mpc52xx_wdt_miscdev = {
659*4882a593Smuzhiyun 	.minor		= WATCHDOG_MINOR,
660*4882a593Smuzhiyun 	.name		= "watchdog",
661*4882a593Smuzhiyun 	.fops		= &mpc52xx_wdt_fops,
662*4882a593Smuzhiyun };
663*4882a593Smuzhiyun 
mpc52xx_gpt_wdt_init(void)664*4882a593Smuzhiyun static int mpc52xx_gpt_wdt_init(void)
665*4882a593Smuzhiyun {
666*4882a593Smuzhiyun 	int err;
667*4882a593Smuzhiyun 
668*4882a593Smuzhiyun 	/* try to register the watchdog misc device */
669*4882a593Smuzhiyun 	err = misc_register(&mpc52xx_wdt_miscdev);
670*4882a593Smuzhiyun 	if (err)
671*4882a593Smuzhiyun 		pr_err("%s: cannot register watchdog device\n", WDT_IDENTITY);
672*4882a593Smuzhiyun 	else
673*4882a593Smuzhiyun 		pr_info("%s: watchdog device registered\n", WDT_IDENTITY);
674*4882a593Smuzhiyun 	return err;
675*4882a593Smuzhiyun }
676*4882a593Smuzhiyun 
mpc52xx_gpt_wdt_setup(struct mpc52xx_gpt_priv * gpt,const u32 * period)677*4882a593Smuzhiyun static int mpc52xx_gpt_wdt_setup(struct mpc52xx_gpt_priv *gpt,
678*4882a593Smuzhiyun 				 const u32 *period)
679*4882a593Smuzhiyun {
680*4882a593Smuzhiyun 	u64 real_timeout;
681*4882a593Smuzhiyun 
682*4882a593Smuzhiyun 	/* remember the gpt for the wdt operation */
683*4882a593Smuzhiyun 	mpc52xx_gpt_wdt = gpt;
684*4882a593Smuzhiyun 
685*4882a593Smuzhiyun 	/* configure the wdt if the device tree contained a timeout */
686*4882a593Smuzhiyun 	if (!period || *period == 0)
687*4882a593Smuzhiyun 		return 0;
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun 	real_timeout = (u64) *period * 1000000000ULL;
690*4882a593Smuzhiyun 	if (mpc52xx_gpt_do_start(gpt, real_timeout, 0, 1))
691*4882a593Smuzhiyun 		dev_warn(gpt->dev, "starting as wdt failed\n");
692*4882a593Smuzhiyun 	else
693*4882a593Smuzhiyun 		dev_info(gpt->dev, "watchdog set to %us timeout\n", *period);
694*4882a593Smuzhiyun 	return 0;
695*4882a593Smuzhiyun }
696*4882a593Smuzhiyun 
697*4882a593Smuzhiyun #else
698*4882a593Smuzhiyun 
mpc52xx_gpt_wdt_init(void)699*4882a593Smuzhiyun static int mpc52xx_gpt_wdt_init(void)
700*4882a593Smuzhiyun {
701*4882a593Smuzhiyun 	return 0;
702*4882a593Smuzhiyun }
703*4882a593Smuzhiyun 
mpc52xx_gpt_wdt_setup(struct mpc52xx_gpt_priv * gpt,const u32 * period)704*4882a593Smuzhiyun static inline int mpc52xx_gpt_wdt_setup(struct mpc52xx_gpt_priv *gpt,
705*4882a593Smuzhiyun 					const u32 *period)
706*4882a593Smuzhiyun {
707*4882a593Smuzhiyun 	return 0;
708*4882a593Smuzhiyun }
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun #endif	/*  CONFIG_MPC5200_WDT	*/
711*4882a593Smuzhiyun 
712*4882a593Smuzhiyun /* ---------------------------------------------------------------------
713*4882a593Smuzhiyun  * of_platform bus binding code
714*4882a593Smuzhiyun  */
mpc52xx_gpt_probe(struct platform_device * ofdev)715*4882a593Smuzhiyun static int mpc52xx_gpt_probe(struct platform_device *ofdev)
716*4882a593Smuzhiyun {
717*4882a593Smuzhiyun 	struct mpc52xx_gpt_priv *gpt;
718*4882a593Smuzhiyun 
719*4882a593Smuzhiyun 	gpt = devm_kzalloc(&ofdev->dev, sizeof *gpt, GFP_KERNEL);
720*4882a593Smuzhiyun 	if (!gpt)
721*4882a593Smuzhiyun 		return -ENOMEM;
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun 	raw_spin_lock_init(&gpt->lock);
724*4882a593Smuzhiyun 	gpt->dev = &ofdev->dev;
725*4882a593Smuzhiyun 	gpt->ipb_freq = mpc5xxx_get_bus_frequency(ofdev->dev.of_node);
726*4882a593Smuzhiyun 	gpt->regs = of_iomap(ofdev->dev.of_node, 0);
727*4882a593Smuzhiyun 	if (!gpt->regs)
728*4882a593Smuzhiyun 		return -ENOMEM;
729*4882a593Smuzhiyun 
730*4882a593Smuzhiyun 	dev_set_drvdata(&ofdev->dev, gpt);
731*4882a593Smuzhiyun 
732*4882a593Smuzhiyun 	mpc52xx_gpt_gpio_setup(gpt, ofdev->dev.of_node);
733*4882a593Smuzhiyun 	mpc52xx_gpt_irq_setup(gpt, ofdev->dev.of_node);
734*4882a593Smuzhiyun 
735*4882a593Smuzhiyun 	mutex_lock(&mpc52xx_gpt_list_mutex);
736*4882a593Smuzhiyun 	list_add(&gpt->list, &mpc52xx_gpt_list);
737*4882a593Smuzhiyun 	mutex_unlock(&mpc52xx_gpt_list_mutex);
738*4882a593Smuzhiyun 
739*4882a593Smuzhiyun 	/* check if this device could be a watchdog */
740*4882a593Smuzhiyun 	if (of_get_property(ofdev->dev.of_node, "fsl,has-wdt", NULL) ||
741*4882a593Smuzhiyun 	    of_get_property(ofdev->dev.of_node, "has-wdt", NULL)) {
742*4882a593Smuzhiyun 		const u32 *on_boot_wdt;
743*4882a593Smuzhiyun 
744*4882a593Smuzhiyun 		gpt->wdt_mode = MPC52xx_GPT_CAN_WDT;
745*4882a593Smuzhiyun 		on_boot_wdt = of_get_property(ofdev->dev.of_node,
746*4882a593Smuzhiyun 					      "fsl,wdt-on-boot", NULL);
747*4882a593Smuzhiyun 		if (on_boot_wdt) {
748*4882a593Smuzhiyun 			dev_info(gpt->dev, "used as watchdog\n");
749*4882a593Smuzhiyun 			gpt->wdt_mode |= MPC52xx_GPT_IS_WDT;
750*4882a593Smuzhiyun 		} else
751*4882a593Smuzhiyun 			dev_info(gpt->dev, "can function as watchdog\n");
752*4882a593Smuzhiyun 		mpc52xx_gpt_wdt_setup(gpt, on_boot_wdt);
753*4882a593Smuzhiyun 	}
754*4882a593Smuzhiyun 
755*4882a593Smuzhiyun 	return 0;
756*4882a593Smuzhiyun }
757*4882a593Smuzhiyun 
mpc52xx_gpt_remove(struct platform_device * ofdev)758*4882a593Smuzhiyun static int mpc52xx_gpt_remove(struct platform_device *ofdev)
759*4882a593Smuzhiyun {
760*4882a593Smuzhiyun 	return -EBUSY;
761*4882a593Smuzhiyun }
762*4882a593Smuzhiyun 
763*4882a593Smuzhiyun static const struct of_device_id mpc52xx_gpt_match[] = {
764*4882a593Smuzhiyun 	{ .compatible = "fsl,mpc5200-gpt", },
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun 	/* Depreciated compatible values; don't use for new dts files */
767*4882a593Smuzhiyun 	{ .compatible = "fsl,mpc5200-gpt-gpio", },
768*4882a593Smuzhiyun 	{ .compatible = "mpc5200-gpt", },
769*4882a593Smuzhiyun 	{}
770*4882a593Smuzhiyun };
771*4882a593Smuzhiyun 
772*4882a593Smuzhiyun static struct platform_driver mpc52xx_gpt_driver = {
773*4882a593Smuzhiyun 	.driver = {
774*4882a593Smuzhiyun 		.name = "mpc52xx-gpt",
775*4882a593Smuzhiyun 		.of_match_table = mpc52xx_gpt_match,
776*4882a593Smuzhiyun 	},
777*4882a593Smuzhiyun 	.probe = mpc52xx_gpt_probe,
778*4882a593Smuzhiyun 	.remove = mpc52xx_gpt_remove,
779*4882a593Smuzhiyun };
780*4882a593Smuzhiyun 
mpc52xx_gpt_init(void)781*4882a593Smuzhiyun static int __init mpc52xx_gpt_init(void)
782*4882a593Smuzhiyun {
783*4882a593Smuzhiyun 	return platform_driver_register(&mpc52xx_gpt_driver);
784*4882a593Smuzhiyun }
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun /* Make sure GPIOs and IRQs get set up before anyone tries to use them */
787*4882a593Smuzhiyun subsys_initcall(mpc52xx_gpt_init);
788*4882a593Smuzhiyun device_initcall(mpc52xx_gpt_wdt_init);
789