1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * linux/arch/arm/mach-sa1100/ssp.c 4 * 5 * Copyright (C) 2003 Russell King. 6 * 7 * Generic SSP driver. This provides the generic core for simple 8 * IO-based SSP applications. 9 */ 10 #include <linux/module.h> 11 #include <linux/kernel.h> 12 #include <linux/sched.h> 13 #include <linux/errno.h> 14 #include <linux/interrupt.h> 15 #include <linux/ioport.h> 16 #include <linux/init.h> 17 #include <linux/io.h> 18 19 #include <mach/hardware.h> 20 #include <mach/irqs.h> 21 #include <asm/hardware/ssp.h> 22 23 #define TIMEOUT 100000 24 25 static irqreturn_t ssp_interrupt(int irq, void *dev_id) 26 { 27 unsigned int status = Ser4SSSR; 28 29 if (status & SSSR_ROR) 30 printk(KERN_WARNING "SSP: receiver overrun\n"); 31 32 Ser4SSSR = SSSR_ROR; 33 34 return status ? IRQ_HANDLED : IRQ_NONE; 35 } 36 37 /** 38 * ssp_write_word - write a word to the SSP port 39 * @data: 16-bit, MSB justified data to write. 40 * 41 * Wait for a free entry in the SSP transmit FIFO, and write a data 42 * word to the SSP port. Wait for the SSP port to start sending 43 * the data. 44 * 45 * The caller is expected to perform the necessary locking. 46 * 47 * Returns: 48 * %-ETIMEDOUT timeout occurred 49 * 0 success 50 */ 51 int ssp_write_word(u16 data) 52 { 53 int timeout = TIMEOUT; 54 55 while (!(Ser4SSSR & SSSR_TNF)) { 56 if (!--timeout) 57 return -ETIMEDOUT; 58 cpu_relax(); 59 } 60 61 Ser4SSDR = data; 62 63 timeout = TIMEOUT; 64 while (!(Ser4SSSR & SSSR_BSY)) { 65 if (!--timeout) 66 return -ETIMEDOUT; 67 cpu_relax(); 68 } 69 70 return 0; 71 } 72 73 /** 74 * ssp_read_word - read a word from the SSP port 75 * 76 * Wait for a data word in the SSP receive FIFO, and return the 77 * received data. Data is LSB justified. 78 * 79 * Note: Currently, if data is not expected to be received, this 80 * function will wait for ever. 81 * 82 * The caller is expected to perform the necessary locking. 83 * 84 * Returns: 85 * %-ETIMEDOUT timeout occurred 86 * 16-bit data success 87 */ 88 int ssp_read_word(u16 *data) 89 { 90 int timeout = TIMEOUT; 91 92 while (!(Ser4SSSR & SSSR_RNE)) { 93 if (!--timeout) 94 return -ETIMEDOUT; 95 cpu_relax(); 96 } 97 98 *data = (u16)Ser4SSDR; 99 100 return 0; 101 } 102 103 /** 104 * ssp_flush - flush the transmit and receive FIFOs 105 * 106 * Wait for the SSP to idle, and ensure that the receive FIFO 107 * is empty. 108 * 109 * The caller is expected to perform the necessary locking. 110 * 111 * Returns: 112 * %-ETIMEDOUT timeout occurred 113 * 0 success 114 */ 115 int ssp_flush(void) 116 { 117 int timeout = TIMEOUT * 2; 118 119 do { 120 while (Ser4SSSR & SSSR_RNE) { 121 if (!--timeout) 122 return -ETIMEDOUT; 123 (void) Ser4SSDR; 124 } 125 if (!--timeout) 126 return -ETIMEDOUT; 127 } while (Ser4SSSR & SSSR_BSY); 128 129 return 0; 130 } 131 132 /** 133 * ssp_enable - enable the SSP port 134 * 135 * Turn on the SSP port. 136 */ 137 void ssp_enable(void) 138 { 139 Ser4SSCR0 |= SSCR0_SSE; 140 } 141 142 /** 143 * ssp_disable - shut down the SSP port 144 * 145 * Turn off the SSP port, optionally powering it down. 146 */ 147 void ssp_disable(void) 148 { 149 Ser4SSCR0 &= ~SSCR0_SSE; 150 } 151 152 /** 153 * ssp_save_state - save the SSP configuration 154 * @ssp: pointer to structure to save SSP configuration 155 * 156 * Save the configured SSP state for suspend. 157 */ 158 void ssp_save_state(struct ssp_state *ssp) 159 { 160 ssp->cr0 = Ser4SSCR0; 161 ssp->cr1 = Ser4SSCR1; 162 163 Ser4SSCR0 &= ~SSCR0_SSE; 164 } 165 166 /** 167 * ssp_restore_state - restore a previously saved SSP configuration 168 * @ssp: pointer to configuration saved by ssp_save_state 169 * 170 * Restore the SSP configuration saved previously by ssp_save_state. 171 */ 172 void ssp_restore_state(struct ssp_state *ssp) 173 { 174 Ser4SSSR = SSSR_ROR; 175 176 Ser4SSCR0 = ssp->cr0 & ~SSCR0_SSE; 177 Ser4SSCR1 = ssp->cr1; 178 Ser4SSCR0 = ssp->cr0; 179 } 180 181 /** 182 * ssp_init - setup the SSP port 183 * 184 * initialise and claim resources for the SSP port. 185 * 186 * Returns: 187 * %-ENODEV if the SSP port is unavailable 188 * %-EBUSY if the resources are already in use 189 * %0 on success 190 */ 191 int ssp_init(void) 192 { 193 int ret; 194 195 if (!(PPAR & PPAR_SPR) && (Ser4MCCR0 & MCCR0_MCE)) 196 return -ENODEV; 197 198 if (!request_mem_region(__PREG(Ser4SSCR0), 0x18, "SSP")) { 199 return -EBUSY; 200 } 201 202 Ser4SSSR = SSSR_ROR; 203 204 ret = request_irq(IRQ_Ser4SSP, ssp_interrupt, 0, "SSP", NULL); 205 if (ret) 206 goto out_region; 207 208 return 0; 209 210 out_region: 211 release_mem_region(__PREG(Ser4SSCR0), 0x18); 212 return ret; 213 } 214 215 /** 216 * ssp_exit - undo the effects of ssp_init 217 * 218 * release and free resources for the SSP port. 219 */ 220 void ssp_exit(void) 221 { 222 Ser4SSCR0 &= ~SSCR0_SSE; 223 224 free_irq(IRQ_Ser4SSP, NULL); 225 release_mem_region(__PREG(Ser4SSCR0), 0x18); 226 } 227 228 MODULE_AUTHOR("Russell King"); 229 MODULE_DESCRIPTION("SA11x0 SSP PIO driver"); 230 MODULE_LICENSE("GPL"); 231 232 EXPORT_SYMBOL(ssp_write_word); 233 EXPORT_SYMBOL(ssp_read_word); 234 EXPORT_SYMBOL(ssp_flush); 235 EXPORT_SYMBOL(ssp_enable); 236 EXPORT_SYMBOL(ssp_disable); 237 EXPORT_SYMBOL(ssp_save_state); 238 EXPORT_SYMBOL(ssp_restore_state); 239 EXPORT_SYMBOL(ssp_init); 240 EXPORT_SYMBOL(ssp_exit); 241