| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 1 | /* | 
|  | 2 | * SPI bus driver for the Topcliff PCH used by Intel SoCs | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 3 | * | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 4 | * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD. | 
|  | 5 | * | 
|  | 6 | * This program is free software; you can redistribute it and/or modify | 
|  | 7 | * it under the terms of the GNU General Public License as published by | 
|  | 8 | * the Free Software Foundation; version 2 of the License. | 
|  | 9 | * | 
|  | 10 | * This program is distributed in the hope that it will be useful, | 
|  | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 13 | * GNU General Public License for more details. | 
|  | 14 | * | 
|  | 15 | * You should have received a copy of the GNU General Public License | 
|  | 16 | * along with this program; if not, write to the Free Software | 
|  | 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA. | 
|  | 18 | */ | 
|  | 19 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 20 | #include <linux/delay.h> | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 21 | #include <linux/pci.h> | 
|  | 22 | #include <linux/wait.h> | 
|  | 23 | #include <linux/spi/spi.h> | 
|  | 24 | #include <linux/interrupt.h> | 
|  | 25 | #include <linux/sched.h> | 
|  | 26 | #include <linux/spi/spidev.h> | 
|  | 27 | #include <linux/module.h> | 
|  | 28 | #include <linux/device.h> | 
|  | 29 |  | 
|  | 30 | /* Register offsets */ | 
|  | 31 | #define PCH_SPCR		0x00	/* SPI control register */ | 
|  | 32 | #define PCH_SPBRR		0x04	/* SPI baud rate register */ | 
|  | 33 | #define PCH_SPSR		0x08	/* SPI status register */ | 
|  | 34 | #define PCH_SPDWR		0x0C	/* SPI write data register */ | 
|  | 35 | #define PCH_SPDRR		0x10	/* SPI read data register */ | 
|  | 36 | #define PCH_SSNXCR		0x18	/* SSN Expand Control Register */ | 
|  | 37 | #define PCH_SRST		0x1C	/* SPI reset register */ | 
|  | 38 |  | 
|  | 39 | #define PCH_SPSR_TFD		0x000007C0 | 
|  | 40 | #define PCH_SPSR_RFD		0x0000F800 | 
|  | 41 |  | 
|  | 42 | #define PCH_READABLE(x)		(((x) & PCH_SPSR_RFD)>>11) | 
|  | 43 | #define PCH_WRITABLE(x)		(((x) & PCH_SPSR_TFD)>>6) | 
|  | 44 |  | 
|  | 45 | #define PCH_RX_THOLD		7 | 
|  | 46 | #define PCH_RX_THOLD_MAX	15 | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 47 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 48 | #define PCH_MAX_BAUDRATE	5000000 | 
|  | 49 | #define PCH_MAX_FIFO_DEPTH	16 | 
|  | 50 |  | 
|  | 51 | #define STATUS_RUNNING		1 | 
|  | 52 | #define STATUS_EXITING		2 | 
|  | 53 | #define PCH_SLEEP_TIME		10 | 
|  | 54 |  | 
|  | 55 | #define PCH_ADDRESS_SIZE	0x20 | 
|  | 56 |  | 
|  | 57 | #define SSN_LOW			0x02U | 
|  | 58 | #define SSN_NO_CONTROL		0x00U | 
|  | 59 | #define PCH_MAX_CS		0xFF | 
|  | 60 | #define PCI_DEVICE_ID_GE_SPI	0x8816 | 
|  | 61 |  | 
|  | 62 | #define SPCR_SPE_BIT		(1 << 0) | 
|  | 63 | #define SPCR_MSTR_BIT		(1 << 1) | 
|  | 64 | #define SPCR_LSBF_BIT		(1 << 4) | 
|  | 65 | #define SPCR_CPHA_BIT		(1 << 5) | 
|  | 66 | #define SPCR_CPOL_BIT		(1 << 6) | 
|  | 67 | #define SPCR_TFIE_BIT		(1 << 8) | 
|  | 68 | #define SPCR_RFIE_BIT		(1 << 9) | 
|  | 69 | #define SPCR_FIE_BIT		(1 << 10) | 
|  | 70 | #define SPCR_ORIE_BIT		(1 << 11) | 
|  | 71 | #define SPCR_MDFIE_BIT		(1 << 12) | 
|  | 72 | #define SPCR_FICLR_BIT		(1 << 24) | 
|  | 73 | #define SPSR_TFI_BIT		(1 << 0) | 
|  | 74 | #define SPSR_RFI_BIT		(1 << 1) | 
|  | 75 | #define SPSR_FI_BIT		(1 << 2) | 
|  | 76 | #define SPBRR_SIZE_BIT		(1 << 10) | 
|  | 77 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 78 | #define PCH_ALL			(SPCR_TFIE_BIT|SPCR_RFIE_BIT|SPCR_FIE_BIT|SPCR_ORIE_BIT|SPCR_MDFIE_BIT) | 
|  | 79 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 80 | #define SPCR_RFIC_FIELD		20 | 
|  | 81 | #define SPCR_TFIC_FIELD		16 | 
|  | 82 |  | 
|  | 83 | #define SPSR_INT_BITS		0x1F | 
|  | 84 | #define MASK_SPBRR_SPBR_BITS	(~((1 << 10) - 1)) | 
|  | 85 | #define MASK_RFIC_SPCR_BITS	(~(0xf << 20)) | 
|  | 86 | #define MASK_TFIC_SPCR_BITS	(~(0xf000f << 12)) | 
|  | 87 |  | 
|  | 88 | #define PCH_CLOCK_HZ		50000000 | 
|  | 89 | #define PCH_MAX_SPBR		1023 | 
|  | 90 |  | 
|  | 91 |  | 
|  | 92 | /** | 
|  | 93 | * struct pch_spi_data - Holds the SPI channel specific details | 
|  | 94 | * @io_remap_addr:		The remapped PCI base address | 
|  | 95 | * @master:			Pointer to the SPI master structure | 
|  | 96 | * @work:			Reference to work queue handler | 
|  | 97 | * @wk:				Workqueue for carrying out execution of the | 
|  | 98 | *				requests | 
|  | 99 | * @wait:			Wait queue for waking up upon receiving an | 
|  | 100 | *				interrupt. | 
|  | 101 | * @transfer_complete:		Status of SPI Transfer | 
|  | 102 | * @bcurrent_msg_processing:	Status flag for message processing | 
|  | 103 | * @lock:			Lock for protecting this structure | 
|  | 104 | * @queue:			SPI Message queue | 
|  | 105 | * @status:			Status of the SPI driver | 
|  | 106 | * @bpw_len:			Length of data to be transferred in bits per | 
|  | 107 | *				word | 
|  | 108 | * @transfer_active:		Flag showing active transfer | 
|  | 109 | * @tx_index:			Transmit data count; for bookkeeping during | 
|  | 110 | *				transfer | 
|  | 111 | * @rx_index:			Receive data count; for bookkeeping during | 
|  | 112 | *				transfer | 
|  | 113 | * @tx_buff:			Buffer for data to be transmitted | 
|  | 114 | * @rx_index:			Buffer for Received data | 
|  | 115 | * @n_curnt_chip:		The chip number that this SPI driver currently | 
|  | 116 | *				operates on | 
|  | 117 | * @current_chip:		Reference to the current chip that this SPI | 
|  | 118 | *				driver currently operates on | 
|  | 119 | * @current_msg:		The current message that this SPI driver is | 
|  | 120 | *				handling | 
|  | 121 | * @cur_trans:			The current transfer that this SPI driver is | 
|  | 122 | *				handling | 
|  | 123 | * @board_dat:			Reference to the SPI device data structure | 
|  | 124 | */ | 
|  | 125 | struct pch_spi_data { | 
|  | 126 | void __iomem *io_remap_addr; | 
|  | 127 | struct spi_master *master; | 
|  | 128 | struct work_struct work; | 
|  | 129 | struct workqueue_struct *wk; | 
|  | 130 | wait_queue_head_t wait; | 
|  | 131 | u8 transfer_complete; | 
|  | 132 | u8 bcurrent_msg_processing; | 
|  | 133 | spinlock_t lock; | 
|  | 134 | struct list_head queue; | 
|  | 135 | u8 status; | 
|  | 136 | u32 bpw_len; | 
|  | 137 | u8 transfer_active; | 
|  | 138 | u32 tx_index; | 
|  | 139 | u32 rx_index; | 
|  | 140 | u16 *pkt_tx_buff; | 
|  | 141 | u16 *pkt_rx_buff; | 
|  | 142 | u8 n_curnt_chip; | 
|  | 143 | struct spi_device *current_chip; | 
|  | 144 | struct spi_message *current_msg; | 
|  | 145 | struct spi_transfer *cur_trans; | 
|  | 146 | struct pch_spi_board_data *board_dat; | 
|  | 147 | }; | 
|  | 148 |  | 
|  | 149 | /** | 
|  | 150 | * struct pch_spi_board_data - Holds the SPI device specific details | 
|  | 151 | * @pdev:		Pointer to the PCI device | 
|  | 152 | * @irq_reg_sts:	Status of IRQ registration | 
|  | 153 | * @pci_req_sts:	Status of pci_request_regions | 
|  | 154 | * @suspend_sts:	Status of suspend | 
|  | 155 | * @data:		Pointer to SPI channel data structure | 
|  | 156 | */ | 
|  | 157 | struct pch_spi_board_data { | 
|  | 158 | struct pci_dev *pdev; | 
|  | 159 | u8 irq_reg_sts; | 
|  | 160 | u8 pci_req_sts; | 
|  | 161 | u8 suspend_sts; | 
|  | 162 | struct pch_spi_data *data; | 
|  | 163 | }; | 
|  | 164 |  | 
|  | 165 | static struct pci_device_id pch_spi_pcidev_id[] = { | 
|  | 166 | {PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_GE_SPI)}, | 
|  | 167 | {0,} | 
|  | 168 | }; | 
|  | 169 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 170 | /** | 
|  | 171 | * pch_spi_writereg() - Performs  register writes | 
|  | 172 | * @master:	Pointer to struct spi_master. | 
|  | 173 | * @idx:	Register offset. | 
|  | 174 | * @val:	Value to be written to register. | 
|  | 175 | */ | 
|  | 176 | static inline void pch_spi_writereg(struct spi_master *master, int idx, u32 val) | 
|  | 177 | { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 178 | struct pch_spi_data *data = spi_master_get_devdata(master); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 179 | iowrite32(val, (data->io_remap_addr + idx)); | 
|  | 180 | } | 
|  | 181 |  | 
|  | 182 | /** | 
|  | 183 | * pch_spi_readreg() - Performs register reads | 
|  | 184 | * @master:	Pointer to struct spi_master. | 
|  | 185 | * @idx:	Register offset. | 
|  | 186 | */ | 
|  | 187 | static inline u32 pch_spi_readreg(struct spi_master *master, int idx) | 
|  | 188 | { | 
|  | 189 | struct pch_spi_data *data = spi_master_get_devdata(master); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 190 | return ioread32(data->io_remap_addr + idx); | 
|  | 191 | } | 
|  | 192 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 193 | static inline void pch_spi_setclr_reg(struct spi_master *master, int idx, | 
|  | 194 | u32 set, u32 clr) | 
|  | 195 | { | 
|  | 196 | u32 tmp = pch_spi_readreg(master, idx); | 
|  | 197 | tmp = (tmp & ~clr) | set; | 
|  | 198 | pch_spi_writereg(master, idx, tmp); | 
|  | 199 | } | 
|  | 200 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 201 | static void pch_spi_set_master_mode(struct spi_master *master) | 
|  | 202 | { | 
|  | 203 | pch_spi_setclr_reg(master, PCH_SPCR, SPCR_MSTR_BIT, 0); | 
|  | 204 | } | 
|  | 205 |  | 
|  | 206 | /** | 
|  | 207 | * pch_spi_clear_fifo() - Clears the Transmit and Receive FIFOs | 
|  | 208 | * @master:	Pointer to struct spi_master. | 
|  | 209 | */ | 
|  | 210 | static void pch_spi_clear_fifo(struct spi_master *master) | 
|  | 211 | { | 
|  | 212 | pch_spi_setclr_reg(master, PCH_SPCR, SPCR_FICLR_BIT, 0); | 
|  | 213 | pch_spi_setclr_reg(master, PCH_SPCR, 0, SPCR_FICLR_BIT); | 
|  | 214 | } | 
|  | 215 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 216 | static void pch_spi_handler_sub(struct pch_spi_data *data, u32 reg_spsr_val, | 
|  | 217 | void __iomem *io_remap_addr) | 
|  | 218 | { | 
|  | 219 | u32 n_read, tx_index, rx_index, bpw_len; | 
|  | 220 | u16 *pkt_rx_buffer, *pkt_tx_buff; | 
|  | 221 | int read_cnt; | 
|  | 222 | u32 reg_spcr_val; | 
|  | 223 | void __iomem *spsr; | 
|  | 224 | void __iomem *spdrr; | 
|  | 225 | void __iomem *spdwr; | 
|  | 226 |  | 
|  | 227 | spsr = io_remap_addr + PCH_SPSR; | 
|  | 228 | iowrite32(reg_spsr_val, spsr); | 
|  | 229 |  | 
|  | 230 | if (data->transfer_active) { | 
|  | 231 | rx_index = data->rx_index; | 
|  | 232 | tx_index = data->tx_index; | 
|  | 233 | bpw_len = data->bpw_len; | 
|  | 234 | pkt_rx_buffer = data->pkt_rx_buff; | 
|  | 235 | pkt_tx_buff = data->pkt_tx_buff; | 
|  | 236 |  | 
|  | 237 | spdrr = io_remap_addr + PCH_SPDRR; | 
|  | 238 | spdwr = io_remap_addr + PCH_SPDWR; | 
|  | 239 |  | 
|  | 240 | n_read = PCH_READABLE(reg_spsr_val); | 
|  | 241 |  | 
|  | 242 | for (read_cnt = 0; (read_cnt < n_read); read_cnt++) { | 
|  | 243 | pkt_rx_buffer[rx_index++] = ioread32(spdrr); | 
|  | 244 | if (tx_index < bpw_len) | 
|  | 245 | iowrite32(pkt_tx_buff[tx_index++], spdwr); | 
|  | 246 | } | 
|  | 247 |  | 
|  | 248 | /* disable RFI if not needed */ | 
|  | 249 | if ((bpw_len - rx_index) <= PCH_MAX_FIFO_DEPTH) { | 
|  | 250 | reg_spcr_val = ioread32(io_remap_addr + PCH_SPCR); | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 251 | reg_spcr_val &= ~SPCR_RFIE_BIT; /* disable RFI */ | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 252 |  | 
|  | 253 | /* reset rx threshold */ | 
|  | 254 | reg_spcr_val &= MASK_RFIC_SPCR_BITS; | 
|  | 255 | reg_spcr_val |= (PCH_RX_THOLD_MAX << SPCR_RFIC_FIELD); | 
|  | 256 | iowrite32(((reg_spcr_val) &= (~(SPCR_RFIE_BIT))), | 
|  | 257 | (io_remap_addr + PCH_SPCR)); | 
|  | 258 | } | 
|  | 259 |  | 
|  | 260 | /* update counts */ | 
|  | 261 | data->tx_index = tx_index; | 
|  | 262 | data->rx_index = rx_index; | 
|  | 263 |  | 
|  | 264 | } | 
|  | 265 |  | 
|  | 266 | /* if transfer complete interrupt */ | 
|  | 267 | if (reg_spsr_val & SPSR_FI_BIT) { | 
|  | 268 | /* disable FI & RFI interrupts */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 269 | pch_spi_setclr_reg(data->master, PCH_SPCR, 0, | 
|  | 270 | SPCR_FIE_BIT | SPCR_TFIE_BIT); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 271 |  | 
|  | 272 | /* transfer is completed;inform pch_spi_process_messages */ | 
|  | 273 | data->transfer_complete = true; | 
|  | 274 | wake_up(&data->wait); | 
|  | 275 | } | 
|  | 276 | } | 
|  | 277 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 278 | /** | 
|  | 279 | * pch_spi_handler() - Interrupt handler | 
|  | 280 | * @irq:	The interrupt number. | 
|  | 281 | * @dev_id:	Pointer to struct pch_spi_board_data. | 
|  | 282 | */ | 
|  | 283 | static irqreturn_t pch_spi_handler(int irq, void *dev_id) | 
|  | 284 | { | 
|  | 285 | u32 reg_spsr_val; | 
|  | 286 | struct pch_spi_data *data; | 
|  | 287 | void __iomem *spsr; | 
|  | 288 | void __iomem *io_remap_addr; | 
|  | 289 | irqreturn_t ret = IRQ_NONE; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 290 | struct pch_spi_board_data *board_dat = dev_id; | 
|  | 291 |  | 
|  | 292 | if (board_dat->suspend_sts) { | 
|  | 293 | dev_dbg(&board_dat->pdev->dev, | 
|  | 294 | "%s returning due to suspend\n", __func__); | 
|  | 295 | return IRQ_NONE; | 
|  | 296 | } | 
|  | 297 |  | 
|  | 298 | data = board_dat->data; | 
|  | 299 | io_remap_addr = data->io_remap_addr; | 
|  | 300 | spsr = io_remap_addr + PCH_SPSR; | 
|  | 301 |  | 
|  | 302 | reg_spsr_val = ioread32(spsr); | 
|  | 303 |  | 
|  | 304 | /* Check if the interrupt is for SPI device */ | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 305 | if (reg_spsr_val & (SPSR_FI_BIT | SPSR_RFI_BIT)) { | 
|  | 306 | pch_spi_handler_sub(data, reg_spsr_val, io_remap_addr); | 
|  | 307 | ret = IRQ_HANDLED; | 
|  | 308 | } | 
|  | 309 |  | 
|  | 310 | dev_dbg(&board_dat->pdev->dev, "%s EXIT return value=%d\n", | 
|  | 311 | __func__, ret); | 
|  | 312 |  | 
|  | 313 | return ret; | 
|  | 314 | } | 
|  | 315 |  | 
|  | 316 | /** | 
|  | 317 | * pch_spi_set_baud_rate() - Sets SPBR field in SPBRR | 
|  | 318 | * @master:	Pointer to struct spi_master. | 
|  | 319 | * @speed_hz:	Baud rate. | 
|  | 320 | */ | 
|  | 321 | static void pch_spi_set_baud_rate(struct spi_master *master, u32 speed_hz) | 
|  | 322 | { | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 323 | u32 n_spbr = PCH_CLOCK_HZ / (speed_hz * 2); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 324 |  | 
|  | 325 | /* if baud rate is less than we can support limit it */ | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 326 | if (n_spbr > PCH_MAX_SPBR) | 
|  | 327 | n_spbr = PCH_MAX_SPBR; | 
|  | 328 |  | 
|  | 329 | pch_spi_setclr_reg(master, PCH_SPBRR, n_spbr, ~MASK_SPBRR_SPBR_BITS); | 
|  | 330 | } | 
|  | 331 |  | 
|  | 332 | /** | 
|  | 333 | * pch_spi_set_bits_per_word() - Sets SIZE field in SPBRR | 
|  | 334 | * @master:		Pointer to struct spi_master. | 
|  | 335 | * @bits_per_word:	Bits per word for SPI transfer. | 
|  | 336 | */ | 
|  | 337 | static void pch_spi_set_bits_per_word(struct spi_master *master, | 
|  | 338 | u8 bits_per_word) | 
|  | 339 | { | 
|  | 340 | if (bits_per_word == 8) | 
|  | 341 | pch_spi_setclr_reg(master, PCH_SPBRR, 0, SPBRR_SIZE_BIT); | 
|  | 342 | else | 
|  | 343 | pch_spi_setclr_reg(master, PCH_SPBRR, SPBRR_SIZE_BIT, 0); | 
|  | 344 | } | 
|  | 345 |  | 
|  | 346 | /** | 
|  | 347 | * pch_spi_setup_transfer() - Configures the PCH SPI hardware for transfer | 
|  | 348 | * @spi:	Pointer to struct spi_device. | 
|  | 349 | */ | 
|  | 350 | static void pch_spi_setup_transfer(struct spi_device *spi) | 
|  | 351 | { | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 352 | u32 flags = 0; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 353 |  | 
|  | 354 | dev_dbg(&spi->dev, "%s SPBRR content =%x setting baud rate=%d\n", | 
|  | 355 | __func__, pch_spi_readreg(spi->master, PCH_SPBRR), | 
|  | 356 | spi->max_speed_hz); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 357 | pch_spi_set_baud_rate(spi->master, spi->max_speed_hz); | 
|  | 358 |  | 
|  | 359 | /* set bits per word */ | 
|  | 360 | pch_spi_set_bits_per_word(spi->master, spi->bits_per_word); | 
|  | 361 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 362 | if (!(spi->mode & SPI_LSB_FIRST)) | 
|  | 363 | flags |= SPCR_LSBF_BIT; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 364 | if (spi->mode & SPI_CPOL) | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 365 | flags |= SPCR_CPOL_BIT; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 366 | if (spi->mode & SPI_CPHA) | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 367 | flags |= SPCR_CPHA_BIT; | 
|  | 368 | pch_spi_setclr_reg(spi->master, PCH_SPCR, flags, | 
|  | 369 | (SPCR_LSBF_BIT | SPCR_CPOL_BIT | SPCR_CPHA_BIT)); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 370 |  | 
|  | 371 | /* Clear the FIFO by toggling  FICLR to 1 and back to 0 */ | 
|  | 372 | pch_spi_clear_fifo(spi->master); | 
|  | 373 | } | 
|  | 374 |  | 
|  | 375 | /** | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 376 | * pch_spi_reset() - Clears SPI registers | 
|  | 377 | * @master:	Pointer to struct spi_master. | 
|  | 378 | */ | 
|  | 379 | static void pch_spi_reset(struct spi_master *master) | 
|  | 380 | { | 
|  | 381 | /* write 1 to reset SPI */ | 
|  | 382 | pch_spi_writereg(master, PCH_SRST, 0x1); | 
|  | 383 |  | 
|  | 384 | /* clear reset */ | 
|  | 385 | pch_spi_writereg(master, PCH_SRST, 0x0); | 
|  | 386 | } | 
|  | 387 |  | 
|  | 388 | static int pch_spi_setup(struct spi_device *pspi) | 
|  | 389 | { | 
|  | 390 | /* check bits per word */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 391 | if (pspi->bits_per_word == 0) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 392 | pspi->bits_per_word = 8; | 
|  | 393 | dev_dbg(&pspi->dev, "%s 8 bits per word\n", __func__); | 
|  | 394 | } | 
|  | 395 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 396 | if ((pspi->bits_per_word != 8) && (pspi->bits_per_word != 16)) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 397 | dev_err(&pspi->dev, "%s Invalid bits per word\n", __func__); | 
|  | 398 | return -EINVAL; | 
|  | 399 | } | 
|  | 400 |  | 
|  | 401 | /* Check baud rate setting */ | 
|  | 402 | /* if baud rate of chip is greater than | 
|  | 403 | max we can support,return error */ | 
|  | 404 | if ((pspi->max_speed_hz) > PCH_MAX_BAUDRATE) | 
|  | 405 | pspi->max_speed_hz = PCH_MAX_BAUDRATE; | 
|  | 406 |  | 
|  | 407 | dev_dbg(&pspi->dev, "%s MODE = %x\n", __func__, | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 408 | (pspi->mode) & (SPI_CPOL | SPI_CPHA)); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 409 |  | 
|  | 410 | return 0; | 
|  | 411 | } | 
|  | 412 |  | 
|  | 413 | static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg) | 
|  | 414 | { | 
|  | 415 |  | 
|  | 416 | struct spi_transfer *transfer; | 
|  | 417 | struct pch_spi_data *data = spi_master_get_devdata(pspi->master); | 
|  | 418 | int retval; | 
|  | 419 | unsigned long flags; | 
|  | 420 |  | 
|  | 421 | /* validate spi message and baud rate */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 422 | if (unlikely(list_empty(&pmsg->transfers) == 1)) { | 
|  | 423 | dev_err(&pspi->dev, "%s list empty\n", __func__); | 
|  | 424 | retval = -EINVAL; | 
|  | 425 | goto err_out; | 
|  | 426 | } | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 427 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 428 | if (unlikely(pspi->max_speed_hz == 0)) { | 
|  | 429 | dev_err(&pspi->dev, "%s pch_spi_tranfer maxspeed=%d\n", | 
|  | 430 | __func__, pspi->max_speed_hz); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 431 | retval = -EINVAL; | 
|  | 432 | goto err_out; | 
|  | 433 | } | 
|  | 434 |  | 
|  | 435 | dev_dbg(&pspi->dev, "%s Transfer List not empty. " | 
|  | 436 | "Transfer Speed is set.\n", __func__); | 
|  | 437 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 438 | /* validate Tx/Rx buffers and Transfer length */ | 
|  | 439 | list_for_each_entry(transfer, &pmsg->transfers, transfer_list) { | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 440 | if (!transfer->tx_buf && !transfer->rx_buf) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 441 | dev_err(&pspi->dev, | 
|  | 442 | "%s Tx and Rx buffer NULL\n", __func__); | 
|  | 443 | retval = -EINVAL; | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 444 | goto err_out; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 445 | } | 
|  | 446 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 447 | if (!transfer->len) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 448 | dev_err(&pspi->dev, "%s Transfer length invalid\n", | 
|  | 449 | __func__); | 
|  | 450 | retval = -EINVAL; | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 451 | goto err_out; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 452 | } | 
|  | 453 |  | 
|  | 454 | dev_dbg(&pspi->dev, "%s Tx/Rx buffer valid. Transfer length" | 
|  | 455 | " valid\n", __func__); | 
|  | 456 |  | 
|  | 457 | /* if baud rate hs been specified validate the same */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 458 | if (transfer->speed_hz > PCH_MAX_BAUDRATE) | 
|  | 459 | transfer->speed_hz = PCH_MAX_BAUDRATE; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 460 |  | 
|  | 461 | /* if bits per word has been specified validate the same */ | 
|  | 462 | if (transfer->bits_per_word) { | 
|  | 463 | if ((transfer->bits_per_word != 8) | 
|  | 464 | && (transfer->bits_per_word != 16)) { | 
|  | 465 | retval = -EINVAL; | 
|  | 466 | dev_err(&pspi->dev, | 
|  | 467 | "%s Invalid bits per word\n", __func__); | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 468 | goto err_out; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 469 | } | 
|  | 470 | } | 
|  | 471 | } | 
|  | 472 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 473 | spin_lock_irqsave(&data->lock, flags); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 474 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 475 | /* We won't process any messages if we have been asked to terminate */ | 
|  | 476 | if (data->status == STATUS_EXITING) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 477 | dev_err(&pspi->dev, "%s status = STATUS_EXITING.\n", __func__); | 
|  | 478 | retval = -ESHUTDOWN; | 
|  | 479 | goto err_return_spinlock; | 
|  | 480 | } | 
|  | 481 |  | 
|  | 482 | /* If suspended ,return -EINVAL */ | 
|  | 483 | if (data->board_dat->suspend_sts) { | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 484 | dev_err(&pspi->dev, "%s suspend; returning EINVAL\n", __func__); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 485 | retval = -EINVAL; | 
|  | 486 | goto err_return_spinlock; | 
|  | 487 | } | 
|  | 488 |  | 
|  | 489 | /* set status of message */ | 
|  | 490 | pmsg->actual_length = 0; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 491 | dev_dbg(&pspi->dev, "%s - pmsg->status =%d\n", __func__, pmsg->status); | 
|  | 492 |  | 
|  | 493 | pmsg->status = -EINPROGRESS; | 
|  | 494 |  | 
|  | 495 | /* add message to queue */ | 
|  | 496 | list_add_tail(&pmsg->queue, &data->queue); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 497 | dev_dbg(&pspi->dev, "%s - Invoked list_add_tail\n", __func__); | 
|  | 498 |  | 
|  | 499 | /* schedule work queue to run */ | 
|  | 500 | queue_work(data->wk, &data->work); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 501 | dev_dbg(&pspi->dev, "%s - Invoked queue work\n", __func__); | 
|  | 502 |  | 
|  | 503 | retval = 0; | 
|  | 504 |  | 
|  | 505 | err_return_spinlock: | 
|  | 506 | spin_unlock_irqrestore(&data->lock, flags); | 
|  | 507 | err_out: | 
|  | 508 | dev_dbg(&pspi->dev, "%s RETURN=%d\n", __func__, retval); | 
|  | 509 | return retval; | 
|  | 510 | } | 
|  | 511 |  | 
|  | 512 | static inline void pch_spi_select_chip(struct pch_spi_data *data, | 
|  | 513 | struct spi_device *pspi) | 
|  | 514 | { | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 515 | if (data->current_chip != NULL) { | 
|  | 516 | if (pspi->chip_select != data->n_curnt_chip) { | 
|  | 517 | dev_dbg(&pspi->dev, "%s : different slave\n", __func__); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 518 | data->current_chip = NULL; | 
|  | 519 | } | 
|  | 520 | } | 
|  | 521 |  | 
|  | 522 | data->current_chip = pspi; | 
|  | 523 |  | 
|  | 524 | data->n_curnt_chip = data->current_chip->chip_select; | 
|  | 525 |  | 
|  | 526 | dev_dbg(&pspi->dev, "%s :Invoking pch_spi_setup_transfer\n", __func__); | 
|  | 527 | pch_spi_setup_transfer(pspi); | 
|  | 528 | } | 
|  | 529 |  | 
|  | 530 | static void pch_spi_set_tx(struct pch_spi_data *data, int *bpw, | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 531 | struct spi_message **ppmsg) | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 532 | { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 533 | int size; | 
|  | 534 | u32 n_writes; | 
|  | 535 | int j; | 
|  | 536 | struct spi_message *pmsg; | 
|  | 537 | const u8 *tx_buf; | 
|  | 538 | const u16 *tx_sbuf; | 
|  | 539 |  | 
|  | 540 | pmsg = *ppmsg; | 
|  | 541 |  | 
|  | 542 | /* set baud rate if needed */ | 
|  | 543 | if (data->cur_trans->speed_hz) { | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 544 | dev_dbg(&data->master->dev, "%s:setting baud rate\n", __func__); | 
|  | 545 | pch_spi_set_baud_rate(data->master, data->cur_trans->speed_hz); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 546 | } | 
|  | 547 |  | 
|  | 548 | /* set bits per word if needed */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 549 | if (data->cur_trans->bits_per_word && | 
|  | 550 | (data->current_msg->spi->bits_per_word != data->cur_trans->bits_per_word)) { | 
|  | 551 | dev_dbg(&data->master->dev, "%s:set bits per word\n", __func__); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 552 | pch_spi_set_bits_per_word(data->master, | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 553 | data->cur_trans->bits_per_word); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 554 | *bpw = data->cur_trans->bits_per_word; | 
|  | 555 | } else { | 
|  | 556 | *bpw = data->current_msg->spi->bits_per_word; | 
|  | 557 | } | 
|  | 558 |  | 
|  | 559 | /* reset Tx/Rx index */ | 
|  | 560 | data->tx_index = 0; | 
|  | 561 | data->rx_index = 0; | 
|  | 562 |  | 
|  | 563 | data->bpw_len = data->cur_trans->len / (*bpw / 8); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 564 |  | 
|  | 565 | /* find alloc size */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 566 | size = data->cur_trans->len * sizeof(*data->pkt_tx_buff); | 
|  | 567 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 568 | /* allocate memory for pkt_tx_buff & pkt_rx_buffer */ | 
|  | 569 | data->pkt_tx_buff = kzalloc(size, GFP_KERNEL); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 570 | if (data->pkt_tx_buff != NULL) { | 
|  | 571 | data->pkt_rx_buff = kzalloc(size, GFP_KERNEL); | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 572 | if (!data->pkt_rx_buff) | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 573 | kfree(data->pkt_tx_buff); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 574 | } | 
|  | 575 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 576 | if (!data->pkt_rx_buff) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 577 | /* flush queue and set status of all transfers to -ENOMEM */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 578 | dev_err(&data->master->dev, "%s :kzalloc failed\n", __func__); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 579 | list_for_each_entry(pmsg, data->queue.next, queue) { | 
|  | 580 | pmsg->status = -ENOMEM; | 
|  | 581 |  | 
|  | 582 | if (pmsg->complete != 0) | 
|  | 583 | pmsg->complete(pmsg->context); | 
|  | 584 |  | 
|  | 585 | /* delete from queue */ | 
|  | 586 | list_del_init(&pmsg->queue); | 
|  | 587 | } | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 588 | return; | 
|  | 589 | } | 
|  | 590 |  | 
|  | 591 | /* copy Tx Data */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 592 | if (data->cur_trans->tx_buf != NULL) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 593 | if (*bpw == 8) { | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 594 | tx_buf = data->cur_trans->tx_buf; | 
|  | 595 | for (j = 0; j < data->bpw_len; j++) | 
|  | 596 | data->pkt_tx_buff[j] = *tx_buf++; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 597 | } else { | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 598 | tx_sbuf = data->cur_trans->tx_buf; | 
|  | 599 | for (j = 0; j < data->bpw_len; j++) | 
|  | 600 | data->pkt_tx_buff[j] = *tx_sbuf++; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 601 | } | 
|  | 602 | } | 
|  | 603 |  | 
|  | 604 | /* if len greater than PCH_MAX_FIFO_DEPTH, write 16,else len bytes */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 605 | n_writes = data->bpw_len; | 
|  | 606 | if (n_writes > PCH_MAX_FIFO_DEPTH) | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 607 | n_writes = PCH_MAX_FIFO_DEPTH; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 608 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 609 | dev_dbg(&data->master->dev, "\n%s:Pulling down SSN low - writing " | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 610 | "0x2 to SSNXCR\n", __func__); | 
|  | 611 | pch_spi_writereg(data->master, PCH_SSNXCR, SSN_LOW); | 
|  | 612 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 613 | for (j = 0; j < n_writes; j++) | 
|  | 614 | pch_spi_writereg(data->master, PCH_SPDWR, data->pkt_tx_buff[j]); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 615 |  | 
|  | 616 | /* update tx_index */ | 
|  | 617 | data->tx_index = j; | 
|  | 618 |  | 
|  | 619 | /* reset transfer complete flag */ | 
|  | 620 | data->transfer_complete = false; | 
|  | 621 | data->transfer_active = true; | 
|  | 622 | } | 
|  | 623 |  | 
|  | 624 |  | 
|  | 625 | static void pch_spi_nomore_transfer(struct pch_spi_data *data, | 
|  | 626 | struct spi_message *pmsg) | 
|  | 627 | { | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 628 | dev_dbg(&data->master->dev, "%s called\n", __func__); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 629 | /* Invoke complete callback | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 630 | * [To the spi core..indicating end of transfer] */ | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 631 | data->current_msg->status = 0; | 
|  | 632 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 633 | if (data->current_msg->complete != 0) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 634 | dev_dbg(&data->master->dev, | 
|  | 635 | "%s:Invoking callback of SPI core\n", __func__); | 
|  | 636 | data->current_msg->complete(data->current_msg->context); | 
|  | 637 | } | 
|  | 638 |  | 
|  | 639 | /* update status in global variable */ | 
|  | 640 | data->bcurrent_msg_processing = false; | 
|  | 641 |  | 
|  | 642 | dev_dbg(&data->master->dev, | 
|  | 643 | "%s:data->bcurrent_msg_processing = false\n", __func__); | 
|  | 644 |  | 
|  | 645 | data->current_msg = NULL; | 
|  | 646 | data->cur_trans = NULL; | 
|  | 647 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 648 | /* check if we have items in list and not suspending | 
|  | 649 | * return 1 if list empty */ | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 650 | if ((list_empty(&data->queue) == 0) && | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 651 | (!data->board_dat->suspend_sts) && | 
|  | 652 | (data->status != STATUS_EXITING)) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 653 | /* We have some more work to do (either there is more tranint | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 654 | * bpw;sfer requests in the current message or there are | 
|  | 655 | *more messages) | 
|  | 656 | */ | 
|  | 657 | dev_dbg(&data->master->dev, "%s:Invoke queue_work\n", __func__); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 658 | queue_work(data->wk, &data->work); | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 659 | } else if (data->board_dat->suspend_sts || | 
|  | 660 | data->status == STATUS_EXITING) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 661 | dev_dbg(&data->master->dev, | 
|  | 662 | "%s suspend/remove initiated, flushing queue\n", | 
|  | 663 | __func__); | 
|  | 664 | list_for_each_entry(pmsg, data->queue.next, queue) { | 
|  | 665 | pmsg->status = -EIO; | 
|  | 666 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 667 | if (pmsg->complete) | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 668 | pmsg->complete(pmsg->context); | 
|  | 669 |  | 
|  | 670 | /* delete from queue */ | 
|  | 671 | list_del_init(&pmsg->queue); | 
|  | 672 | } | 
|  | 673 | } | 
|  | 674 | } | 
|  | 675 |  | 
|  | 676 | static void pch_spi_set_ir(struct pch_spi_data *data) | 
|  | 677 | { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 678 | /* enable interrupts */ | 
|  | 679 | if ((data->bpw_len) > PCH_MAX_FIFO_DEPTH) { | 
|  | 680 | /* set receive threhold to PCH_RX_THOLD */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 681 | pch_spi_setclr_reg(data->master, PCH_SPCR, | 
|  | 682 | PCH_RX_THOLD << SPCR_TFIC_FIELD, | 
|  | 683 | ~MASK_TFIC_SPCR_BITS); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 684 | /* enable FI and RFI interrupts */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 685 | pch_spi_setclr_reg(data->master, PCH_SPCR, | 
|  | 686 | SPCR_RFIE_BIT | SPCR_TFIE_BIT, 0); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 687 | } else { | 
|  | 688 | /* set receive threhold to maximum */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 689 | pch_spi_setclr_reg(data->master, PCH_SPCR, | 
|  | 690 | PCH_RX_THOLD_MAX << SPCR_TFIC_FIELD, | 
|  | 691 | ~MASK_TFIC_SPCR_BITS); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 692 | /* enable FI interrupt */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 693 | pch_spi_setclr_reg(data->master, PCH_SPCR, SPCR_FIE_BIT, 0); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 694 | } | 
|  | 695 |  | 
|  | 696 | dev_dbg(&data->master->dev, | 
|  | 697 | "%s:invoking pch_spi_set_enable to enable SPI\n", __func__); | 
|  | 698 |  | 
|  | 699 | /* SPI set enable */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 700 | pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, SPCR_SPE_BIT, 0); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 701 |  | 
|  | 702 | /* Wait until the transfer completes; go to sleep after | 
|  | 703 | initiating the transfer. */ | 
|  | 704 | dev_dbg(&data->master->dev, | 
|  | 705 | "%s:waiting for transfer to get over\n", __func__); | 
|  | 706 |  | 
|  | 707 | wait_event_interruptible(data->wait, data->transfer_complete); | 
|  | 708 |  | 
|  | 709 | pch_spi_writereg(data->master, PCH_SSNXCR, SSN_NO_CONTROL); | 
|  | 710 | dev_dbg(&data->master->dev, | 
|  | 711 | "%s:no more control over SSN-writing 0 to SSNXCR.", __func__); | 
|  | 712 |  | 
|  | 713 | data->transfer_active = false; | 
|  | 714 | dev_dbg(&data->master->dev, | 
|  | 715 | "%s set data->transfer_active = false\n", __func__); | 
|  | 716 |  | 
|  | 717 | /* clear all interrupts */ | 
|  | 718 | pch_spi_writereg(data->master, PCH_SPSR, | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 719 | pch_spi_readreg(data->master, PCH_SPSR)); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 720 | /* disable interrupts */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 721 | pch_spi_setclr_reg(data->master, PCH_SPCR, 0, PCH_ALL); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 722 | } | 
|  | 723 |  | 
|  | 724 | static void pch_spi_copy_rx_data(struct pch_spi_data *data, int bpw) | 
|  | 725 | { | 
|  | 726 | int j; | 
|  | 727 | u8 *rx_buf; | 
|  | 728 | u16 *rx_sbuf; | 
|  | 729 |  | 
|  | 730 | /* copy Rx Data */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 731 | if (!data->cur_trans->rx_buf) | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 732 | return; | 
|  | 733 |  | 
|  | 734 | if (bpw == 8) { | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 735 | rx_buf = data->cur_trans->rx_buf; | 
|  | 736 | for (j = 0; j < data->bpw_len; j++) | 
|  | 737 | *rx_buf++ = data->pkt_rx_buff[j] & 0xFF; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 738 | } else { | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 739 | rx_sbuf = data->cur_trans->rx_buf; | 
|  | 740 | for (j = 0; j < data->bpw_len; j++) | 
|  | 741 | *rx_sbuf++ = data->pkt_rx_buff[j]; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 742 | } | 
|  | 743 | } | 
|  | 744 |  | 
|  | 745 |  | 
|  | 746 | static void pch_spi_process_messages(struct work_struct *pwork) | 
|  | 747 | { | 
|  | 748 | struct spi_message *pmsg; | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 749 | struct pch_spi_data *data; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 750 | int bpw; | 
|  | 751 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 752 | data = container_of(pwork, struct pch_spi_data, work); | 
| Grant Likely | 8e41b52 | 2010-10-13 23:03:15 -0600 | [diff] [blame] | 753 | dev_dbg(&data->master->dev, "%s data initialized\n", __func__); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 754 |  | 
|  | 755 | spin_lock(&data->lock); | 
|  | 756 |  | 
|  | 757 | /* check if suspend has been initiated;if yes flush queue */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 758 | if (data->board_dat->suspend_sts || (data->status == STATUS_EXITING)) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 759 | dev_dbg(&data->master->dev, | 
|  | 760 | "%s suspend/remove initiated,flushing queue\n", | 
|  | 761 | __func__); | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 762 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 763 | list_for_each_entry(pmsg, data->queue.next, queue) { | 
|  | 764 | pmsg->status = -EIO; | 
|  | 765 |  | 
|  | 766 | if (pmsg->complete != 0) { | 
|  | 767 | spin_unlock(&data->lock); | 
|  | 768 | pmsg->complete(pmsg->context); | 
|  | 769 | spin_lock(&data->lock); | 
|  | 770 | } | 
|  | 771 |  | 
|  | 772 | /* delete from queue */ | 
|  | 773 | list_del_init(&pmsg->queue); | 
|  | 774 | } | 
|  | 775 |  | 
|  | 776 | spin_unlock(&data->lock); | 
|  | 777 | return; | 
|  | 778 | } | 
|  | 779 |  | 
|  | 780 | data->bcurrent_msg_processing = true; | 
|  | 781 | dev_dbg(&data->master->dev, | 
|  | 782 | "%s Set data->bcurrent_msg_processing= true\n", __func__); | 
|  | 783 |  | 
|  | 784 | /* Get the message from the queue and delete it from there. */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 785 | data->current_msg = list_entry(data->queue.next, struct spi_message, | 
|  | 786 | queue); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 787 |  | 
|  | 788 | list_del_init(&data->current_msg->queue); | 
|  | 789 |  | 
|  | 790 | data->current_msg->status = 0; | 
|  | 791 |  | 
|  | 792 | pch_spi_select_chip(data, data->current_msg->spi); | 
|  | 793 |  | 
|  | 794 | spin_unlock(&data->lock); | 
|  | 795 |  | 
|  | 796 | do { | 
|  | 797 | /* If we are already processing a message get the next | 
|  | 798 | transfer structure from the message otherwise retrieve | 
|  | 799 | the 1st transfer request from the message. */ | 
|  | 800 | spin_lock(&data->lock); | 
|  | 801 |  | 
|  | 802 | if (data->cur_trans == NULL) { | 
|  | 803 | data->cur_trans = | 
|  | 804 | list_entry(data->current_msg->transfers. | 
|  | 805 | next, struct spi_transfer, | 
|  | 806 | transfer_list); | 
|  | 807 | dev_dbg(&data->master->dev, | 
|  | 808 | "%s :Getting 1st transfer message\n", __func__); | 
|  | 809 | } else { | 
|  | 810 | data->cur_trans = | 
|  | 811 | list_entry(data->cur_trans->transfer_list.next, | 
|  | 812 | struct spi_transfer, | 
|  | 813 | transfer_list); | 
|  | 814 | dev_dbg(&data->master->dev, | 
|  | 815 | "%s :Getting next transfer message\n", | 
|  | 816 | __func__); | 
|  | 817 | } | 
|  | 818 |  | 
|  | 819 | spin_unlock(&data->lock); | 
|  | 820 |  | 
|  | 821 | pch_spi_set_tx(data, &bpw, &pmsg); | 
|  | 822 |  | 
|  | 823 | /* Control interrupt*/ | 
|  | 824 | pch_spi_set_ir(data); | 
|  | 825 |  | 
|  | 826 | /* Disable SPI transfer */ | 
|  | 827 | pch_spi_setclr_reg(data->current_chip->master, PCH_SPCR, 0, | 
|  | 828 | SPCR_SPE_BIT); | 
|  | 829 |  | 
|  | 830 | /* clear FIFO */ | 
|  | 831 | pch_spi_clear_fifo(data->master); | 
|  | 832 |  | 
|  | 833 | /* copy Rx Data */ | 
|  | 834 | pch_spi_copy_rx_data(data, bpw); | 
|  | 835 |  | 
|  | 836 | /* free memory */ | 
|  | 837 | kfree(data->pkt_rx_buff); | 
|  | 838 | data->pkt_rx_buff = NULL; | 
|  | 839 |  | 
|  | 840 | kfree(data->pkt_tx_buff); | 
|  | 841 | data->pkt_tx_buff = NULL; | 
|  | 842 |  | 
|  | 843 | /* increment message count */ | 
|  | 844 | data->current_msg->actual_length += data->cur_trans->len; | 
|  | 845 |  | 
|  | 846 | dev_dbg(&data->master->dev, | 
|  | 847 | "%s:data->current_msg->actual_length=%d\n", | 
|  | 848 | __func__, data->current_msg->actual_length); | 
|  | 849 |  | 
|  | 850 | /* check for delay */ | 
|  | 851 | if (data->cur_trans->delay_usecs) { | 
|  | 852 | dev_dbg(&data->master->dev, "%s:" | 
|  | 853 | "delay in usec=%d\n", __func__, | 
|  | 854 | data->cur_trans->delay_usecs); | 
|  | 855 | udelay(data->cur_trans->delay_usecs); | 
|  | 856 | } | 
|  | 857 |  | 
|  | 858 | spin_lock(&data->lock); | 
|  | 859 |  | 
|  | 860 | /* No more transfer in this message. */ | 
|  | 861 | if ((data->cur_trans->transfer_list.next) == | 
|  | 862 | &(data->current_msg->transfers)) { | 
|  | 863 | pch_spi_nomore_transfer(data, pmsg); | 
|  | 864 | } | 
|  | 865 |  | 
|  | 866 | spin_unlock(&data->lock); | 
|  | 867 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 868 | } while (data->cur_trans != NULL); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 869 | } | 
|  | 870 |  | 
|  | 871 | static void pch_spi_free_resources(struct pch_spi_board_data *board_dat) | 
|  | 872 | { | 
|  | 873 | dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__); | 
|  | 874 |  | 
|  | 875 | /* free workqueue */ | 
|  | 876 | if (board_dat->data->wk != NULL) { | 
|  | 877 | destroy_workqueue(board_dat->data->wk); | 
|  | 878 | board_dat->data->wk = NULL; | 
|  | 879 | dev_dbg(&board_dat->pdev->dev, | 
|  | 880 | "%s destroy_workqueue invoked successfully\n", | 
|  | 881 | __func__); | 
|  | 882 | } | 
|  | 883 |  | 
|  | 884 | /* disable interrupts & free IRQ */ | 
|  | 885 | if (board_dat->irq_reg_sts) { | 
|  | 886 | /* disable interrupts */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 887 | pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0, | 
|  | 888 | PCH_ALL); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 889 |  | 
|  | 890 | /* free IRQ */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 891 | free_irq(board_dat->pdev->irq, board_dat); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 892 |  | 
|  | 893 | dev_dbg(&board_dat->pdev->dev, | 
|  | 894 | "%s free_irq invoked successfully\n", __func__); | 
|  | 895 |  | 
|  | 896 | board_dat->irq_reg_sts = false; | 
|  | 897 | } | 
|  | 898 |  | 
|  | 899 | /* unmap PCI base address */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 900 | if (board_dat->data->io_remap_addr != 0) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 901 | pci_iounmap(board_dat->pdev, board_dat->data->io_remap_addr); | 
|  | 902 |  | 
|  | 903 | board_dat->data->io_remap_addr = 0; | 
|  | 904 |  | 
|  | 905 | dev_dbg(&board_dat->pdev->dev, | 
|  | 906 | "%s pci_iounmap invoked successfully\n", __func__); | 
|  | 907 | } | 
|  | 908 |  | 
|  | 909 | /* release PCI region */ | 
|  | 910 | if (board_dat->pci_req_sts) { | 
|  | 911 | pci_release_regions(board_dat->pdev); | 
|  | 912 | dev_dbg(&board_dat->pdev->dev, | 
|  | 913 | "%s pci_release_regions invoked successfully\n", | 
|  | 914 | __func__); | 
|  | 915 | board_dat->pci_req_sts = false; | 
|  | 916 | } | 
|  | 917 | } | 
|  | 918 |  | 
|  | 919 | static int pch_spi_get_resources(struct pch_spi_board_data *board_dat) | 
|  | 920 | { | 
|  | 921 | void __iomem *io_remap_addr; | 
|  | 922 | int retval; | 
|  | 923 | dev_dbg(&board_dat->pdev->dev, "%s ENTRY\n", __func__); | 
|  | 924 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 925 | /* create workqueue */ | 
|  | 926 | board_dat->data->wk = create_singlethread_workqueue(KBUILD_MODNAME); | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 927 | if (!board_dat->data->wk) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 928 | dev_err(&board_dat->pdev->dev, | 
|  | 929 | "%s create_singlet hread_workqueue failed\n", __func__); | 
|  | 930 | retval = -EBUSY; | 
|  | 931 | goto err_return; | 
|  | 932 | } | 
|  | 933 |  | 
|  | 934 | dev_dbg(&board_dat->pdev->dev, | 
|  | 935 | "%s create_singlethread_workqueue success\n", __func__); | 
|  | 936 |  | 
|  | 937 | retval = pci_request_regions(board_dat->pdev, KBUILD_MODNAME); | 
|  | 938 | if (retval != 0) { | 
|  | 939 | dev_err(&board_dat->pdev->dev, | 
|  | 940 | "%s request_region failed\n", __func__); | 
|  | 941 | goto err_return; | 
|  | 942 | } | 
|  | 943 |  | 
|  | 944 | board_dat->pci_req_sts = true; | 
|  | 945 |  | 
|  | 946 | io_remap_addr = pci_iomap(board_dat->pdev, 1, 0); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 947 | if (io_remap_addr == 0) { | 
|  | 948 | dev_err(&board_dat->pdev->dev, | 
|  | 949 | "%s pci_iomap failed\n", __func__); | 
|  | 950 | retval = -ENOMEM; | 
|  | 951 | goto err_return; | 
|  | 952 | } | 
|  | 953 |  | 
|  | 954 | /* calculate base address for all channels */ | 
|  | 955 | board_dat->data->io_remap_addr = io_remap_addr; | 
|  | 956 |  | 
|  | 957 | /* reset PCH SPI h/w */ | 
|  | 958 | pch_spi_reset(board_dat->data->master); | 
|  | 959 | dev_dbg(&board_dat->pdev->dev, | 
|  | 960 | "%s pch_spi_reset invoked successfully\n", __func__); | 
|  | 961 |  | 
|  | 962 | /* register IRQ */ | 
|  | 963 | retval = request_irq(board_dat->pdev->irq, pch_spi_handler, | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 964 | IRQF_SHARED, KBUILD_MODNAME, board_dat); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 965 | if (retval != 0) { | 
|  | 966 | dev_err(&board_dat->pdev->dev, | 
|  | 967 | "%s request_irq failed\n", __func__); | 
|  | 968 | goto err_return; | 
|  | 969 | } | 
|  | 970 |  | 
|  | 971 | dev_dbg(&board_dat->pdev->dev, "%s request_irq returned=%d\n", | 
|  | 972 | __func__, retval); | 
|  | 973 |  | 
|  | 974 | board_dat->irq_reg_sts = true; | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 975 | dev_dbg(&board_dat->pdev->dev, "%s data->irq_reg_sts=true\n", __func__); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 976 |  | 
|  | 977 | err_return: | 
|  | 978 | if (retval != 0) { | 
|  | 979 | dev_err(&board_dat->pdev->dev, | 
|  | 980 | "%s FAIL:invoking pch_spi_free_resources\n", __func__); | 
|  | 981 | pch_spi_free_resources(board_dat); | 
|  | 982 | } | 
|  | 983 |  | 
|  | 984 | dev_dbg(&board_dat->pdev->dev, "%s Return=%d\n", __func__, retval); | 
|  | 985 |  | 
|  | 986 | return retval; | 
|  | 987 | } | 
|  | 988 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 989 | static int pch_spi_probe(struct pci_dev *pdev, const struct pci_device_id *id) | 
|  | 990 | { | 
|  | 991 |  | 
|  | 992 | struct spi_master *master; | 
|  | 993 |  | 
|  | 994 | struct pch_spi_board_data *board_dat; | 
|  | 995 | int retval; | 
|  | 996 |  | 
|  | 997 | dev_dbg(&pdev->dev, "%s ENTRY\n", __func__); | 
|  | 998 |  | 
|  | 999 | /* allocate memory for private data */ | 
|  | 1000 | board_dat = kzalloc(sizeof(struct pch_spi_board_data), GFP_KERNEL); | 
|  | 1001 | if (board_dat == NULL) { | 
|  | 1002 | dev_err(&pdev->dev, | 
|  | 1003 | " %s memory allocation for private data failed\n", | 
|  | 1004 | __func__); | 
|  | 1005 | retval = -ENOMEM; | 
|  | 1006 | goto err_kmalloc; | 
|  | 1007 | } | 
|  | 1008 |  | 
|  | 1009 | dev_dbg(&pdev->dev, | 
|  | 1010 | "%s memory allocation for private data success\n", __func__); | 
|  | 1011 |  | 
|  | 1012 | /* enable PCI device */ | 
|  | 1013 | retval = pci_enable_device(pdev); | 
|  | 1014 | if (retval != 0) { | 
|  | 1015 | dev_err(&pdev->dev, "%s pci_enable_device FAILED\n", __func__); | 
|  | 1016 |  | 
|  | 1017 | goto err_pci_en_device; | 
|  | 1018 | } | 
|  | 1019 |  | 
|  | 1020 | dev_dbg(&pdev->dev, "%s pci_enable_device returned=%d\n", | 
|  | 1021 | __func__, retval); | 
|  | 1022 |  | 
|  | 1023 | board_dat->pdev = pdev; | 
|  | 1024 |  | 
|  | 1025 | /* alllocate memory for SPI master */ | 
|  | 1026 | master = spi_alloc_master(&pdev->dev, sizeof(struct pch_spi_data)); | 
|  | 1027 | if (master == NULL) { | 
|  | 1028 | retval = -ENOMEM; | 
|  | 1029 | dev_err(&pdev->dev, "%s Fail.\n", __func__); | 
|  | 1030 | goto err_spi_alloc_master; | 
|  | 1031 | } | 
|  | 1032 |  | 
|  | 1033 | dev_dbg(&pdev->dev, | 
|  | 1034 | "%s spi_alloc_master returned non NULL\n", __func__); | 
|  | 1035 |  | 
|  | 1036 | /* initialize members of SPI master */ | 
|  | 1037 | master->bus_num = -1; | 
|  | 1038 | master->num_chipselect = PCH_MAX_CS; | 
|  | 1039 | master->setup = pch_spi_setup; | 
|  | 1040 | master->transfer = pch_spi_transfer; | 
|  | 1041 | dev_dbg(&pdev->dev, | 
|  | 1042 | "%s transfer member of SPI master initialized\n", __func__); | 
|  | 1043 |  | 
|  | 1044 | board_dat->data = spi_master_get_devdata(master); | 
|  | 1045 |  | 
|  | 1046 | board_dat->data->master = master; | 
|  | 1047 | board_dat->data->n_curnt_chip = 255; | 
|  | 1048 | board_dat->data->board_dat = board_dat; | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 1049 | board_dat->data->status = STATUS_RUNNING; | 
|  | 1050 |  | 
|  | 1051 | INIT_LIST_HEAD(&board_dat->data->queue); | 
|  | 1052 | spin_lock_init(&board_dat->data->lock); | 
|  | 1053 | INIT_WORK(&board_dat->data->work, pch_spi_process_messages); | 
|  | 1054 | init_waitqueue_head(&board_dat->data->wait); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 1055 |  | 
|  | 1056 | /* allocate resources for PCH SPI */ | 
|  | 1057 | retval = pch_spi_get_resources(board_dat); | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 1058 | if (retval) { | 
|  | 1059 | dev_err(&pdev->dev, "%s fail(retval=%d)\n", __func__, retval); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 1060 | goto err_spi_get_resources; | 
|  | 1061 | } | 
|  | 1062 |  | 
|  | 1063 | dev_dbg(&pdev->dev, "%s pch_spi_get_resources returned=%d\n", | 
|  | 1064 | __func__, retval); | 
|  | 1065 |  | 
|  | 1066 | /* save private data in dev */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 1067 | pci_set_drvdata(pdev, board_dat); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 1068 | dev_dbg(&pdev->dev, "%s invoked pci_set_drvdata\n", __func__); | 
|  | 1069 |  | 
|  | 1070 | /* set master mode */ | 
|  | 1071 | pch_spi_set_master_mode(master); | 
|  | 1072 | dev_dbg(&pdev->dev, | 
|  | 1073 | "%s invoked pch_spi_set_master_mode\n", __func__); | 
|  | 1074 |  | 
|  | 1075 | /* Register the controller with the SPI core. */ | 
|  | 1076 | retval = spi_register_master(master); | 
|  | 1077 | if (retval != 0) { | 
|  | 1078 | dev_err(&pdev->dev, | 
|  | 1079 | "%s spi_register_master FAILED\n", __func__); | 
|  | 1080 | goto err_spi_reg_master; | 
|  | 1081 | } | 
|  | 1082 |  | 
|  | 1083 | dev_dbg(&pdev->dev, "%s spi_register_master returned=%d\n", | 
|  | 1084 | __func__, retval); | 
|  | 1085 |  | 
|  | 1086 |  | 
|  | 1087 | return 0; | 
|  | 1088 |  | 
|  | 1089 | err_spi_reg_master: | 
|  | 1090 | spi_unregister_master(master); | 
|  | 1091 | err_spi_get_resources: | 
|  | 1092 | err_spi_alloc_master: | 
|  | 1093 | spi_master_put(master); | 
|  | 1094 | pci_disable_device(pdev); | 
|  | 1095 | err_pci_en_device: | 
|  | 1096 | kfree(board_dat); | 
|  | 1097 | err_kmalloc: | 
|  | 1098 | return retval; | 
|  | 1099 | } | 
|  | 1100 |  | 
|  | 1101 | static void pch_spi_remove(struct pci_dev *pdev) | 
|  | 1102 | { | 
|  | 1103 | struct pch_spi_board_data *board_dat = pci_get_drvdata(pdev); | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 1104 | int count; | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 1105 |  | 
|  | 1106 | dev_dbg(&pdev->dev, "%s ENTRY\n", __func__); | 
|  | 1107 |  | 
|  | 1108 | if (!board_dat) { | 
|  | 1109 | dev_err(&pdev->dev, | 
|  | 1110 | "%s pci_get_drvdata returned NULL\n", __func__); | 
|  | 1111 | return; | 
|  | 1112 | } | 
|  | 1113 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 1114 | /* check for any pending messages; no action is taken if the queue | 
|  | 1115 | * is still full; but at least we tried.  Unload anyway */ | 
|  | 1116 | count = 500; | 
|  | 1117 | spin_lock(&board_dat->data->lock); | 
|  | 1118 | board_dat->data->status = STATUS_EXITING; | 
|  | 1119 | while ((list_empty(&board_dat->data->queue) == 0) && --count) { | 
|  | 1120 | dev_dbg(&board_dat->pdev->dev, "%s :queue not empty\n", | 
|  | 1121 | __func__); | 
|  | 1122 | spin_unlock(&board_dat->data->lock); | 
|  | 1123 | msleep(PCH_SLEEP_TIME); | 
|  | 1124 | spin_lock(&board_dat->data->lock); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 1125 | } | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 1126 | spin_unlock(&board_dat->data->lock); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 1127 |  | 
|  | 1128 | /* Free resources allocated for PCH SPI */ | 
|  | 1129 | pch_spi_free_resources(board_dat); | 
|  | 1130 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 1131 | spi_unregister_master(board_dat->data->master); | 
|  | 1132 |  | 
|  | 1133 | /* free memory for private data */ | 
|  | 1134 | kfree(board_dat); | 
|  | 1135 |  | 
|  | 1136 | pci_set_drvdata(pdev, NULL); | 
|  | 1137 |  | 
|  | 1138 | /* disable PCI device */ | 
|  | 1139 | pci_disable_device(pdev); | 
|  | 1140 |  | 
|  | 1141 | dev_dbg(&pdev->dev, "%s invoked pci_disable_device\n", __func__); | 
|  | 1142 | } | 
|  | 1143 |  | 
|  | 1144 | #ifdef CONFIG_PM | 
|  | 1145 | static int pch_spi_suspend(struct pci_dev *pdev, pm_message_t state) | 
|  | 1146 | { | 
|  | 1147 | u8 count; | 
|  | 1148 | int retval; | 
|  | 1149 |  | 
|  | 1150 | struct pch_spi_board_data *board_dat = pci_get_drvdata(pdev); | 
|  | 1151 |  | 
|  | 1152 | dev_dbg(&pdev->dev, "%s ENTRY\n", __func__); | 
|  | 1153 |  | 
|  | 1154 | if (!board_dat) { | 
|  | 1155 | dev_err(&pdev->dev, | 
|  | 1156 | "%s pci_get_drvdata returned NULL\n", __func__); | 
|  | 1157 | return -EFAULT; | 
|  | 1158 | } | 
|  | 1159 |  | 
|  | 1160 | retval = 0; | 
|  | 1161 | board_dat->suspend_sts = true; | 
|  | 1162 |  | 
|  | 1163 | /* check if the current message is processed: | 
|  | 1164 | Only after thats done the transfer will be suspended */ | 
|  | 1165 | count = 255; | 
|  | 1166 | while ((--count) > 0) { | 
|  | 1167 | if (!(board_dat->data->bcurrent_msg_processing)) { | 
|  | 1168 | dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_" | 
|  | 1169 | "msg_processing = false\n", __func__); | 
|  | 1170 | break; | 
|  | 1171 | } else { | 
|  | 1172 | dev_dbg(&pdev->dev, "%s board_dat->data->bCurrent_msg_" | 
|  | 1173 | "processing = true\n", __func__); | 
|  | 1174 | } | 
|  | 1175 | msleep(PCH_SLEEP_TIME); | 
|  | 1176 | } | 
|  | 1177 |  | 
|  | 1178 | /* Free IRQ */ | 
|  | 1179 | if (board_dat->irq_reg_sts) { | 
|  | 1180 | /* disable all interrupts */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 1181 | pch_spi_setclr_reg(board_dat->data->master, PCH_SPCR, 0, | 
|  | 1182 | PCH_ALL); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 1183 | pch_spi_reset(board_dat->data->master); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 1184 |  | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 1185 | free_irq(board_dat->pdev->irq, board_dat); | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 1186 |  | 
|  | 1187 | board_dat->irq_reg_sts = false; | 
|  | 1188 | dev_dbg(&pdev->dev, | 
|  | 1189 | "%s free_irq invoked successfully.\n", __func__); | 
|  | 1190 | } | 
|  | 1191 |  | 
|  | 1192 | /* save config space */ | 
|  | 1193 | retval = pci_save_state(pdev); | 
|  | 1194 |  | 
|  | 1195 | if (retval == 0) { | 
|  | 1196 | dev_dbg(&pdev->dev, "%s pci_save_state returned=%d\n", | 
|  | 1197 | __func__, retval); | 
|  | 1198 | /* disable PM notifications */ | 
|  | 1199 | pci_enable_wake(pdev, PCI_D3hot, 0); | 
|  | 1200 | dev_dbg(&pdev->dev, | 
|  | 1201 | "%s pci_enable_wake invoked successfully\n", __func__); | 
|  | 1202 | /* disable PCI device */ | 
|  | 1203 | pci_disable_device(pdev); | 
|  | 1204 | dev_dbg(&pdev->dev, | 
|  | 1205 | "%s pci_disable_device invoked successfully\n", | 
|  | 1206 | __func__); | 
|  | 1207 | /* move device to D3hot  state */ | 
|  | 1208 | pci_set_power_state(pdev, PCI_D3hot); | 
|  | 1209 | dev_dbg(&pdev->dev, | 
|  | 1210 | "%s pci_set_power_state invoked successfully\n", | 
|  | 1211 | __func__); | 
|  | 1212 | } else { | 
|  | 1213 | dev_err(&pdev->dev, "%s pci_save_state failed\n", __func__); | 
|  | 1214 | } | 
|  | 1215 |  | 
|  | 1216 | dev_dbg(&pdev->dev, "%s return=%d\n", __func__, retval); | 
|  | 1217 |  | 
|  | 1218 | return retval; | 
|  | 1219 | } | 
|  | 1220 |  | 
|  | 1221 | static int pch_spi_resume(struct pci_dev *pdev) | 
|  | 1222 | { | 
|  | 1223 | int retval; | 
|  | 1224 |  | 
|  | 1225 | struct pch_spi_board_data *board = pci_get_drvdata(pdev); | 
|  | 1226 | dev_dbg(&pdev->dev, "%s ENTRY\n", __func__); | 
|  | 1227 |  | 
|  | 1228 | if (!board) { | 
|  | 1229 | dev_err(&pdev->dev, | 
|  | 1230 | "%s pci_get_drvdata returned NULL\n", __func__); | 
|  | 1231 | return -EFAULT; | 
|  | 1232 | } | 
|  | 1233 |  | 
|  | 1234 | /* move device to DO power state */ | 
|  | 1235 | pci_set_power_state(pdev, PCI_D0); | 
|  | 1236 |  | 
|  | 1237 | /* restore state */ | 
|  | 1238 | pci_restore_state(pdev); | 
|  | 1239 |  | 
|  | 1240 | retval = pci_enable_device(pdev); | 
|  | 1241 | if (retval < 0) { | 
|  | 1242 | dev_err(&pdev->dev, | 
|  | 1243 | "%s pci_enable_device failed\n", __func__); | 
|  | 1244 | } else { | 
|  | 1245 | /* disable PM notifications */ | 
|  | 1246 | pci_enable_wake(pdev, PCI_D3hot, 0); | 
|  | 1247 |  | 
|  | 1248 | /* register IRQ handler */ | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 1249 | if (!board->irq_reg_sts) { | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 1250 | /* register IRQ */ | 
|  | 1251 | retval = request_irq(board->pdev->irq, pch_spi_handler, | 
|  | 1252 | IRQF_SHARED, KBUILD_MODNAME, | 
|  | 1253 | board); | 
|  | 1254 | if (retval < 0) { | 
|  | 1255 | dev_err(&pdev->dev, | 
|  | 1256 | "%s request_irq failed\n", __func__); | 
|  | 1257 | return retval; | 
|  | 1258 | } | 
|  | 1259 | board->irq_reg_sts = true; | 
|  | 1260 |  | 
|  | 1261 | /* reset PCH SPI h/w */ | 
|  | 1262 | pch_spi_reset(board->data->master); | 
|  | 1263 | pch_spi_set_master_mode(board->data->master); | 
|  | 1264 |  | 
|  | 1265 | /* set suspend status to false */ | 
|  | 1266 | board->suspend_sts = false; | 
|  | 1267 |  | 
|  | 1268 | } | 
|  | 1269 | } | 
|  | 1270 |  | 
|  | 1271 | dev_dbg(&pdev->dev, "%s returning=%d\n", __func__, retval); | 
|  | 1272 |  | 
|  | 1273 | return retval; | 
|  | 1274 | } | 
|  | 1275 | #else | 
|  | 1276 | #define pch_spi_suspend NULL | 
|  | 1277 | #define pch_spi_resume NULL | 
|  | 1278 |  | 
|  | 1279 | #endif | 
|  | 1280 |  | 
|  | 1281 | static struct pci_driver pch_spi_pcidev = { | 
|  | 1282 | .name = "pch_spi", | 
|  | 1283 | .id_table = pch_spi_pcidev_id, | 
|  | 1284 | .probe = pch_spi_probe, | 
|  | 1285 | .remove = pch_spi_remove, | 
|  | 1286 | .suspend = pch_spi_suspend, | 
|  | 1287 | .resume = pch_spi_resume, | 
|  | 1288 | }; | 
|  | 1289 |  | 
|  | 1290 | static int __init pch_spi_init(void) | 
|  | 1291 | { | 
|  | 1292 | return pci_register_driver(&pch_spi_pcidev); | 
|  | 1293 | } | 
|  | 1294 | module_init(pch_spi_init); | 
|  | 1295 |  | 
| Masayuki Ohtake | e8b17b5 | 2010-10-08 12:44:49 -0600 | [diff] [blame] | 1296 | static void __exit pch_spi_exit(void) | 
|  | 1297 | { | 
|  | 1298 | pci_unregister_driver(&pch_spi_pcidev); | 
|  | 1299 | } | 
|  | 1300 | module_exit(pch_spi_exit); | 
|  | 1301 |  | 
|  | 1302 | MODULE_LICENSE("GPL"); | 
| Grant Likely | 65308c4 | 2010-09-29 17:31:34 +0900 | [diff] [blame] | 1303 | MODULE_DESCRIPTION("Topcliff PCH SPI PCI Driver"); |