1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr> 7 */ 8 9#include <linux/kernel.h> 10#include <linux/module.h> 11#include <linux/spinlock.h> 12#include <linux/log2.h> 13#include <bcm63xx_cpu.h> 14#include <bcm63xx_io.h> 15#include <bcm63xx_regs.h> 16#include <bcm63xx_cs.h> 17 18static DEFINE_SPINLOCK(bcm63xx_cs_lock); 19 20/* 21 * check if given chip select exists 22 */ 23static int is_valid_cs(unsigned int cs) 24{ 25 if (cs > 6) 26 return 0; 27 return 1; 28} 29 30/* 31 * Configure chipselect base address and size (bytes). 32 * Size must be a power of two between 8k and 256M. 33 */ 34int bcm63xx_set_cs_base(unsigned int cs, u32 base, unsigned int size) 35{ 36 unsigned long flags; 37 u32 val; 38 39 if (!is_valid_cs(cs)) 40 return -EINVAL; 41 42 /* sanity check on size */ 43 if (size != roundup_pow_of_two(size)) 44 return -EINVAL; 45 46 if (size < 8 * 1024 || size > 256 * 1024 * 1024) 47 return -EINVAL; 48 49 val = (base & MPI_CSBASE_BASE_MASK); 50 /* 8k => 0 - 256M => 15 */ 51 val |= (ilog2(size) - ilog2(8 * 1024)) << MPI_CSBASE_SIZE_SHIFT; 52 53 spin_lock_irqsave(&bcm63xx_cs_lock, flags); 54 bcm_mpi_writel(val, MPI_CSBASE_REG(cs)); 55 spin_unlock_irqrestore(&bcm63xx_cs_lock, flags); 56 57 return 0; 58} 59 60EXPORT_SYMBOL(bcm63xx_set_cs_base); 61 62/* 63 * configure chipselect timing (ns) 64 */ 65int bcm63xx_set_cs_timing(unsigned int cs, unsigned int wait, 66 unsigned int setup, unsigned int hold) 67{ 68 unsigned long flags; 69 u32 val; 70 71 if (!is_valid_cs(cs)) 72 return -EINVAL; 73 74 spin_lock_irqsave(&bcm63xx_cs_lock, flags); 75 val = bcm_mpi_readl(MPI_CSCTL_REG(cs)); 76 val &= ~(MPI_CSCTL_WAIT_MASK); 77 val &= ~(MPI_CSCTL_SETUP_MASK); 78 val &= ~(MPI_CSCTL_HOLD_MASK); 79 val |= wait << MPI_CSCTL_WAIT_SHIFT; 80 val |= setup << MPI_CSCTL_SETUP_SHIFT; 81 val |= hold << MPI_CSCTL_HOLD_SHIFT; 82 bcm_mpi_writel(val, MPI_CSCTL_REG(cs)); 83 spin_unlock_irqrestore(&bcm63xx_cs_lock, flags); 84 85 return 0; 86} 87 88EXPORT_SYMBOL(bcm63xx_set_cs_timing); 89 90/* 91 * configure other chipselect parameter (data bus size, ...) 92 */ 93int bcm63xx_set_cs_param(unsigned int cs, u32 params) 94{ 95 unsigned long flags; 96 u32 val; 97 98 if (!is_valid_cs(cs)) 99 return -EINVAL; 100 101 /* none of this fields apply to pcmcia */ 102 if (cs == MPI_CS_PCMCIA_COMMON || 103 cs == MPI_CS_PCMCIA_ATTR || 104 cs == MPI_CS_PCMCIA_IO) 105 return -EINVAL; 106 107 spin_lock_irqsave(&bcm63xx_cs_lock, flags); 108 val = bcm_mpi_readl(MPI_CSCTL_REG(cs)); 109 val &= ~(MPI_CSCTL_DATA16_MASK); 110 val &= ~(MPI_CSCTL_SYNCMODE_MASK); 111 val &= ~(MPI_CSCTL_TSIZE_MASK); 112 val &= ~(MPI_CSCTL_ENDIANSWAP_MASK); 113 val |= params; 114 bcm_mpi_writel(val, MPI_CSCTL_REG(cs)); 115 spin_unlock_irqrestore(&bcm63xx_cs_lock, flags); 116 117 return 0; 118} 119 120EXPORT_SYMBOL(bcm63xx_set_cs_param); 121 122/* 123 * set cs status (enable/disable) 124 */ 125int bcm63xx_set_cs_status(unsigned int cs, int enable) 126{ 127 unsigned long flags; 128 u32 val; 129 130 if (!is_valid_cs(cs)) 131 return -EINVAL; 132 133 spin_lock_irqsave(&bcm63xx_cs_lock, flags); 134 val = bcm_mpi_readl(MPI_CSCTL_REG(cs)); 135 if (enable) 136 val |= MPI_CSCTL_ENABLE_MASK; 137 else 138 val &= ~MPI_CSCTL_ENABLE_MASK; 139 bcm_mpi_writel(val, MPI_CSCTL_REG(cs)); 140 spin_unlock_irqrestore(&bcm63xx_cs_lock, flags); 141 return 0; 142} 143 144EXPORT_SYMBOL(bcm63xx_set_cs_status); 145