18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * General Purpose functions for the global management of the
48c2ecf20Sopenharmony_ci * Communication Processor Module.
58c2ecf20Sopenharmony_ci * Copyright (c) 1997 Dan error_act (dmalek@jlc.net)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * In addition to the individual control of the communication
88c2ecf20Sopenharmony_ci * channels, there are a few functions that globally affect the
98c2ecf20Sopenharmony_ci * communication processor.
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * Buffer descriptors must be allocated from the dual ported memory
128c2ecf20Sopenharmony_ci * space.  The allocator for that is here.  When the communication
138c2ecf20Sopenharmony_ci * process is reset, we reclaim the memory available.  There is
148c2ecf20Sopenharmony_ci * currently no deallocator for this memory.
158c2ecf20Sopenharmony_ci * The amount of space available is platform dependent.  On the
168c2ecf20Sopenharmony_ci * MBX, the EPPC software loads additional microcode into the
178c2ecf20Sopenharmony_ci * communication processor, and uses some of the DP ram for this
188c2ecf20Sopenharmony_ci * purpose.  Current, the first 512 bytes and the last 256 bytes of
198c2ecf20Sopenharmony_ci * memory are used.  Right now I am conservative and only use the
208c2ecf20Sopenharmony_ci * memory that can never be used for microcode.  If there are
218c2ecf20Sopenharmony_ci * applications that require more DP ram, we can expand the boundaries
228c2ecf20Sopenharmony_ci * but then we have to be careful of any downloaded microcode.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci#include <linux/errno.h>
258c2ecf20Sopenharmony_ci#include <linux/sched.h>
268c2ecf20Sopenharmony_ci#include <linux/kernel.h>
278c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
288c2ecf20Sopenharmony_ci#include <linux/param.h>
298c2ecf20Sopenharmony_ci#include <linux/string.h>
308c2ecf20Sopenharmony_ci#include <linux/mm.h>
318c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
328c2ecf20Sopenharmony_ci#include <linux/irq.h>
338c2ecf20Sopenharmony_ci#include <linux/module.h>
348c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
358c2ecf20Sopenharmony_ci#include <linux/slab.h>
368c2ecf20Sopenharmony_ci#include <asm/page.h>
378c2ecf20Sopenharmony_ci#include <asm/8xx_immap.h>
388c2ecf20Sopenharmony_ci#include <asm/cpm1.h>
398c2ecf20Sopenharmony_ci#include <asm/io.h>
408c2ecf20Sopenharmony_ci#include <asm/rheap.h>
418c2ecf20Sopenharmony_ci#include <asm/prom.h>
428c2ecf20Sopenharmony_ci#include <asm/cpm.h>
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#include <asm/fs_pd.h>
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#ifdef CONFIG_8xx_GPIO
478c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
488c2ecf20Sopenharmony_ci#endif
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci#define CPM_MAP_SIZE    (0x4000)
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cicpm8xx_t __iomem *cpmp;  /* Pointer to comm processor space */
538c2ecf20Sopenharmony_ciimmap_t __iomem *mpc8xx_immr = (void __iomem *)VIRT_IMMR_BASE;
548c2ecf20Sopenharmony_cistatic cpic8xx_t __iomem *cpic_reg;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_cistatic struct irq_domain *cpm_pic_host;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void cpm_mask_irq(struct irq_data *d)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	clrbits32(&cpic_reg->cpic_cimr, (1 << cpm_vec));
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic void cpm_unmask_irq(struct irq_data *d)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	setbits32(&cpic_reg->cpic_cimr, (1 << cpm_vec));
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cistatic void cpm_end_irq(struct irq_data *d)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	unsigned int cpm_vec = (unsigned int)irqd_to_hwirq(d);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	out_be32(&cpic_reg->cpic_cisr, (1 << cpm_vec));
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic struct irq_chip cpm_pic = {
808c2ecf20Sopenharmony_ci	.name = "CPM PIC",
818c2ecf20Sopenharmony_ci	.irq_mask = cpm_mask_irq,
828c2ecf20Sopenharmony_ci	.irq_unmask = cpm_unmask_irq,
838c2ecf20Sopenharmony_ci	.irq_eoi = cpm_end_irq,
848c2ecf20Sopenharmony_ci};
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ciint cpm_get_irq(void)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	int cpm_vec;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	/*
918c2ecf20Sopenharmony_ci	 * Get the vector by setting the ACK bit and then reading
928c2ecf20Sopenharmony_ci	 * the register.
938c2ecf20Sopenharmony_ci	 */
948c2ecf20Sopenharmony_ci	out_be16(&cpic_reg->cpic_civr, 1);
958c2ecf20Sopenharmony_ci	cpm_vec = in_be16(&cpic_reg->cpic_civr);
968c2ecf20Sopenharmony_ci	cpm_vec >>= 11;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return irq_linear_revmap(cpm_pic_host, cpm_vec);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic int cpm_pic_host_map(struct irq_domain *h, unsigned int virq,
1028c2ecf20Sopenharmony_ci			  irq_hw_number_t hw)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	pr_debug("cpm_pic_host_map(%d, 0x%lx)\n", virq, hw);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	irq_set_status_flags(virq, IRQ_LEVEL);
1078c2ecf20Sopenharmony_ci	irq_set_chip_and_handler(virq, &cpm_pic, handle_fasteoi_irq);
1088c2ecf20Sopenharmony_ci	return 0;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/*
1128c2ecf20Sopenharmony_ci * The CPM can generate the error interrupt when there is a race condition
1138c2ecf20Sopenharmony_ci * between generating and masking interrupts.  All we have to do is ACK it
1148c2ecf20Sopenharmony_ci * and return.  This is a no-op function so we don't need any special
1158c2ecf20Sopenharmony_ci * tests in the interrupt handler.
1168c2ecf20Sopenharmony_ci */
1178c2ecf20Sopenharmony_cistatic irqreturn_t cpm_error_interrupt(int irq, void *dev)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic const struct irq_domain_ops cpm_pic_host_ops = {
1238c2ecf20Sopenharmony_ci	.map = cpm_pic_host_map,
1248c2ecf20Sopenharmony_ci};
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ciunsigned int __init cpm_pic_init(void)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	struct device_node *np = NULL;
1298c2ecf20Sopenharmony_ci	struct resource res;
1308c2ecf20Sopenharmony_ci	unsigned int sirq = 0, hwirq, eirq;
1318c2ecf20Sopenharmony_ci	int ret;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	pr_debug("cpm_pic_init\n");
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "fsl,cpm1-pic");
1368c2ecf20Sopenharmony_ci	if (np == NULL)
1378c2ecf20Sopenharmony_ci		np = of_find_compatible_node(NULL, "cpm-pic", "CPM");
1388c2ecf20Sopenharmony_ci	if (np == NULL) {
1398c2ecf20Sopenharmony_ci		printk(KERN_ERR "CPM PIC init: can not find cpm-pic node\n");
1408c2ecf20Sopenharmony_ci		return sirq;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	ret = of_address_to_resource(np, 0, &res);
1448c2ecf20Sopenharmony_ci	if (ret)
1458c2ecf20Sopenharmony_ci		goto end;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	cpic_reg = ioremap(res.start, resource_size(&res));
1488c2ecf20Sopenharmony_ci	if (cpic_reg == NULL)
1498c2ecf20Sopenharmony_ci		goto end;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	sirq = irq_of_parse_and_map(np, 0);
1528c2ecf20Sopenharmony_ci	if (!sirq)
1538c2ecf20Sopenharmony_ci		goto end;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* Initialize the CPM interrupt controller. */
1568c2ecf20Sopenharmony_ci	hwirq = (unsigned int)virq_to_hw(sirq);
1578c2ecf20Sopenharmony_ci	out_be32(&cpic_reg->cpic_cicr,
1588c2ecf20Sopenharmony_ci	    (CICR_SCD_SCC4 | CICR_SCC_SCC3 | CICR_SCB_SCC2 | CICR_SCA_SCC1) |
1598c2ecf20Sopenharmony_ci		((hwirq/2) << 13) | CICR_HP_MASK);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	out_be32(&cpic_reg->cpic_cimr, 0);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	cpm_pic_host = irq_domain_add_linear(np, 64, &cpm_pic_host_ops, NULL);
1648c2ecf20Sopenharmony_ci	if (cpm_pic_host == NULL) {
1658c2ecf20Sopenharmony_ci		printk(KERN_ERR "CPM2 PIC: failed to allocate irq host!\n");
1668c2ecf20Sopenharmony_ci		sirq = 0;
1678c2ecf20Sopenharmony_ci		goto end;
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* Install our own error handler. */
1718c2ecf20Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "fsl,cpm1");
1728c2ecf20Sopenharmony_ci	if (np == NULL)
1738c2ecf20Sopenharmony_ci		np = of_find_node_by_type(NULL, "cpm");
1748c2ecf20Sopenharmony_ci	if (np == NULL) {
1758c2ecf20Sopenharmony_ci		printk(KERN_ERR "CPM PIC init: can not find cpm node\n");
1768c2ecf20Sopenharmony_ci		goto end;
1778c2ecf20Sopenharmony_ci	}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	eirq = irq_of_parse_and_map(np, 0);
1808c2ecf20Sopenharmony_ci	if (!eirq)
1818c2ecf20Sopenharmony_ci		goto end;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (request_irq(eirq, cpm_error_interrupt, IRQF_NO_THREAD, "error",
1848c2ecf20Sopenharmony_ci			NULL))
1858c2ecf20Sopenharmony_ci		printk(KERN_ERR "Could not allocate CPM error IRQ!");
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	setbits32(&cpic_reg->cpic_cicr, CICR_IEN);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ciend:
1908c2ecf20Sopenharmony_ci	of_node_put(np);
1918c2ecf20Sopenharmony_ci	return sirq;
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_civoid __init cpm_reset(void)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	sysconf8xx_t __iomem *siu_conf;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	cpmp = &mpc8xx_immr->im_cpm;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci#ifndef CONFIG_PPC_EARLY_DEBUG_CPM
2018c2ecf20Sopenharmony_ci	/* Perform a reset. */
2028c2ecf20Sopenharmony_ci	out_be16(&cpmp->cp_cpcr, CPM_CR_RST | CPM_CR_FLG);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	/* Wait for it. */
2058c2ecf20Sopenharmony_ci	while (in_be16(&cpmp->cp_cpcr) & CPM_CR_FLG);
2068c2ecf20Sopenharmony_ci#endif
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci#ifdef CONFIG_UCODE_PATCH
2098c2ecf20Sopenharmony_ci	cpm_load_patch(cpmp);
2108c2ecf20Sopenharmony_ci#endif
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/*
2138c2ecf20Sopenharmony_ci	 * Set SDMA Bus Request priority 5.
2148c2ecf20Sopenharmony_ci	 * On 860T, this also enables FEC priority 6.  I am not sure
2158c2ecf20Sopenharmony_ci	 * this is what we really want for some applications, but the
2168c2ecf20Sopenharmony_ci	 * manual recommends it.
2178c2ecf20Sopenharmony_ci	 * Bit 25, FAM can also be set to use FEC aggressive mode (860T).
2188c2ecf20Sopenharmony_ci	 */
2198c2ecf20Sopenharmony_ci	siu_conf = immr_map(im_siu_conf);
2208c2ecf20Sopenharmony_ci	if ((mfspr(SPRN_IMMR) & 0xffff) == 0x0900) /* MPC885 */
2218c2ecf20Sopenharmony_ci		out_be32(&siu_conf->sc_sdcr, 0x40);
2228c2ecf20Sopenharmony_ci	else
2238c2ecf20Sopenharmony_ci		out_be32(&siu_conf->sc_sdcr, 1);
2248c2ecf20Sopenharmony_ci	immr_unmap(siu_conf);
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cmd_lock);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci#define MAX_CR_CMD_LOOPS        10000
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ciint cpm_command(u32 command, u8 opcode)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	int i, ret;
2348c2ecf20Sopenharmony_ci	unsigned long flags;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	if (command & 0xffffff0f)
2378c2ecf20Sopenharmony_ci		return -EINVAL;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cmd_lock, flags);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	ret = 0;
2428c2ecf20Sopenharmony_ci	out_be16(&cpmp->cp_cpcr, command | CPM_CR_FLG | (opcode << 8));
2438c2ecf20Sopenharmony_ci	for (i = 0; i < MAX_CR_CMD_LOOPS; i++)
2448c2ecf20Sopenharmony_ci		if ((in_be16(&cpmp->cp_cpcr) & CPM_CR_FLG) == 0)
2458c2ecf20Sopenharmony_ci			goto out;
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	printk(KERN_ERR "%s(): Not able to issue CPM command\n", __func__);
2488c2ecf20Sopenharmony_ci	ret = -EIO;
2498c2ecf20Sopenharmony_ciout:
2508c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cmd_lock, flags);
2518c2ecf20Sopenharmony_ci	return ret;
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cpm_command);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci/*
2568c2ecf20Sopenharmony_ci * Set a baud rate generator.  This needs lots of work.  There are
2578c2ecf20Sopenharmony_ci * four BRGs, any of which can be wired to any channel.
2588c2ecf20Sopenharmony_ci * The internal baud rate clock is the system clock divided by 16.
2598c2ecf20Sopenharmony_ci * This assumes the baudrate is 16x oversampled by the uart.
2608c2ecf20Sopenharmony_ci */
2618c2ecf20Sopenharmony_ci#define BRG_INT_CLK		(get_brgfreq())
2628c2ecf20Sopenharmony_ci#define BRG_UART_CLK		(BRG_INT_CLK/16)
2638c2ecf20Sopenharmony_ci#define BRG_UART_CLK_DIV16	(BRG_UART_CLK/16)
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_civoid
2668c2ecf20Sopenharmony_cicpm_setbrg(uint brg, uint rate)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	u32 __iomem *bp;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* This is good enough to get SMCs running..... */
2718c2ecf20Sopenharmony_ci	bp = &cpmp->cp_brgc1;
2728c2ecf20Sopenharmony_ci	bp += brg;
2738c2ecf20Sopenharmony_ci	/*
2748c2ecf20Sopenharmony_ci	 * The BRG has a 12-bit counter.  For really slow baud rates (or
2758c2ecf20Sopenharmony_ci	 * really fast processors), we may have to further divide by 16.
2768c2ecf20Sopenharmony_ci	 */
2778c2ecf20Sopenharmony_ci	if (((BRG_UART_CLK / rate) - 1) < 4096)
2788c2ecf20Sopenharmony_ci		out_be32(bp, (((BRG_UART_CLK / rate) - 1) << 1) | CPM_BRG_EN);
2798c2ecf20Sopenharmony_ci	else
2808c2ecf20Sopenharmony_ci		out_be32(bp, (((BRG_UART_CLK_DIV16 / rate) - 1) << 1) |
2818c2ecf20Sopenharmony_ci			      CPM_BRG_EN | CPM_BRG_DIV16);
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(cpm_setbrg);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistruct cpm_ioport16 {
2868c2ecf20Sopenharmony_ci	__be16 dir, par, odr_sor, dat, intr;
2878c2ecf20Sopenharmony_ci	__be16 res[3];
2888c2ecf20Sopenharmony_ci};
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_cistruct cpm_ioport32b {
2918c2ecf20Sopenharmony_ci	__be32 dir, par, odr, dat;
2928c2ecf20Sopenharmony_ci};
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cistruct cpm_ioport32e {
2958c2ecf20Sopenharmony_ci	__be32 dir, par, sor, odr, dat;
2968c2ecf20Sopenharmony_ci};
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_cistatic void __init cpm1_set_pin32(int port, int pin, int flags)
2998c2ecf20Sopenharmony_ci{
3008c2ecf20Sopenharmony_ci	struct cpm_ioport32e __iomem *iop;
3018c2ecf20Sopenharmony_ci	pin = 1 << (31 - pin);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	if (port == CPM_PORTB)
3048c2ecf20Sopenharmony_ci		iop = (struct cpm_ioport32e __iomem *)
3058c2ecf20Sopenharmony_ci		      &mpc8xx_immr->im_cpm.cp_pbdir;
3068c2ecf20Sopenharmony_ci	else
3078c2ecf20Sopenharmony_ci		iop = (struct cpm_ioport32e __iomem *)
3088c2ecf20Sopenharmony_ci		      &mpc8xx_immr->im_cpm.cp_pedir;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	if (flags & CPM_PIN_OUTPUT)
3118c2ecf20Sopenharmony_ci		setbits32(&iop->dir, pin);
3128c2ecf20Sopenharmony_ci	else
3138c2ecf20Sopenharmony_ci		clrbits32(&iop->dir, pin);
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	if (!(flags & CPM_PIN_GPIO))
3168c2ecf20Sopenharmony_ci		setbits32(&iop->par, pin);
3178c2ecf20Sopenharmony_ci	else
3188c2ecf20Sopenharmony_ci		clrbits32(&iop->par, pin);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (port == CPM_PORTB) {
3218c2ecf20Sopenharmony_ci		if (flags & CPM_PIN_OPENDRAIN)
3228c2ecf20Sopenharmony_ci			setbits16(&mpc8xx_immr->im_cpm.cp_pbodr, pin);
3238c2ecf20Sopenharmony_ci		else
3248c2ecf20Sopenharmony_ci			clrbits16(&mpc8xx_immr->im_cpm.cp_pbodr, pin);
3258c2ecf20Sopenharmony_ci	}
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	if (port == CPM_PORTE) {
3288c2ecf20Sopenharmony_ci		if (flags & CPM_PIN_SECONDARY)
3298c2ecf20Sopenharmony_ci			setbits32(&iop->sor, pin);
3308c2ecf20Sopenharmony_ci		else
3318c2ecf20Sopenharmony_ci			clrbits32(&iop->sor, pin);
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci		if (flags & CPM_PIN_OPENDRAIN)
3348c2ecf20Sopenharmony_ci			setbits32(&mpc8xx_immr->im_cpm.cp_peodr, pin);
3358c2ecf20Sopenharmony_ci		else
3368c2ecf20Sopenharmony_ci			clrbits32(&mpc8xx_immr->im_cpm.cp_peodr, pin);
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_cistatic void __init cpm1_set_pin16(int port, int pin, int flags)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct cpm_ioport16 __iomem *iop =
3438c2ecf20Sopenharmony_ci		(struct cpm_ioport16 __iomem *)&mpc8xx_immr->im_ioport;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	pin = 1 << (15 - pin);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (port != 0)
3488c2ecf20Sopenharmony_ci		iop += port - 1;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (flags & CPM_PIN_OUTPUT)
3518c2ecf20Sopenharmony_ci		setbits16(&iop->dir, pin);
3528c2ecf20Sopenharmony_ci	else
3538c2ecf20Sopenharmony_ci		clrbits16(&iop->dir, pin);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (!(flags & CPM_PIN_GPIO))
3568c2ecf20Sopenharmony_ci		setbits16(&iop->par, pin);
3578c2ecf20Sopenharmony_ci	else
3588c2ecf20Sopenharmony_ci		clrbits16(&iop->par, pin);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (port == CPM_PORTA) {
3618c2ecf20Sopenharmony_ci		if (flags & CPM_PIN_OPENDRAIN)
3628c2ecf20Sopenharmony_ci			setbits16(&iop->odr_sor, pin);
3638c2ecf20Sopenharmony_ci		else
3648c2ecf20Sopenharmony_ci			clrbits16(&iop->odr_sor, pin);
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci	if (port == CPM_PORTC) {
3678c2ecf20Sopenharmony_ci		if (flags & CPM_PIN_SECONDARY)
3688c2ecf20Sopenharmony_ci			setbits16(&iop->odr_sor, pin);
3698c2ecf20Sopenharmony_ci		else
3708c2ecf20Sopenharmony_ci			clrbits16(&iop->odr_sor, pin);
3718c2ecf20Sopenharmony_ci		if (flags & CPM_PIN_FALLEDGE)
3728c2ecf20Sopenharmony_ci			setbits16(&iop->intr, pin);
3738c2ecf20Sopenharmony_ci		else
3748c2ecf20Sopenharmony_ci			clrbits16(&iop->intr, pin);
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_civoid __init cpm1_set_pin(enum cpm_port port, int pin, int flags)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	if (port == CPM_PORTB || port == CPM_PORTE)
3818c2ecf20Sopenharmony_ci		cpm1_set_pin32(port, pin, flags);
3828c2ecf20Sopenharmony_ci	else
3838c2ecf20Sopenharmony_ci		cpm1_set_pin16(port, pin, flags);
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ciint __init cpm1_clk_setup(enum cpm_clk_target target, int clock, int mode)
3878c2ecf20Sopenharmony_ci{
3888c2ecf20Sopenharmony_ci	int shift;
3898c2ecf20Sopenharmony_ci	int i, bits = 0;
3908c2ecf20Sopenharmony_ci	u32 __iomem *reg;
3918c2ecf20Sopenharmony_ci	u32 mask = 7;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	u8 clk_map[][3] = {
3948c2ecf20Sopenharmony_ci		{CPM_CLK_SCC1, CPM_BRG1, 0},
3958c2ecf20Sopenharmony_ci		{CPM_CLK_SCC1, CPM_BRG2, 1},
3968c2ecf20Sopenharmony_ci		{CPM_CLK_SCC1, CPM_BRG3, 2},
3978c2ecf20Sopenharmony_ci		{CPM_CLK_SCC1, CPM_BRG4, 3},
3988c2ecf20Sopenharmony_ci		{CPM_CLK_SCC1, CPM_CLK1, 4},
3998c2ecf20Sopenharmony_ci		{CPM_CLK_SCC1, CPM_CLK2, 5},
4008c2ecf20Sopenharmony_ci		{CPM_CLK_SCC1, CPM_CLK3, 6},
4018c2ecf20Sopenharmony_ci		{CPM_CLK_SCC1, CPM_CLK4, 7},
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci		{CPM_CLK_SCC2, CPM_BRG1, 0},
4048c2ecf20Sopenharmony_ci		{CPM_CLK_SCC2, CPM_BRG2, 1},
4058c2ecf20Sopenharmony_ci		{CPM_CLK_SCC2, CPM_BRG3, 2},
4068c2ecf20Sopenharmony_ci		{CPM_CLK_SCC2, CPM_BRG4, 3},
4078c2ecf20Sopenharmony_ci		{CPM_CLK_SCC2, CPM_CLK1, 4},
4088c2ecf20Sopenharmony_ci		{CPM_CLK_SCC2, CPM_CLK2, 5},
4098c2ecf20Sopenharmony_ci		{CPM_CLK_SCC2, CPM_CLK3, 6},
4108c2ecf20Sopenharmony_ci		{CPM_CLK_SCC2, CPM_CLK4, 7},
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci		{CPM_CLK_SCC3, CPM_BRG1, 0},
4138c2ecf20Sopenharmony_ci		{CPM_CLK_SCC3, CPM_BRG2, 1},
4148c2ecf20Sopenharmony_ci		{CPM_CLK_SCC3, CPM_BRG3, 2},
4158c2ecf20Sopenharmony_ci		{CPM_CLK_SCC3, CPM_BRG4, 3},
4168c2ecf20Sopenharmony_ci		{CPM_CLK_SCC3, CPM_CLK5, 4},
4178c2ecf20Sopenharmony_ci		{CPM_CLK_SCC3, CPM_CLK6, 5},
4188c2ecf20Sopenharmony_ci		{CPM_CLK_SCC3, CPM_CLK7, 6},
4198c2ecf20Sopenharmony_ci		{CPM_CLK_SCC3, CPM_CLK8, 7},
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci		{CPM_CLK_SCC4, CPM_BRG1, 0},
4228c2ecf20Sopenharmony_ci		{CPM_CLK_SCC4, CPM_BRG2, 1},
4238c2ecf20Sopenharmony_ci		{CPM_CLK_SCC4, CPM_BRG3, 2},
4248c2ecf20Sopenharmony_ci		{CPM_CLK_SCC4, CPM_BRG4, 3},
4258c2ecf20Sopenharmony_ci		{CPM_CLK_SCC4, CPM_CLK5, 4},
4268c2ecf20Sopenharmony_ci		{CPM_CLK_SCC4, CPM_CLK6, 5},
4278c2ecf20Sopenharmony_ci		{CPM_CLK_SCC4, CPM_CLK7, 6},
4288c2ecf20Sopenharmony_ci		{CPM_CLK_SCC4, CPM_CLK8, 7},
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci		{CPM_CLK_SMC1, CPM_BRG1, 0},
4318c2ecf20Sopenharmony_ci		{CPM_CLK_SMC1, CPM_BRG2, 1},
4328c2ecf20Sopenharmony_ci		{CPM_CLK_SMC1, CPM_BRG3, 2},
4338c2ecf20Sopenharmony_ci		{CPM_CLK_SMC1, CPM_BRG4, 3},
4348c2ecf20Sopenharmony_ci		{CPM_CLK_SMC1, CPM_CLK1, 4},
4358c2ecf20Sopenharmony_ci		{CPM_CLK_SMC1, CPM_CLK2, 5},
4368c2ecf20Sopenharmony_ci		{CPM_CLK_SMC1, CPM_CLK3, 6},
4378c2ecf20Sopenharmony_ci		{CPM_CLK_SMC1, CPM_CLK4, 7},
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci		{CPM_CLK_SMC2, CPM_BRG1, 0},
4408c2ecf20Sopenharmony_ci		{CPM_CLK_SMC2, CPM_BRG2, 1},
4418c2ecf20Sopenharmony_ci		{CPM_CLK_SMC2, CPM_BRG3, 2},
4428c2ecf20Sopenharmony_ci		{CPM_CLK_SMC2, CPM_BRG4, 3},
4438c2ecf20Sopenharmony_ci		{CPM_CLK_SMC2, CPM_CLK5, 4},
4448c2ecf20Sopenharmony_ci		{CPM_CLK_SMC2, CPM_CLK6, 5},
4458c2ecf20Sopenharmony_ci		{CPM_CLK_SMC2, CPM_CLK7, 6},
4468c2ecf20Sopenharmony_ci		{CPM_CLK_SMC2, CPM_CLK8, 7},
4478c2ecf20Sopenharmony_ci	};
4488c2ecf20Sopenharmony_ci
4498c2ecf20Sopenharmony_ci	switch (target) {
4508c2ecf20Sopenharmony_ci	case CPM_CLK_SCC1:
4518c2ecf20Sopenharmony_ci		reg = &mpc8xx_immr->im_cpm.cp_sicr;
4528c2ecf20Sopenharmony_ci		shift = 0;
4538c2ecf20Sopenharmony_ci		break;
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	case CPM_CLK_SCC2:
4568c2ecf20Sopenharmony_ci		reg = &mpc8xx_immr->im_cpm.cp_sicr;
4578c2ecf20Sopenharmony_ci		shift = 8;
4588c2ecf20Sopenharmony_ci		break;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	case CPM_CLK_SCC3:
4618c2ecf20Sopenharmony_ci		reg = &mpc8xx_immr->im_cpm.cp_sicr;
4628c2ecf20Sopenharmony_ci		shift = 16;
4638c2ecf20Sopenharmony_ci		break;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	case CPM_CLK_SCC4:
4668c2ecf20Sopenharmony_ci		reg = &mpc8xx_immr->im_cpm.cp_sicr;
4678c2ecf20Sopenharmony_ci		shift = 24;
4688c2ecf20Sopenharmony_ci		break;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	case CPM_CLK_SMC1:
4718c2ecf20Sopenharmony_ci		reg = &mpc8xx_immr->im_cpm.cp_simode;
4728c2ecf20Sopenharmony_ci		shift = 12;
4738c2ecf20Sopenharmony_ci		break;
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	case CPM_CLK_SMC2:
4768c2ecf20Sopenharmony_ci		reg = &mpc8xx_immr->im_cpm.cp_simode;
4778c2ecf20Sopenharmony_ci		shift = 28;
4788c2ecf20Sopenharmony_ci		break;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	default:
4818c2ecf20Sopenharmony_ci		printk(KERN_ERR "cpm1_clock_setup: invalid clock target\n");
4828c2ecf20Sopenharmony_ci		return -EINVAL;
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(clk_map); i++) {
4868c2ecf20Sopenharmony_ci		if (clk_map[i][0] == target && clk_map[i][1] == clock) {
4878c2ecf20Sopenharmony_ci			bits = clk_map[i][2];
4888c2ecf20Sopenharmony_ci			break;
4898c2ecf20Sopenharmony_ci		}
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	if (i == ARRAY_SIZE(clk_map)) {
4938c2ecf20Sopenharmony_ci		printk(KERN_ERR "cpm1_clock_setup: invalid clock combination\n");
4948c2ecf20Sopenharmony_ci		return -EINVAL;
4958c2ecf20Sopenharmony_ci	}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	bits <<= shift;
4988c2ecf20Sopenharmony_ci	mask <<= shift;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	if (reg == &mpc8xx_immr->im_cpm.cp_sicr) {
5018c2ecf20Sopenharmony_ci		if (mode == CPM_CLK_RTX) {
5028c2ecf20Sopenharmony_ci			bits |= bits << 3;
5038c2ecf20Sopenharmony_ci			mask |= mask << 3;
5048c2ecf20Sopenharmony_ci		} else if (mode == CPM_CLK_RX) {
5058c2ecf20Sopenharmony_ci			bits <<= 3;
5068c2ecf20Sopenharmony_ci			mask <<= 3;
5078c2ecf20Sopenharmony_ci		}
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	out_be32(reg, (in_be32(reg) & ~mask) | bits);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	return 0;
5138c2ecf20Sopenharmony_ci}
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci/*
5168c2ecf20Sopenharmony_ci * GPIO LIB API implementation
5178c2ecf20Sopenharmony_ci */
5188c2ecf20Sopenharmony_ci#ifdef CONFIG_8xx_GPIO
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistruct cpm1_gpio16_chip {
5218c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip mm_gc;
5228c2ecf20Sopenharmony_ci	spinlock_t lock;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	/* shadowed data register to clear/set bits safely */
5258c2ecf20Sopenharmony_ci	u16 cpdata;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	/* IRQ associated with Pins when relevant */
5288c2ecf20Sopenharmony_ci	int irq[16];
5298c2ecf20Sopenharmony_ci};
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic void cpm1_gpio16_save_regs(struct of_mm_gpio_chip *mm_gc)
5328c2ecf20Sopenharmony_ci{
5338c2ecf20Sopenharmony_ci	struct cpm1_gpio16_chip *cpm1_gc =
5348c2ecf20Sopenharmony_ci		container_of(mm_gc, struct cpm1_gpio16_chip, mm_gc);
5358c2ecf20Sopenharmony_ci	struct cpm_ioport16 __iomem *iop = mm_gc->regs;
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	cpm1_gc->cpdata = in_be16(&iop->dat);
5388c2ecf20Sopenharmony_ci}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_cistatic int cpm1_gpio16_get(struct gpio_chip *gc, unsigned int gpio)
5418c2ecf20Sopenharmony_ci{
5428c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
5438c2ecf20Sopenharmony_ci	struct cpm_ioport16 __iomem *iop = mm_gc->regs;
5448c2ecf20Sopenharmony_ci	u16 pin_mask;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	pin_mask = 1 << (15 - gpio);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	return !!(in_be16(&iop->dat) & pin_mask);
5498c2ecf20Sopenharmony_ci}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_cistatic void __cpm1_gpio16_set(struct of_mm_gpio_chip *mm_gc, u16 pin_mask,
5528c2ecf20Sopenharmony_ci	int value)
5538c2ecf20Sopenharmony_ci{
5548c2ecf20Sopenharmony_ci	struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
5558c2ecf20Sopenharmony_ci	struct cpm_ioport16 __iomem *iop = mm_gc->regs;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	if (value)
5588c2ecf20Sopenharmony_ci		cpm1_gc->cpdata |= pin_mask;
5598c2ecf20Sopenharmony_ci	else
5608c2ecf20Sopenharmony_ci		cpm1_gc->cpdata &= ~pin_mask;
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	out_be16(&iop->dat, cpm1_gc->cpdata);
5638c2ecf20Sopenharmony_ci}
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_cistatic void cpm1_gpio16_set(struct gpio_chip *gc, unsigned int gpio, int value)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
5688c2ecf20Sopenharmony_ci	struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
5698c2ecf20Sopenharmony_ci	unsigned long flags;
5708c2ecf20Sopenharmony_ci	u16 pin_mask = 1 << (15 - gpio);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cpm1_gc->lock, flags);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	__cpm1_gpio16_set(mm_gc, pin_mask, value);
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cpm1_gc->lock, flags);
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic int cpm1_gpio16_to_irq(struct gpio_chip *gc, unsigned int gpio)
5808c2ecf20Sopenharmony_ci{
5818c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
5828c2ecf20Sopenharmony_ci	struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	return cpm1_gc->irq[gpio] ? : -ENXIO;
5858c2ecf20Sopenharmony_ci}
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_cistatic int cpm1_gpio16_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
5908c2ecf20Sopenharmony_ci	struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
5918c2ecf20Sopenharmony_ci	struct cpm_ioport16 __iomem *iop = mm_gc->regs;
5928c2ecf20Sopenharmony_ci	unsigned long flags;
5938c2ecf20Sopenharmony_ci	u16 pin_mask = 1 << (15 - gpio);
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cpm1_gc->lock, flags);
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci	setbits16(&iop->dir, pin_mask);
5988c2ecf20Sopenharmony_ci	__cpm1_gpio16_set(mm_gc, pin_mask, val);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cpm1_gc->lock, flags);
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci	return 0;
6038c2ecf20Sopenharmony_ci}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_cistatic int cpm1_gpio16_dir_in(struct gpio_chip *gc, unsigned int gpio)
6068c2ecf20Sopenharmony_ci{
6078c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
6088c2ecf20Sopenharmony_ci	struct cpm1_gpio16_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
6098c2ecf20Sopenharmony_ci	struct cpm_ioport16 __iomem *iop = mm_gc->regs;
6108c2ecf20Sopenharmony_ci	unsigned long flags;
6118c2ecf20Sopenharmony_ci	u16 pin_mask = 1 << (15 - gpio);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cpm1_gc->lock, flags);
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	clrbits16(&iop->dir, pin_mask);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cpm1_gc->lock, flags);
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	return 0;
6208c2ecf20Sopenharmony_ci}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ciint cpm1_gpiochip_add16(struct device *dev)
6238c2ecf20Sopenharmony_ci{
6248c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
6258c2ecf20Sopenharmony_ci	struct cpm1_gpio16_chip *cpm1_gc;
6268c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip *mm_gc;
6278c2ecf20Sopenharmony_ci	struct gpio_chip *gc;
6288c2ecf20Sopenharmony_ci	u16 mask;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	cpm1_gc = kzalloc(sizeof(*cpm1_gc), GFP_KERNEL);
6318c2ecf20Sopenharmony_ci	if (!cpm1_gc)
6328c2ecf20Sopenharmony_ci		return -ENOMEM;
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci	spin_lock_init(&cpm1_gc->lock);
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (!of_property_read_u16(np, "fsl,cpm1-gpio-irq-mask", &mask)) {
6378c2ecf20Sopenharmony_ci		int i, j;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		for (i = 0, j = 0; i < 16; i++)
6408c2ecf20Sopenharmony_ci			if (mask & (1 << (15 - i)))
6418c2ecf20Sopenharmony_ci				cpm1_gc->irq[i] = irq_of_parse_and_map(np, j++);
6428c2ecf20Sopenharmony_ci	}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	mm_gc = &cpm1_gc->mm_gc;
6458c2ecf20Sopenharmony_ci	gc = &mm_gc->gc;
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	mm_gc->save_regs = cpm1_gpio16_save_regs;
6488c2ecf20Sopenharmony_ci	gc->ngpio = 16;
6498c2ecf20Sopenharmony_ci	gc->direction_input = cpm1_gpio16_dir_in;
6508c2ecf20Sopenharmony_ci	gc->direction_output = cpm1_gpio16_dir_out;
6518c2ecf20Sopenharmony_ci	gc->get = cpm1_gpio16_get;
6528c2ecf20Sopenharmony_ci	gc->set = cpm1_gpio16_set;
6538c2ecf20Sopenharmony_ci	gc->to_irq = cpm1_gpio16_to_irq;
6548c2ecf20Sopenharmony_ci	gc->parent = dev;
6558c2ecf20Sopenharmony_ci	gc->owner = THIS_MODULE;
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	return of_mm_gpiochip_add_data(np, mm_gc, cpm1_gc);
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_cistruct cpm1_gpio32_chip {
6618c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip mm_gc;
6628c2ecf20Sopenharmony_ci	spinlock_t lock;
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	/* shadowed data register to clear/set bits safely */
6658c2ecf20Sopenharmony_ci	u32 cpdata;
6668c2ecf20Sopenharmony_ci};
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_cistatic void cpm1_gpio32_save_regs(struct of_mm_gpio_chip *mm_gc)
6698c2ecf20Sopenharmony_ci{
6708c2ecf20Sopenharmony_ci	struct cpm1_gpio32_chip *cpm1_gc =
6718c2ecf20Sopenharmony_ci		container_of(mm_gc, struct cpm1_gpio32_chip, mm_gc);
6728c2ecf20Sopenharmony_ci	struct cpm_ioport32b __iomem *iop = mm_gc->regs;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	cpm1_gc->cpdata = in_be32(&iop->dat);
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic int cpm1_gpio32_get(struct gpio_chip *gc, unsigned int gpio)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
6808c2ecf20Sopenharmony_ci	struct cpm_ioport32b __iomem *iop = mm_gc->regs;
6818c2ecf20Sopenharmony_ci	u32 pin_mask;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	pin_mask = 1 << (31 - gpio);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	return !!(in_be32(&iop->dat) & pin_mask);
6868c2ecf20Sopenharmony_ci}
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_cistatic void __cpm1_gpio32_set(struct of_mm_gpio_chip *mm_gc, u32 pin_mask,
6898c2ecf20Sopenharmony_ci	int value)
6908c2ecf20Sopenharmony_ci{
6918c2ecf20Sopenharmony_ci	struct cpm1_gpio32_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
6928c2ecf20Sopenharmony_ci	struct cpm_ioport32b __iomem *iop = mm_gc->regs;
6938c2ecf20Sopenharmony_ci
6948c2ecf20Sopenharmony_ci	if (value)
6958c2ecf20Sopenharmony_ci		cpm1_gc->cpdata |= pin_mask;
6968c2ecf20Sopenharmony_ci	else
6978c2ecf20Sopenharmony_ci		cpm1_gc->cpdata &= ~pin_mask;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	out_be32(&iop->dat, cpm1_gc->cpdata);
7008c2ecf20Sopenharmony_ci}
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_cistatic void cpm1_gpio32_set(struct gpio_chip *gc, unsigned int gpio, int value)
7038c2ecf20Sopenharmony_ci{
7048c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
7058c2ecf20Sopenharmony_ci	struct cpm1_gpio32_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
7068c2ecf20Sopenharmony_ci	unsigned long flags;
7078c2ecf20Sopenharmony_ci	u32 pin_mask = 1 << (31 - gpio);
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cpm1_gc->lock, flags);
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	__cpm1_gpio32_set(mm_gc, pin_mask, value);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cpm1_gc->lock, flags);
7148c2ecf20Sopenharmony_ci}
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_cistatic int cpm1_gpio32_dir_out(struct gpio_chip *gc, unsigned int gpio, int val)
7178c2ecf20Sopenharmony_ci{
7188c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
7198c2ecf20Sopenharmony_ci	struct cpm1_gpio32_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
7208c2ecf20Sopenharmony_ci	struct cpm_ioport32b __iomem *iop = mm_gc->regs;
7218c2ecf20Sopenharmony_ci	unsigned long flags;
7228c2ecf20Sopenharmony_ci	u32 pin_mask = 1 << (31 - gpio);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cpm1_gc->lock, flags);
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	setbits32(&iop->dir, pin_mask);
7278c2ecf20Sopenharmony_ci	__cpm1_gpio32_set(mm_gc, pin_mask, val);
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cpm1_gc->lock, flags);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	return 0;
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_cistatic int cpm1_gpio32_dir_in(struct gpio_chip *gc, unsigned int gpio)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc);
7378c2ecf20Sopenharmony_ci	struct cpm1_gpio32_chip *cpm1_gc = gpiochip_get_data(&mm_gc->gc);
7388c2ecf20Sopenharmony_ci	struct cpm_ioport32b __iomem *iop = mm_gc->regs;
7398c2ecf20Sopenharmony_ci	unsigned long flags;
7408c2ecf20Sopenharmony_ci	u32 pin_mask = 1 << (31 - gpio);
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	spin_lock_irqsave(&cpm1_gc->lock, flags);
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ci	clrbits32(&iop->dir, pin_mask);
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&cpm1_gc->lock, flags);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci	return 0;
7498c2ecf20Sopenharmony_ci}
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_ciint cpm1_gpiochip_add32(struct device *dev)
7528c2ecf20Sopenharmony_ci{
7538c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
7548c2ecf20Sopenharmony_ci	struct cpm1_gpio32_chip *cpm1_gc;
7558c2ecf20Sopenharmony_ci	struct of_mm_gpio_chip *mm_gc;
7568c2ecf20Sopenharmony_ci	struct gpio_chip *gc;
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	cpm1_gc = kzalloc(sizeof(*cpm1_gc), GFP_KERNEL);
7598c2ecf20Sopenharmony_ci	if (!cpm1_gc)
7608c2ecf20Sopenharmony_ci		return -ENOMEM;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	spin_lock_init(&cpm1_gc->lock);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	mm_gc = &cpm1_gc->mm_gc;
7658c2ecf20Sopenharmony_ci	gc = &mm_gc->gc;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	mm_gc->save_regs = cpm1_gpio32_save_regs;
7688c2ecf20Sopenharmony_ci	gc->ngpio = 32;
7698c2ecf20Sopenharmony_ci	gc->direction_input = cpm1_gpio32_dir_in;
7708c2ecf20Sopenharmony_ci	gc->direction_output = cpm1_gpio32_dir_out;
7718c2ecf20Sopenharmony_ci	gc->get = cpm1_gpio32_get;
7728c2ecf20Sopenharmony_ci	gc->set = cpm1_gpio32_set;
7738c2ecf20Sopenharmony_ci	gc->parent = dev;
7748c2ecf20Sopenharmony_ci	gc->owner = THIS_MODULE;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	return of_mm_gpiochip_add_data(np, mm_gc, cpm1_gc);
7778c2ecf20Sopenharmony_ci}
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci#endif /* CONFIG_8xx_GPIO */
780