162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * arch/powerpc/sysdev/qe_lib/qe_io.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * QE Parallel I/O ports configuration routines
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright 2006 Freescale Semiconductor, Inc. All rights reserved.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Author: Li Yang <LeoLi@freescale.com>
1062306a36Sopenharmony_ci * Based on code from Shlomi Gridish <gridish@freescale.com>
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/stddef.h>
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/errno.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/ioport.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <asm/io.h>
2062306a36Sopenharmony_ci#include <soc/fsl/qe/qe.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#undef DEBUG
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic struct qe_pio_regs __iomem *par_io;
2562306a36Sopenharmony_cistatic int num_par_io_ports = 0;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ciint par_io_init(struct device_node *np)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	struct resource res;
3062306a36Sopenharmony_ci	int ret;
3162306a36Sopenharmony_ci	u32 num_ports;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	/* Map Parallel I/O ports registers */
3462306a36Sopenharmony_ci	ret = of_address_to_resource(np, 0, &res);
3562306a36Sopenharmony_ci	if (ret)
3662306a36Sopenharmony_ci		return ret;
3762306a36Sopenharmony_ci	par_io = ioremap(res.start, resource_size(&res));
3862306a36Sopenharmony_ci	if (!par_io)
3962306a36Sopenharmony_ci		return -ENOMEM;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (!of_property_read_u32(np, "num-ports", &num_ports))
4262306a36Sopenharmony_ci		num_par_io_ports = num_ports;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_civoid __par_io_config_pin(struct qe_pio_regs __iomem *par_io, u8 pin, int dir,
4862306a36Sopenharmony_ci			 int open_drain, int assignment, int has_irq)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	u32 pin_mask1bit;
5162306a36Sopenharmony_ci	u32 pin_mask2bits;
5262306a36Sopenharmony_ci	u32 new_mask2bits;
5362306a36Sopenharmony_ci	u32 tmp_val;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* calculate pin location for single and 2 bits information */
5662306a36Sopenharmony_ci	pin_mask1bit = (u32) (1 << (QE_PIO_PINS - (pin + 1)));
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	/* Set open drain, if required */
5962306a36Sopenharmony_ci	tmp_val = ioread32be(&par_io->cpodr);
6062306a36Sopenharmony_ci	if (open_drain)
6162306a36Sopenharmony_ci		iowrite32be(pin_mask1bit | tmp_val, &par_io->cpodr);
6262306a36Sopenharmony_ci	else
6362306a36Sopenharmony_ci		iowrite32be(~pin_mask1bit & tmp_val, &par_io->cpodr);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	/* define direction */
6662306a36Sopenharmony_ci	tmp_val = (pin > (QE_PIO_PINS / 2) - 1) ?
6762306a36Sopenharmony_ci		ioread32be(&par_io->cpdir2) :
6862306a36Sopenharmony_ci		ioread32be(&par_io->cpdir1);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* get all bits mask for 2 bit per port */
7162306a36Sopenharmony_ci	pin_mask2bits = (u32) (0x3 << (QE_PIO_PINS -
7262306a36Sopenharmony_ci				(pin % (QE_PIO_PINS / 2) + 1) * 2));
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* Get the final mask we need for the right definition */
7562306a36Sopenharmony_ci	new_mask2bits = (u32) (dir << (QE_PIO_PINS -
7662306a36Sopenharmony_ci				(pin % (QE_PIO_PINS / 2) + 1) * 2));
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* clear and set 2 bits mask */
7962306a36Sopenharmony_ci	if (pin > (QE_PIO_PINS / 2) - 1) {
8062306a36Sopenharmony_ci		iowrite32be(~pin_mask2bits & tmp_val, &par_io->cpdir2);
8162306a36Sopenharmony_ci		tmp_val &= ~pin_mask2bits;
8262306a36Sopenharmony_ci		iowrite32be(new_mask2bits | tmp_val, &par_io->cpdir2);
8362306a36Sopenharmony_ci	} else {
8462306a36Sopenharmony_ci		iowrite32be(~pin_mask2bits & tmp_val, &par_io->cpdir1);
8562306a36Sopenharmony_ci		tmp_val &= ~pin_mask2bits;
8662306a36Sopenharmony_ci		iowrite32be(new_mask2bits | tmp_val, &par_io->cpdir1);
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci	/* define pin assignment */
8962306a36Sopenharmony_ci	tmp_val = (pin > (QE_PIO_PINS / 2) - 1) ?
9062306a36Sopenharmony_ci		ioread32be(&par_io->cppar2) :
9162306a36Sopenharmony_ci		ioread32be(&par_io->cppar1);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	new_mask2bits = (u32) (assignment << (QE_PIO_PINS -
9462306a36Sopenharmony_ci			(pin % (QE_PIO_PINS / 2) + 1) * 2));
9562306a36Sopenharmony_ci	/* clear and set 2 bits mask */
9662306a36Sopenharmony_ci	if (pin > (QE_PIO_PINS / 2) - 1) {
9762306a36Sopenharmony_ci		iowrite32be(~pin_mask2bits & tmp_val, &par_io->cppar2);
9862306a36Sopenharmony_ci		tmp_val &= ~pin_mask2bits;
9962306a36Sopenharmony_ci		iowrite32be(new_mask2bits | tmp_val, &par_io->cppar2);
10062306a36Sopenharmony_ci	} else {
10162306a36Sopenharmony_ci		iowrite32be(~pin_mask2bits & tmp_val, &par_io->cppar1);
10262306a36Sopenharmony_ci		tmp_val &= ~pin_mask2bits;
10362306a36Sopenharmony_ci		iowrite32be(new_mask2bits | tmp_val, &par_io->cppar1);
10462306a36Sopenharmony_ci	}
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ciEXPORT_SYMBOL(__par_io_config_pin);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ciint par_io_config_pin(u8 port, u8 pin, int dir, int open_drain,
10962306a36Sopenharmony_ci		      int assignment, int has_irq)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	if (!par_io || port >= num_par_io_ports)
11262306a36Sopenharmony_ci		return -EINVAL;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	__par_io_config_pin(&par_io[port], pin, dir, open_drain, assignment,
11562306a36Sopenharmony_ci			    has_irq);
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ciEXPORT_SYMBOL(par_io_config_pin);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ciint par_io_data_set(u8 port, u8 pin, u8 val)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	u32 pin_mask, tmp_val;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	if (port >= num_par_io_ports)
12562306a36Sopenharmony_ci		return -EINVAL;
12662306a36Sopenharmony_ci	if (pin >= QE_PIO_PINS)
12762306a36Sopenharmony_ci		return -EINVAL;
12862306a36Sopenharmony_ci	/* calculate pin location */
12962306a36Sopenharmony_ci	pin_mask = (u32) (1 << (QE_PIO_PINS - 1 - pin));
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	tmp_val = ioread32be(&par_io[port].cpdata);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	if (val == 0)		/* clear */
13462306a36Sopenharmony_ci		iowrite32be(~pin_mask & tmp_val, &par_io[port].cpdata);
13562306a36Sopenharmony_ci	else			/* set */
13662306a36Sopenharmony_ci		iowrite32be(pin_mask | tmp_val, &par_io[port].cpdata);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return 0;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ciEXPORT_SYMBOL(par_io_data_set);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ciint par_io_of_config(struct device_node *np)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct device_node *pio;
14562306a36Sopenharmony_ci	int pio_map_len;
14662306a36Sopenharmony_ci	const __be32 *pio_map;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (par_io == NULL) {
14962306a36Sopenharmony_ci		printk(KERN_ERR "par_io not initialized\n");
15062306a36Sopenharmony_ci		return -1;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	pio = of_parse_phandle(np, "pio-handle", 0);
15462306a36Sopenharmony_ci	if (pio == NULL) {
15562306a36Sopenharmony_ci		printk(KERN_ERR "pio-handle not available\n");
15662306a36Sopenharmony_ci		return -1;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	pio_map = of_get_property(pio, "pio-map", &pio_map_len);
16062306a36Sopenharmony_ci	if (pio_map == NULL) {
16162306a36Sopenharmony_ci		printk(KERN_ERR "pio-map is not set!\n");
16262306a36Sopenharmony_ci		return -1;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci	pio_map_len /= sizeof(unsigned int);
16562306a36Sopenharmony_ci	if ((pio_map_len % 6) != 0) {
16662306a36Sopenharmony_ci		printk(KERN_ERR "pio-map format wrong!\n");
16762306a36Sopenharmony_ci		return -1;
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	while (pio_map_len > 0) {
17162306a36Sopenharmony_ci		u8 port        = be32_to_cpu(pio_map[0]);
17262306a36Sopenharmony_ci		u8 pin         = be32_to_cpu(pio_map[1]);
17362306a36Sopenharmony_ci		int dir        = be32_to_cpu(pio_map[2]);
17462306a36Sopenharmony_ci		int open_drain = be32_to_cpu(pio_map[3]);
17562306a36Sopenharmony_ci		int assignment = be32_to_cpu(pio_map[4]);
17662306a36Sopenharmony_ci		int has_irq    = be32_to_cpu(pio_map[5]);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		par_io_config_pin(port, pin, dir, open_drain,
17962306a36Sopenharmony_ci				  assignment, has_irq);
18062306a36Sopenharmony_ci		pio_map += 6;
18162306a36Sopenharmony_ci		pio_map_len -= 6;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci	of_node_put(pio);
18462306a36Sopenharmony_ci	return 0;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ciEXPORT_SYMBOL(par_io_of_config);
187