diff options
Diffstat (limited to 'arch/powerpc/sysdev/qe_lib/ucc.c')
| -rw-r--r-- | arch/powerpc/sysdev/qe_lib/ucc.c | 251 | 
1 files changed, 251 insertions, 0 deletions
diff --git a/arch/powerpc/sysdev/qe_lib/ucc.c b/arch/powerpc/sysdev/qe_lib/ucc.c new file mode 100644 index 00000000000..916c9e5df57 --- /dev/null +++ b/arch/powerpc/sysdev/qe_lib/ucc.c @@ -0,0 +1,251 @@ +/* + * arch/powerpc/sysdev/qe_lib/ucc.c + * + * QE UCC API Set - UCC specific routines implementations. + * + * Copyright (C) 2006 Freescale Semicondutor, Inc. All rights reserved. + * + * Authors: 	Shlomi Gridish <gridish@freescale.com> + * 		Li Yang <leoli@freescale.com> + * + * This program is free software; you can redistribute  it and/or modify it + * under  the terms of  the GNU General  Public License as published by the + * Free Software Foundation;  either version 2 of the  License, or (at your + * option) any later version. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include <linux/stddef.h> + +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/immap_qe.h> +#include <asm/qe.h> +#include <asm/ucc.h> + +static DEFINE_SPINLOCK(ucc_lock); + +int ucc_set_qe_mux_mii_mng(int ucc_num) +{ +	unsigned long flags; + +	spin_lock_irqsave(&ucc_lock, flags); +	out_be32(&qe_immr->qmx.cmxgcr, +		 ((in_be32(&qe_immr->qmx.cmxgcr) & +		   ~QE_CMXGCR_MII_ENET_MNG) | +		  (ucc_num << QE_CMXGCR_MII_ENET_MNG_SHIFT))); +	spin_unlock_irqrestore(&ucc_lock, flags); + +	return 0; +} + +int ucc_set_type(int ucc_num, struct ucc_common *regs, +		 enum ucc_speed_type speed) +{ +	u8 guemr = 0; + +	/* check if the UCC number is in range. */ +	if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) +		return -EINVAL; + +	guemr = regs->guemr; +	guemr &= ~(UCC_GUEMR_MODE_MASK_RX | UCC_GUEMR_MODE_MASK_TX); +	switch (speed) { +	case UCC_SPEED_TYPE_SLOW: +		guemr |= (UCC_GUEMR_MODE_SLOW_RX | UCC_GUEMR_MODE_SLOW_TX); +		break; +	case UCC_SPEED_TYPE_FAST: +		guemr |= (UCC_GUEMR_MODE_FAST_RX | UCC_GUEMR_MODE_FAST_TX); +		break; +	default: +		return -EINVAL; +	} +	regs->guemr = guemr; + +	return 0; +} + +int ucc_init_guemr(struct ucc_common *regs) +{ +	u8 guemr = 0; + +	if (!regs) +		return -EINVAL; + +	/* Set bit 3 (which is reserved in the GUEMR register) to 1 */ +	guemr = UCC_GUEMR_SET_RESERVED3; + +	regs->guemr = guemr; + +	return 0; +} + +static void get_cmxucr_reg(int ucc_num, volatile u32 ** p_cmxucr, u8 * reg_num, +			   u8 * shift) +{ +	switch (ucc_num) { +	case 0: *p_cmxucr = &(qe_immr->qmx.cmxucr1); +		*reg_num = 1; +		*shift = 16; +		break; +	case 2: *p_cmxucr = &(qe_immr->qmx.cmxucr1); +		*reg_num = 1; +		*shift = 0; +		break; +	case 4: *p_cmxucr = &(qe_immr->qmx.cmxucr2); +		*reg_num = 2; +		*shift = 16; +		break; +	case 6: *p_cmxucr = &(qe_immr->qmx.cmxucr2); +		*reg_num = 2; +		*shift = 0; +		break; +	case 1: *p_cmxucr = &(qe_immr->qmx.cmxucr3); +		*reg_num = 3; +		*shift = 16; +		break; +	case 3: *p_cmxucr = &(qe_immr->qmx.cmxucr3); +		*reg_num = 3; +		*shift = 0; +		break; +	case 5: *p_cmxucr = &(qe_immr->qmx.cmxucr4); +		*reg_num = 4; +		*shift = 16; +		break; +	case 7: *p_cmxucr = &(qe_immr->qmx.cmxucr4); +		*reg_num = 4; +		*shift = 0; +		break; +	default: +		break; +	} +} + +int ucc_mux_set_grant_tsa_bkpt(int ucc_num, int set, u32 mask) +{ +	volatile u32 *p_cmxucr; +	u8 reg_num; +	u8 shift; + +	/* check if the UCC number is in range. */ +	if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) +		return -EINVAL; + +	get_cmxucr_reg(ucc_num, &p_cmxucr, ®_num, &shift); + +	if (set) +		out_be32(p_cmxucr, in_be32(p_cmxucr) | (mask << shift)); +	else +		out_be32(p_cmxucr, in_be32(p_cmxucr) & ~(mask << shift)); + +	return 0; +} + +int ucc_set_qe_mux_rxtx(int ucc_num, enum qe_clock clock, enum comm_dir mode) +{ +	volatile u32 *p_cmxucr; +	u8 reg_num; +	u8 shift; +	u32 clock_bits; +	u32 clock_mask; +	int source = -1; + +	/* check if the UCC number is in range. */ +	if ((ucc_num > UCC_MAX_NUM - 1) || (ucc_num < 0)) +		return -EINVAL; + +	if (!((mode == COMM_DIR_RX) || (mode == COMM_DIR_TX))) { +		printk(KERN_ERR +		       "ucc_set_qe_mux_rxtx: bad comm mode type passed."); +		return -EINVAL; +	} + +	get_cmxucr_reg(ucc_num, &p_cmxucr, ®_num, &shift); + +	switch (reg_num) { +	case 1: +		switch (clock) { +		case QE_BRG1:	source = 1; break; +		case QE_BRG2:	source = 2; break; +		case QE_BRG7:	source = 3; break; +		case QE_BRG8:	source = 4; break; +		case QE_CLK9:	source = 5; break; +		case QE_CLK10:	source = 6; break; +		case QE_CLK11:	source = 7; break; +		case QE_CLK12:	source = 8; break; +		case QE_CLK15:	source = 9; break; +		case QE_CLK16:	source = 10; break; +		default: 	source = -1; break; +		} +		break; +	case 2: +		switch (clock) { +		case QE_BRG5:	source = 1; break; +		case QE_BRG6:	source = 2; break; +		case QE_BRG7:	source = 3; break; +		case QE_BRG8:	source = 4; break; +		case QE_CLK13:	source = 5; break; +		case QE_CLK14:	source = 6; break; +		case QE_CLK19:	source = 7; break; +		case QE_CLK20:	source = 8; break; +		case QE_CLK15:	source = 9; break; +		case QE_CLK16:	source = 10; break; +		default: 	source = -1; break; +		} +		break; +	case 3: +		switch (clock) { +		case QE_BRG9:	source = 1; break; +		case QE_BRG10:	source = 2; break; +		case QE_BRG15:	source = 3; break; +		case QE_BRG16:	source = 4; break; +		case QE_CLK3:	source = 5; break; +		case QE_CLK4:	source = 6; break; +		case QE_CLK17:	source = 7; break; +		case QE_CLK18:	source = 8; break; +		case QE_CLK7:	source = 9; break; +		case QE_CLK8:	source = 10; break; +		default:	source = -1; break; +		} +		break; +	case 4: +		switch (clock) { +		case QE_BRG13:	source = 1; break; +		case QE_BRG14:	source = 2; break; +		case QE_BRG15:	source = 3; break; +		case QE_BRG16:	source = 4; break; +		case QE_CLK5:	source = 5; break; +		case QE_CLK6:	source = 6; break; +		case QE_CLK21:	source = 7; break; +		case QE_CLK22:	source = 8; break; +		case QE_CLK7:	source = 9; break; +		case QE_CLK8:	source = 10; break; +		default: 	source = -1; break; +		} +		break; +	default: +		source = -1; +		break; +	} + +	if (source == -1) { +		printk(KERN_ERR +		     "ucc_set_qe_mux_rxtx: Bad combination of clock and UCC."); +		return -ENOENT; +	} + +	clock_bits = (u32) source; +	clock_mask = QE_CMXUCR_TX_CLK_SRC_MASK; +	if (mode == COMM_DIR_RX) { +		clock_bits <<= 4;  /* Rx field is 4 bits to left of Tx field */ +		clock_mask <<= 4;  /* Rx field is 4 bits to left of Tx field */ +	} +	clock_bits <<= shift; +	clock_mask <<= shift; + +	out_be32(p_cmxucr, (in_be32(p_cmxucr) & ~clock_mask) | clock_bits); + +	return 0; +}  |