18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/kernel.h>
108c2ecf20Sopenharmony_ci#include <linux/errno.h>
118c2ecf20Sopenharmony_ci#include <linux/export.h>
128c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
138c2ecf20Sopenharmony_ci#include <linux/log2.h>
148c2ecf20Sopenharmony_ci#include <bcm63xx_cpu.h>
158c2ecf20Sopenharmony_ci#include <bcm63xx_io.h>
168c2ecf20Sopenharmony_ci#include <bcm63xx_regs.h>
178c2ecf20Sopenharmony_ci#include <bcm63xx_cs.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(bcm63xx_cs_lock);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * check if given chip select exists
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_cistatic int is_valid_cs(unsigned int cs)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	if (cs > 6)
278c2ecf20Sopenharmony_ci		return 0;
288c2ecf20Sopenharmony_ci	return 1;
298c2ecf20Sopenharmony_ci}
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/*
328c2ecf20Sopenharmony_ci * Configure chipselect base address and size (bytes).
338c2ecf20Sopenharmony_ci * Size must be a power of two between 8k and 256M.
348c2ecf20Sopenharmony_ci */
358c2ecf20Sopenharmony_ciint bcm63xx_set_cs_base(unsigned int cs, u32 base, unsigned int size)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	unsigned long flags;
388c2ecf20Sopenharmony_ci	u32 val;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	if (!is_valid_cs(cs))
418c2ecf20Sopenharmony_ci		return -EINVAL;
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	/* sanity check on size */
448c2ecf20Sopenharmony_ci	if (size != roundup_pow_of_two(size))
458c2ecf20Sopenharmony_ci		return -EINVAL;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	if (size < 8 * 1024 || size > 256 * 1024 * 1024)
488c2ecf20Sopenharmony_ci		return -EINVAL;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	val = (base & MPI_CSBASE_BASE_MASK);
518c2ecf20Sopenharmony_ci	/* 8k => 0 - 256M => 15 */
528c2ecf20Sopenharmony_ci	val |= (ilog2(size) - ilog2(8 * 1024)) << MPI_CSBASE_SIZE_SHIFT;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	spin_lock_irqsave(&bcm63xx_cs_lock, flags);
558c2ecf20Sopenharmony_ci	bcm_mpi_writel(val, MPI_CSBASE_REG(cs));
568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&bcm63xx_cs_lock, flags);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	return 0;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bcm63xx_set_cs_base);
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci/*
648c2ecf20Sopenharmony_ci * configure chipselect timing (ns)
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_ciint bcm63xx_set_cs_timing(unsigned int cs, unsigned int wait,
678c2ecf20Sopenharmony_ci			   unsigned int setup, unsigned int hold)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	unsigned long flags;
708c2ecf20Sopenharmony_ci	u32 val;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (!is_valid_cs(cs))
738c2ecf20Sopenharmony_ci		return -EINVAL;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	spin_lock_irqsave(&bcm63xx_cs_lock, flags);
768c2ecf20Sopenharmony_ci	val = bcm_mpi_readl(MPI_CSCTL_REG(cs));
778c2ecf20Sopenharmony_ci	val &= ~(MPI_CSCTL_WAIT_MASK);
788c2ecf20Sopenharmony_ci	val &= ~(MPI_CSCTL_SETUP_MASK);
798c2ecf20Sopenharmony_ci	val &= ~(MPI_CSCTL_HOLD_MASK);
808c2ecf20Sopenharmony_ci	val |= wait << MPI_CSCTL_WAIT_SHIFT;
818c2ecf20Sopenharmony_ci	val |= setup << MPI_CSCTL_SETUP_SHIFT;
828c2ecf20Sopenharmony_ci	val |= hold << MPI_CSCTL_HOLD_SHIFT;
838c2ecf20Sopenharmony_ci	bcm_mpi_writel(val, MPI_CSCTL_REG(cs));
848c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&bcm63xx_cs_lock, flags);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return 0;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bcm63xx_set_cs_timing);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci/*
928c2ecf20Sopenharmony_ci * configure other chipselect parameter (data bus size, ...)
938c2ecf20Sopenharmony_ci */
948c2ecf20Sopenharmony_ciint bcm63xx_set_cs_param(unsigned int cs, u32 params)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	unsigned long flags;
978c2ecf20Sopenharmony_ci	u32 val;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (!is_valid_cs(cs))
1008c2ecf20Sopenharmony_ci		return -EINVAL;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* none of this fields apply to pcmcia */
1038c2ecf20Sopenharmony_ci	if (cs == MPI_CS_PCMCIA_COMMON ||
1048c2ecf20Sopenharmony_ci	    cs == MPI_CS_PCMCIA_ATTR ||
1058c2ecf20Sopenharmony_ci	    cs == MPI_CS_PCMCIA_IO)
1068c2ecf20Sopenharmony_ci		return -EINVAL;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	spin_lock_irqsave(&bcm63xx_cs_lock, flags);
1098c2ecf20Sopenharmony_ci	val = bcm_mpi_readl(MPI_CSCTL_REG(cs));
1108c2ecf20Sopenharmony_ci	val &= ~(MPI_CSCTL_DATA16_MASK);
1118c2ecf20Sopenharmony_ci	val &= ~(MPI_CSCTL_SYNCMODE_MASK);
1128c2ecf20Sopenharmony_ci	val &= ~(MPI_CSCTL_TSIZE_MASK);
1138c2ecf20Sopenharmony_ci	val &= ~(MPI_CSCTL_ENDIANSWAP_MASK);
1148c2ecf20Sopenharmony_ci	val |= params;
1158c2ecf20Sopenharmony_ci	bcm_mpi_writel(val, MPI_CSCTL_REG(cs));
1168c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&bcm63xx_cs_lock, flags);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return 0;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bcm63xx_set_cs_param);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci/*
1248c2ecf20Sopenharmony_ci * set cs status (enable/disable)
1258c2ecf20Sopenharmony_ci */
1268c2ecf20Sopenharmony_ciint bcm63xx_set_cs_status(unsigned int cs, int enable)
1278c2ecf20Sopenharmony_ci{
1288c2ecf20Sopenharmony_ci	unsigned long flags;
1298c2ecf20Sopenharmony_ci	u32 val;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (!is_valid_cs(cs))
1328c2ecf20Sopenharmony_ci		return -EINVAL;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	spin_lock_irqsave(&bcm63xx_cs_lock, flags);
1358c2ecf20Sopenharmony_ci	val = bcm_mpi_readl(MPI_CSCTL_REG(cs));
1368c2ecf20Sopenharmony_ci	if (enable)
1378c2ecf20Sopenharmony_ci		val |= MPI_CSCTL_ENABLE_MASK;
1388c2ecf20Sopenharmony_ci	else
1398c2ecf20Sopenharmony_ci		val &= ~MPI_CSCTL_ENABLE_MASK;
1408c2ecf20Sopenharmony_ci	bcm_mpi_writel(val, MPI_CSCTL_REG(cs));
1418c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&bcm63xx_cs_lock, flags);
1428c2ecf20Sopenharmony_ci	return 0;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bcm63xx_set_cs_status);
146