xref: /openbmc/linux/drivers/spi/spi-dln2.c (revision 2b5e368e)
13d8c0d74SLaurentiu Palcu /*
23d8c0d74SLaurentiu Palcu  * Driver for the Diolan DLN-2 USB-SPI adapter
33d8c0d74SLaurentiu Palcu  *
43d8c0d74SLaurentiu Palcu  * Copyright (c) 2014 Intel Corporation
53d8c0d74SLaurentiu Palcu  *
63d8c0d74SLaurentiu Palcu  * This program is free software; you can redistribute it and/or
73d8c0d74SLaurentiu Palcu  * modify it under the terms of the GNU General Public License as
83d8c0d74SLaurentiu Palcu  * published by the Free Software Foundation, version 2.
93d8c0d74SLaurentiu Palcu  */
103d8c0d74SLaurentiu Palcu 
113d8c0d74SLaurentiu Palcu #include <linux/kernel.h>
123d8c0d74SLaurentiu Palcu #include <linux/module.h>
133d8c0d74SLaurentiu Palcu #include <linux/platform_device.h>
143d8c0d74SLaurentiu Palcu #include <linux/mfd/dln2.h>
153d8c0d74SLaurentiu Palcu #include <linux/spi/spi.h>
163d8c0d74SLaurentiu Palcu #include <linux/pm_runtime.h>
173d8c0d74SLaurentiu Palcu #include <asm/unaligned.h>
183d8c0d74SLaurentiu Palcu 
193d8c0d74SLaurentiu Palcu #define DLN2_SPI_MODULE_ID		0x02
203d8c0d74SLaurentiu Palcu #define DLN2_SPI_CMD(cmd)		DLN2_CMD(cmd, DLN2_SPI_MODULE_ID)
213d8c0d74SLaurentiu Palcu 
223d8c0d74SLaurentiu Palcu /* SPI commands */
233d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_PORT_COUNT			DLN2_SPI_CMD(0x00)
243d8c0d74SLaurentiu Palcu #define DLN2_SPI_ENABLE				DLN2_SPI_CMD(0x11)
253d8c0d74SLaurentiu Palcu #define DLN2_SPI_DISABLE			DLN2_SPI_CMD(0x12)
263d8c0d74SLaurentiu Palcu #define DLN2_SPI_IS_ENABLED			DLN2_SPI_CMD(0x13)
273d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_MODE			DLN2_SPI_CMD(0x14)
283d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MODE			DLN2_SPI_CMD(0x15)
293d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_FRAME_SIZE			DLN2_SPI_CMD(0x16)
303d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_FRAME_SIZE			DLN2_SPI_CMD(0x17)
313d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_FREQUENCY			DLN2_SPI_CMD(0x18)
323d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_FREQUENCY			DLN2_SPI_CMD(0x19)
333d8c0d74SLaurentiu Palcu #define DLN2_SPI_READ_WRITE			DLN2_SPI_CMD(0x1A)
343d8c0d74SLaurentiu Palcu #define DLN2_SPI_READ				DLN2_SPI_CMD(0x1B)
353d8c0d74SLaurentiu Palcu #define DLN2_SPI_WRITE				DLN2_SPI_CMD(0x1C)
363d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_DELAY_BETWEEN_SS		DLN2_SPI_CMD(0x20)
373d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_DELAY_BETWEEN_SS		DLN2_SPI_CMD(0x21)
383d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_DELAY_AFTER_SS		DLN2_SPI_CMD(0x22)
393d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_DELAY_AFTER_SS		DLN2_SPI_CMD(0x23)
403d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_DELAY_BETWEEN_FRAMES	DLN2_SPI_CMD(0x24)
413d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_DELAY_BETWEEN_FRAMES	DLN2_SPI_CMD(0x25)
423d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_SS				DLN2_SPI_CMD(0x26)
433d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_SS				DLN2_SPI_CMD(0x27)
443d8c0d74SLaurentiu Palcu #define DLN2_SPI_RELEASE_SS			DLN2_SPI_CMD(0x28)
453d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_VARIABLE_ENABLE		DLN2_SPI_CMD(0x2B)
463d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_VARIABLE_DISABLE		DLN2_SPI_CMD(0x2C)
473d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_VARIABLE_IS_ENABLED		DLN2_SPI_CMD(0x2D)
483d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_AAT_ENABLE			DLN2_SPI_CMD(0x2E)
493d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_AAT_DISABLE			DLN2_SPI_CMD(0x2F)
503d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_AAT_IS_ENABLED		DLN2_SPI_CMD(0x30)
513d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_BETWEEN_FRAMES_ENABLE	DLN2_SPI_CMD(0x31)
523d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_BETWEEN_FRAMES_DISABLE	DLN2_SPI_CMD(0x32)
533d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_BETWEEN_FRAMES_IS_ENABLED	DLN2_SPI_CMD(0x33)
543d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_CPHA			DLN2_SPI_CMD(0x34)
553d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_CPHA			DLN2_SPI_CMD(0x35)
563d8c0d74SLaurentiu Palcu #define DLN2_SPI_SET_CPOL			DLN2_SPI_CMD(0x36)
573d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_CPOL			DLN2_SPI_CMD(0x37)
583d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_MULTI_ENABLE		DLN2_SPI_CMD(0x38)
593d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_MULTI_DISABLE		DLN2_SPI_CMD(0x39)
603d8c0d74SLaurentiu Palcu #define DLN2_SPI_SS_MULTI_IS_ENABLED		DLN2_SPI_CMD(0x3A)
613d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_SUPPORTED_MODES		DLN2_SPI_CMD(0x40)
623d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_SUPPORTED_CPHA_VALUES	DLN2_SPI_CMD(0x41)
633d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_SUPPORTED_CPOL_VALUES	DLN2_SPI_CMD(0x42)
643d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_SUPPORTED_FRAME_SIZES	DLN2_SPI_CMD(0x43)
653d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_SS_COUNT			DLN2_SPI_CMD(0x44)
663d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MIN_FREQUENCY		DLN2_SPI_CMD(0x45)
673d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MAX_FREQUENCY		DLN2_SPI_CMD(0x46)
683d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MIN_DELAY_BETWEEN_SS	DLN2_SPI_CMD(0x47)
693d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MAX_DELAY_BETWEEN_SS	DLN2_SPI_CMD(0x48)
703d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MIN_DELAY_AFTER_SS		DLN2_SPI_CMD(0x49)
713d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MAX_DELAY_AFTER_SS		DLN2_SPI_CMD(0x4A)
723d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MIN_DELAY_BETWEEN_FRAMES	DLN2_SPI_CMD(0x4B)
733d8c0d74SLaurentiu Palcu #define DLN2_SPI_GET_MAX_DELAY_BETWEEN_FRAMES	DLN2_SPI_CMD(0x4C)
743d8c0d74SLaurentiu Palcu 
753d8c0d74SLaurentiu Palcu #define DLN2_SPI_MAX_XFER_SIZE			256
763d8c0d74SLaurentiu Palcu #define DLN2_SPI_BUF_SIZE			(DLN2_SPI_MAX_XFER_SIZE + 16)
773d8c0d74SLaurentiu Palcu #define DLN2_SPI_ATTR_LEAVE_SS_LOW		BIT(0)
783d8c0d74SLaurentiu Palcu #define DLN2_TRANSFERS_WAIT_COMPLETE		1
793d8c0d74SLaurentiu Palcu #define DLN2_TRANSFERS_CANCEL			0
803d8c0d74SLaurentiu Palcu #define DLN2_RPM_AUTOSUSPEND_TIMEOUT		2000
813d8c0d74SLaurentiu Palcu 
823d8c0d74SLaurentiu Palcu struct dln2_spi {
833d8c0d74SLaurentiu Palcu 	struct platform_device *pdev;
843d8c0d74SLaurentiu Palcu 	struct spi_master *master;
853d8c0d74SLaurentiu Palcu 	u8 port;
863d8c0d74SLaurentiu Palcu 
873d8c0d74SLaurentiu Palcu 	/*
883d8c0d74SLaurentiu Palcu 	 * This buffer will be used mainly for read/write operations. Since
893d8c0d74SLaurentiu Palcu 	 * they're quite large, we cannot use the stack. Protection is not
903d8c0d74SLaurentiu Palcu 	 * needed because all SPI communication is serialized by the SPI core.
913d8c0d74SLaurentiu Palcu 	 */
923d8c0d74SLaurentiu Palcu 	void *buf;
933d8c0d74SLaurentiu Palcu 
943d8c0d74SLaurentiu Palcu 	u8 bpw;
953d8c0d74SLaurentiu Palcu 	u32 speed;
963d8c0d74SLaurentiu Palcu 	u16 mode;
973d8c0d74SLaurentiu Palcu 	u8 cs;
983d8c0d74SLaurentiu Palcu };
993d8c0d74SLaurentiu Palcu 
1003d8c0d74SLaurentiu Palcu /*
1013d8c0d74SLaurentiu Palcu  * Enable/Disable SPI module. The disable command will wait for transfers to
1023d8c0d74SLaurentiu Palcu  * complete first.
1033d8c0d74SLaurentiu Palcu  */
1043d8c0d74SLaurentiu Palcu static int dln2_spi_enable(struct dln2_spi *dln2, bool enable)
1053d8c0d74SLaurentiu Palcu {
1063d8c0d74SLaurentiu Palcu 	u16 cmd;
1073d8c0d74SLaurentiu Palcu 	struct {
1083d8c0d74SLaurentiu Palcu 		u8 port;
1093d8c0d74SLaurentiu Palcu 		u8 wait_for_completion;
1103d8c0d74SLaurentiu Palcu 	} tx;
1113d8c0d74SLaurentiu Palcu 	unsigned len = sizeof(tx);
1123d8c0d74SLaurentiu Palcu 
1133d8c0d74SLaurentiu Palcu 	tx.port = dln2->port;
1143d8c0d74SLaurentiu Palcu 
1153d8c0d74SLaurentiu Palcu 	if (enable) {
1163d8c0d74SLaurentiu Palcu 		cmd = DLN2_SPI_ENABLE;
1173d8c0d74SLaurentiu Palcu 		len -= sizeof(tx.wait_for_completion);
1183d8c0d74SLaurentiu Palcu 	} else {
1193d8c0d74SLaurentiu Palcu 		tx.wait_for_completion = DLN2_TRANSFERS_WAIT_COMPLETE;
1203d8c0d74SLaurentiu Palcu 		cmd = DLN2_SPI_DISABLE;
1213d8c0d74SLaurentiu Palcu 	}
1223d8c0d74SLaurentiu Palcu 
1232b5e368eSLaurentiu Palcu 	return dln2_transfer_tx(dln2->pdev, cmd, &tx, len);
1243d8c0d74SLaurentiu Palcu }
1253d8c0d74SLaurentiu Palcu 
1263d8c0d74SLaurentiu Palcu /*
1273d8c0d74SLaurentiu Palcu  * Select/unselect multiple CS lines. The selected lines will be automatically
1283d8c0d74SLaurentiu Palcu  * toggled LOW/HIGH by the board firmware during transfers, provided they're
1293d8c0d74SLaurentiu Palcu  * enabled first.
1303d8c0d74SLaurentiu Palcu  *
1313d8c0d74SLaurentiu Palcu  * Ex: cs_mask = 0x03 -> CS0 & CS1 will be selected and the next WR/RD operation
1323d8c0d74SLaurentiu Palcu  *                       will toggle the lines LOW/HIGH automatically.
1333d8c0d74SLaurentiu Palcu  */
1343d8c0d74SLaurentiu Palcu static int dln2_spi_cs_set(struct dln2_spi *dln2, u8 cs_mask)
1353d8c0d74SLaurentiu Palcu {
1363d8c0d74SLaurentiu Palcu 	struct {
1373d8c0d74SLaurentiu Palcu 		u8 port;
1383d8c0d74SLaurentiu Palcu 		u8 cs;
1393d8c0d74SLaurentiu Palcu 	} tx;
1403d8c0d74SLaurentiu Palcu 
1413d8c0d74SLaurentiu Palcu 	tx.port = dln2->port;
1423d8c0d74SLaurentiu Palcu 
1433d8c0d74SLaurentiu Palcu 	/*
1443d8c0d74SLaurentiu Palcu 	 * According to Diolan docs, "a slave device can be selected by changing
1453d8c0d74SLaurentiu Palcu 	 * the corresponding bit value to 0". The rest must be set to 1. Hence
1463d8c0d74SLaurentiu Palcu 	 * the bitwise NOT in front.
1473d8c0d74SLaurentiu Palcu 	 */
1483d8c0d74SLaurentiu Palcu 	tx.cs = ~cs_mask;
1493d8c0d74SLaurentiu Palcu 
1503d8c0d74SLaurentiu Palcu 	return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_SS, &tx, sizeof(tx));
1513d8c0d74SLaurentiu Palcu }
1523d8c0d74SLaurentiu Palcu 
1533d8c0d74SLaurentiu Palcu /*
1543d8c0d74SLaurentiu Palcu  * Select one CS line. The other lines will be un-selected.
1553d8c0d74SLaurentiu Palcu  */
1563d8c0d74SLaurentiu Palcu static int dln2_spi_cs_set_one(struct dln2_spi *dln2, u8 cs)
1573d8c0d74SLaurentiu Palcu {
1583d8c0d74SLaurentiu Palcu 	return dln2_spi_cs_set(dln2, BIT(cs));
1593d8c0d74SLaurentiu Palcu }
1603d8c0d74SLaurentiu Palcu 
1613d8c0d74SLaurentiu Palcu /*
1623d8c0d74SLaurentiu Palcu  * Enable/disable CS lines for usage. The module has to be disabled first.
1633d8c0d74SLaurentiu Palcu  */
1643d8c0d74SLaurentiu Palcu static int dln2_spi_cs_enable(struct dln2_spi *dln2, u8 cs_mask, bool enable)
1653d8c0d74SLaurentiu Palcu {
1663d8c0d74SLaurentiu Palcu 	struct {
1673d8c0d74SLaurentiu Palcu 		u8 port;
1683d8c0d74SLaurentiu Palcu 		u8 cs;
1693d8c0d74SLaurentiu Palcu 	} tx;
1703d8c0d74SLaurentiu Palcu 	u16 cmd;
1713d8c0d74SLaurentiu Palcu 
1723d8c0d74SLaurentiu Palcu 	tx.port = dln2->port;
1733d8c0d74SLaurentiu Palcu 	tx.cs = cs_mask;
1743d8c0d74SLaurentiu Palcu 	cmd = enable ? DLN2_SPI_SS_MULTI_ENABLE : DLN2_SPI_SS_MULTI_DISABLE;
1753d8c0d74SLaurentiu Palcu 
1763d8c0d74SLaurentiu Palcu 	return dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx));
1773d8c0d74SLaurentiu Palcu }
1783d8c0d74SLaurentiu Palcu 
1793d8c0d74SLaurentiu Palcu static int dln2_spi_cs_enable_all(struct dln2_spi *dln2, bool enable)
1803d8c0d74SLaurentiu Palcu {
1813d8c0d74SLaurentiu Palcu 	u8 cs_mask = GENMASK(dln2->master->num_chipselect - 1, 0);
1823d8c0d74SLaurentiu Palcu 
1833d8c0d74SLaurentiu Palcu 	return dln2_spi_cs_enable(dln2, cs_mask, enable);
1843d8c0d74SLaurentiu Palcu }
1853d8c0d74SLaurentiu Palcu 
1863d8c0d74SLaurentiu Palcu static int dln2_spi_get_cs_num(struct dln2_spi *dln2, u16 *cs_num)
1873d8c0d74SLaurentiu Palcu {
1883d8c0d74SLaurentiu Palcu 	int ret;
1893d8c0d74SLaurentiu Palcu 	struct {
1903d8c0d74SLaurentiu Palcu 		u8 port;
1913d8c0d74SLaurentiu Palcu 	} tx;
1923d8c0d74SLaurentiu Palcu 	struct {
1933d8c0d74SLaurentiu Palcu 		__le16 cs_count;
1943d8c0d74SLaurentiu Palcu 	} rx;
1953d8c0d74SLaurentiu Palcu 	unsigned rx_len = sizeof(rx);
1963d8c0d74SLaurentiu Palcu 
1973d8c0d74SLaurentiu Palcu 	tx.port = dln2->port;
1983d8c0d74SLaurentiu Palcu 	ret = dln2_transfer(dln2->pdev, DLN2_SPI_GET_SS_COUNT, &tx, sizeof(tx),
1993d8c0d74SLaurentiu Palcu 			    &rx, &rx_len);
2003d8c0d74SLaurentiu Palcu 	if (ret < 0)
2013d8c0d74SLaurentiu Palcu 		return ret;
2023d8c0d74SLaurentiu Palcu 	if (rx_len < sizeof(rx))
2033d8c0d74SLaurentiu Palcu 		return -EPROTO;
2043d8c0d74SLaurentiu Palcu 
2053d8c0d74SLaurentiu Palcu 	*cs_num = le16_to_cpu(rx.cs_count);
2063d8c0d74SLaurentiu Palcu 
2073d8c0d74SLaurentiu Palcu 	dev_dbg(&dln2->pdev->dev, "cs_num = %d\n", *cs_num);
2083d8c0d74SLaurentiu Palcu 
2093d8c0d74SLaurentiu Palcu 	return 0;
2103d8c0d74SLaurentiu Palcu }
2113d8c0d74SLaurentiu Palcu 
2123d8c0d74SLaurentiu Palcu static int dln2_spi_get_speed(struct dln2_spi *dln2, u16 cmd, u32 *freq)
2133d8c0d74SLaurentiu Palcu {
2143d8c0d74SLaurentiu Palcu 	int ret;
2153d8c0d74SLaurentiu Palcu 	struct {
2163d8c0d74SLaurentiu Palcu 		u8 port;
2173d8c0d74SLaurentiu Palcu 	} tx;
2183d8c0d74SLaurentiu Palcu 	struct {
2193d8c0d74SLaurentiu Palcu 		__le32 speed;
2203d8c0d74SLaurentiu Palcu 	} rx;
2213d8c0d74SLaurentiu Palcu 	unsigned rx_len = sizeof(rx);
2223d8c0d74SLaurentiu Palcu 
2233d8c0d74SLaurentiu Palcu 	tx.port = dln2->port;
2243d8c0d74SLaurentiu Palcu 
2253d8c0d74SLaurentiu Palcu 	ret = dln2_transfer(dln2->pdev, cmd, &tx, sizeof(tx), &rx, &rx_len);
2263d8c0d74SLaurentiu Palcu 	if (ret < 0)
2273d8c0d74SLaurentiu Palcu 		return ret;
2283d8c0d74SLaurentiu Palcu 	if (rx_len < sizeof(rx))
2293d8c0d74SLaurentiu Palcu 		return -EPROTO;
2303d8c0d74SLaurentiu Palcu 
2313d8c0d74SLaurentiu Palcu 	*freq = le32_to_cpu(rx.speed);
2323d8c0d74SLaurentiu Palcu 
2333d8c0d74SLaurentiu Palcu 	return 0;
2343d8c0d74SLaurentiu Palcu }
2353d8c0d74SLaurentiu Palcu 
2363d8c0d74SLaurentiu Palcu /*
2373d8c0d74SLaurentiu Palcu  * Get bus min/max frequencies.
2383d8c0d74SLaurentiu Palcu  */
2393d8c0d74SLaurentiu Palcu static int dln2_spi_get_speed_range(struct dln2_spi *dln2, u32 *fmin, u32 *fmax)
2403d8c0d74SLaurentiu Palcu {
2413d8c0d74SLaurentiu Palcu 	int ret;
2423d8c0d74SLaurentiu Palcu 
2433d8c0d74SLaurentiu Palcu 	ret = dln2_spi_get_speed(dln2, DLN2_SPI_GET_MIN_FREQUENCY, fmin);
2443d8c0d74SLaurentiu Palcu 	if (ret < 0)
2453d8c0d74SLaurentiu Palcu 		return ret;
2463d8c0d74SLaurentiu Palcu 
2473d8c0d74SLaurentiu Palcu 	ret = dln2_spi_get_speed(dln2, DLN2_SPI_GET_MAX_FREQUENCY, fmax);
2483d8c0d74SLaurentiu Palcu 	if (ret < 0)
2493d8c0d74SLaurentiu Palcu 		return ret;
2503d8c0d74SLaurentiu Palcu 
2513d8c0d74SLaurentiu Palcu 	dev_dbg(&dln2->pdev->dev, "freq_min = %d, freq_max = %d\n",
2523d8c0d74SLaurentiu Palcu 		*fmin, *fmax);
2533d8c0d74SLaurentiu Palcu 
2543d8c0d74SLaurentiu Palcu 	return 0;
2553d8c0d74SLaurentiu Palcu }
2563d8c0d74SLaurentiu Palcu 
2573d8c0d74SLaurentiu Palcu /*
2583d8c0d74SLaurentiu Palcu  * Set the bus speed. The module will automatically round down to the closest
2593d8c0d74SLaurentiu Palcu  * available frequency and returns it. The module has to be disabled first.
2603d8c0d74SLaurentiu Palcu  */
2613d8c0d74SLaurentiu Palcu static int dln2_spi_set_speed(struct dln2_spi *dln2, u32 speed)
2623d8c0d74SLaurentiu Palcu {
2633d8c0d74SLaurentiu Palcu 	int ret;
2643d8c0d74SLaurentiu Palcu 	struct {
2653d8c0d74SLaurentiu Palcu 		u8 port;
2663d8c0d74SLaurentiu Palcu 		__le32 speed;
2673d8c0d74SLaurentiu Palcu 	} __packed tx;
2683d8c0d74SLaurentiu Palcu 	struct {
2693d8c0d74SLaurentiu Palcu 		__le32 speed;
2703d8c0d74SLaurentiu Palcu 	} rx;
2713d8c0d74SLaurentiu Palcu 	int rx_len = sizeof(rx);
2723d8c0d74SLaurentiu Palcu 
2733d8c0d74SLaurentiu Palcu 	tx.port = dln2->port;
2743d8c0d74SLaurentiu Palcu 	tx.speed = cpu_to_le32(speed);
2753d8c0d74SLaurentiu Palcu 
2763d8c0d74SLaurentiu Palcu 	ret = dln2_transfer(dln2->pdev, DLN2_SPI_SET_FREQUENCY, &tx, sizeof(tx),
2773d8c0d74SLaurentiu Palcu 			    &rx, &rx_len);
2783d8c0d74SLaurentiu Palcu 	if (ret < 0)
2793d8c0d74SLaurentiu Palcu 		return ret;
2803d8c0d74SLaurentiu Palcu 	if (rx_len < sizeof(rx))
2813d8c0d74SLaurentiu Palcu 		return -EPROTO;
2823d8c0d74SLaurentiu Palcu 
2833d8c0d74SLaurentiu Palcu 	return 0;
2843d8c0d74SLaurentiu Palcu }
2853d8c0d74SLaurentiu Palcu 
2863d8c0d74SLaurentiu Palcu /*
2873d8c0d74SLaurentiu Palcu  * Change CPOL & CPHA. The module has to be disabled first.
2883d8c0d74SLaurentiu Palcu  */
2893d8c0d74SLaurentiu Palcu static int dln2_spi_set_mode(struct dln2_spi *dln2, u8 mode)
2903d8c0d74SLaurentiu Palcu {
2913d8c0d74SLaurentiu Palcu 	struct {
2923d8c0d74SLaurentiu Palcu 		u8 port;
2933d8c0d74SLaurentiu Palcu 		u8 mode;
2943d8c0d74SLaurentiu Palcu 	} tx;
2953d8c0d74SLaurentiu Palcu 
2963d8c0d74SLaurentiu Palcu 	tx.port = dln2->port;
2973d8c0d74SLaurentiu Palcu 	tx.mode = mode;
2983d8c0d74SLaurentiu Palcu 
2993d8c0d74SLaurentiu Palcu 	return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_MODE, &tx, sizeof(tx));
3003d8c0d74SLaurentiu Palcu }
3013d8c0d74SLaurentiu Palcu 
3023d8c0d74SLaurentiu Palcu /*
3033d8c0d74SLaurentiu Palcu  * Change frame size. The module has to be disabled first.
3043d8c0d74SLaurentiu Palcu  */
3053d8c0d74SLaurentiu Palcu static int dln2_spi_set_bpw(struct dln2_spi *dln2, u8 bpw)
3063d8c0d74SLaurentiu Palcu {
3073d8c0d74SLaurentiu Palcu 	struct {
3083d8c0d74SLaurentiu Palcu 		u8 port;
3093d8c0d74SLaurentiu Palcu 		u8 bpw;
3103d8c0d74SLaurentiu Palcu 	} tx;
3113d8c0d74SLaurentiu Palcu 
3123d8c0d74SLaurentiu Palcu 	tx.port = dln2->port;
3133d8c0d74SLaurentiu Palcu 	tx.bpw = bpw;
3143d8c0d74SLaurentiu Palcu 
3153d8c0d74SLaurentiu Palcu 	return dln2_transfer_tx(dln2->pdev, DLN2_SPI_SET_FRAME_SIZE,
3163d8c0d74SLaurentiu Palcu 				&tx, sizeof(tx));
3173d8c0d74SLaurentiu Palcu }
3183d8c0d74SLaurentiu Palcu 
3193d8c0d74SLaurentiu Palcu static int dln2_spi_get_supported_frame_sizes(struct dln2_spi *dln2,
3203d8c0d74SLaurentiu Palcu 					      u32 *bpw_mask)
3213d8c0d74SLaurentiu Palcu {
3223d8c0d74SLaurentiu Palcu 	int ret;
3233d8c0d74SLaurentiu Palcu 	struct {
3243d8c0d74SLaurentiu Palcu 		u8 port;
3253d8c0d74SLaurentiu Palcu 	} tx;
3263d8c0d74SLaurentiu Palcu 	struct {
3273d8c0d74SLaurentiu Palcu 		u8 count;
3283d8c0d74SLaurentiu Palcu 		u8 frame_sizes[36];
3293d8c0d74SLaurentiu Palcu 	} *rx = dln2->buf;
3303d8c0d74SLaurentiu Palcu 	unsigned rx_len = sizeof(*rx);
3313d8c0d74SLaurentiu Palcu 	int i;
3323d8c0d74SLaurentiu Palcu 
3333d8c0d74SLaurentiu Palcu 	tx.port = dln2->port;
3343d8c0d74SLaurentiu Palcu 
3353d8c0d74SLaurentiu Palcu 	ret = dln2_transfer(dln2->pdev, DLN2_SPI_GET_SUPPORTED_FRAME_SIZES,
3363d8c0d74SLaurentiu Palcu 			    &tx, sizeof(tx), rx, &rx_len);
3373d8c0d74SLaurentiu Palcu 	if (ret < 0)
3383d8c0d74SLaurentiu Palcu 		return ret;
3393d8c0d74SLaurentiu Palcu 	if (rx_len < sizeof(*rx))
3403d8c0d74SLaurentiu Palcu 		return -EPROTO;
3413d8c0d74SLaurentiu Palcu 	if (rx->count > ARRAY_SIZE(rx->frame_sizes))
3423d8c0d74SLaurentiu Palcu 		return -EPROTO;
3433d8c0d74SLaurentiu Palcu 
3443d8c0d74SLaurentiu Palcu 	*bpw_mask = 0;
3453d8c0d74SLaurentiu Palcu 	for (i = 0; i < rx->count; i++)
3463d8c0d74SLaurentiu Palcu 		*bpw_mask |= BIT(rx->frame_sizes[i] - 1);
3473d8c0d74SLaurentiu Palcu 
3483d8c0d74SLaurentiu Palcu 	dev_dbg(&dln2->pdev->dev, "bpw_mask = 0x%X\n", *bpw_mask);
3493d8c0d74SLaurentiu Palcu 
3503d8c0d74SLaurentiu Palcu 	return 0;
3513d8c0d74SLaurentiu Palcu }
3523d8c0d74SLaurentiu Palcu 
3533d8c0d74SLaurentiu Palcu /*
3543d8c0d74SLaurentiu Palcu  * Copy the data to DLN2 buffer and change the byte order to LE, requested by
3553d8c0d74SLaurentiu Palcu  * DLN2 module. SPI core makes sure that the data length is a multiple of word
3563d8c0d74SLaurentiu Palcu  * size.
3573d8c0d74SLaurentiu Palcu  */
3583d8c0d74SLaurentiu Palcu static int dln2_spi_copy_to_buf(u8 *dln2_buf, const u8 *src, u16 len, u8 bpw)
3593d8c0d74SLaurentiu Palcu {
3603d8c0d74SLaurentiu Palcu #ifdef __LITTLE_ENDIAN
3613d8c0d74SLaurentiu Palcu 	memcpy(dln2_buf, src, len);
3623d8c0d74SLaurentiu Palcu #else
3633d8c0d74SLaurentiu Palcu 	if (bpw <= 8) {
3643d8c0d74SLaurentiu Palcu 		memcpy(dln2_buf, src, len);
3653d8c0d74SLaurentiu Palcu 	} else if (bpw <= 16) {
3663d8c0d74SLaurentiu Palcu 		__le16 *d = (__le16 *)dln2_buf;
3673d8c0d74SLaurentiu Palcu 		u16 *s = (u16 *)src;
3683d8c0d74SLaurentiu Palcu 
3693d8c0d74SLaurentiu Palcu 		len = len / 2;
3703d8c0d74SLaurentiu Palcu 		while (len--)
3713d8c0d74SLaurentiu Palcu 			*d++ = cpu_to_le16p(s++);
3723d8c0d74SLaurentiu Palcu 	} else {
3733d8c0d74SLaurentiu Palcu 		__le32 *d = (__le32 *)dln2_buf;
3743d8c0d74SLaurentiu Palcu 		u32 *s = (u32 *)src;
3753d8c0d74SLaurentiu Palcu 
3763d8c0d74SLaurentiu Palcu 		len = len / 4;
3773d8c0d74SLaurentiu Palcu 		while (len--)
3783d8c0d74SLaurentiu Palcu 			*d++ = cpu_to_le32p(s++);
3793d8c0d74SLaurentiu Palcu 	}
3803d8c0d74SLaurentiu Palcu #endif
3813d8c0d74SLaurentiu Palcu 
3823d8c0d74SLaurentiu Palcu 	return 0;
3833d8c0d74SLaurentiu Palcu }
3843d8c0d74SLaurentiu Palcu 
3853d8c0d74SLaurentiu Palcu /*
3863d8c0d74SLaurentiu Palcu  * Copy the data from DLN2 buffer and convert to CPU byte order since the DLN2
3873d8c0d74SLaurentiu Palcu  * buffer is LE ordered. SPI core makes sure that the data length is a multiple
3883d8c0d74SLaurentiu Palcu  * of word size. The RX dln2_buf is 2 byte aligned so, for BE, we have to make
3893d8c0d74SLaurentiu Palcu  * sure we avoid unaligned accesses for 32 bit case.
3903d8c0d74SLaurentiu Palcu  */
3913d8c0d74SLaurentiu Palcu static int dln2_spi_copy_from_buf(u8 *dest, const u8 *dln2_buf, u16 len, u8 bpw)
3923d8c0d74SLaurentiu Palcu {
3933d8c0d74SLaurentiu Palcu #ifdef __LITTLE_ENDIAN
3943d8c0d74SLaurentiu Palcu 	memcpy(dest, dln2_buf, len);
3953d8c0d74SLaurentiu Palcu #else
3963d8c0d74SLaurentiu Palcu 	if (bpw <= 8) {
3973d8c0d74SLaurentiu Palcu 		memcpy(dest, dln2_buf, len);
3983d8c0d74SLaurentiu Palcu 	} else if (bpw <= 16) {
3993d8c0d74SLaurentiu Palcu 		u16 *d = (u16 *)dest;
4003d8c0d74SLaurentiu Palcu 		__le16 *s = (__le16 *)dln2_buf;
4013d8c0d74SLaurentiu Palcu 
4023d8c0d74SLaurentiu Palcu 		len = len / 2;
4033d8c0d74SLaurentiu Palcu 		while (len--)
4043d8c0d74SLaurentiu Palcu 			*d++ = le16_to_cpup(s++);
4053d8c0d74SLaurentiu Palcu 	} else {
4063d8c0d74SLaurentiu Palcu 		u32 *d = (u32 *)dest;
4073d8c0d74SLaurentiu Palcu 		__le32 *s = (__le32 *)dln2_buf;
4083d8c0d74SLaurentiu Palcu 
4093d8c0d74SLaurentiu Palcu 		len = len / 4;
4103d8c0d74SLaurentiu Palcu 		while (len--)
4113d8c0d74SLaurentiu Palcu 			*d++ = get_unaligned_le32(s++);
4123d8c0d74SLaurentiu Palcu 	}
4133d8c0d74SLaurentiu Palcu #endif
4143d8c0d74SLaurentiu Palcu 
4153d8c0d74SLaurentiu Palcu 	return 0;
4163d8c0d74SLaurentiu Palcu }
4173d8c0d74SLaurentiu Palcu 
4183d8c0d74SLaurentiu Palcu /*
4193d8c0d74SLaurentiu Palcu  * Perform one write operation.
4203d8c0d74SLaurentiu Palcu  */
4213d8c0d74SLaurentiu Palcu static int dln2_spi_write_one(struct dln2_spi *dln2, const u8 *data,
4223d8c0d74SLaurentiu Palcu 			      u16 data_len, u8 attr)
4233d8c0d74SLaurentiu Palcu {
4243d8c0d74SLaurentiu Palcu 	struct {
4253d8c0d74SLaurentiu Palcu 		u8 port;
4263d8c0d74SLaurentiu Palcu 		__le16 size;
4273d8c0d74SLaurentiu Palcu 		u8 attr;
4283d8c0d74SLaurentiu Palcu 		u8 buf[DLN2_SPI_MAX_XFER_SIZE];
4293d8c0d74SLaurentiu Palcu 	} __packed *tx = dln2->buf;
4303d8c0d74SLaurentiu Palcu 	unsigned tx_len;
4313d8c0d74SLaurentiu Palcu 
4323d8c0d74SLaurentiu Palcu 	BUILD_BUG_ON(sizeof(*tx) > DLN2_SPI_BUF_SIZE);
4333d8c0d74SLaurentiu Palcu 
4343d8c0d74SLaurentiu Palcu 	if (data_len > DLN2_SPI_MAX_XFER_SIZE)
4353d8c0d74SLaurentiu Palcu 		return -EINVAL;
4363d8c0d74SLaurentiu Palcu 
4373d8c0d74SLaurentiu Palcu 	tx->port = dln2->port;
4383d8c0d74SLaurentiu Palcu 	tx->size = cpu_to_le16(data_len);
4393d8c0d74SLaurentiu Palcu 	tx->attr = attr;
4403d8c0d74SLaurentiu Palcu 
4413d8c0d74SLaurentiu Palcu 	dln2_spi_copy_to_buf(tx->buf, data, data_len, dln2->bpw);
4423d8c0d74SLaurentiu Palcu 
4433d8c0d74SLaurentiu Palcu 	tx_len = sizeof(*tx) + data_len - DLN2_SPI_MAX_XFER_SIZE;
4443d8c0d74SLaurentiu Palcu 	return dln2_transfer_tx(dln2->pdev, DLN2_SPI_WRITE, tx, tx_len);
4453d8c0d74SLaurentiu Palcu }
4463d8c0d74SLaurentiu Palcu 
4473d8c0d74SLaurentiu Palcu /*
4483d8c0d74SLaurentiu Palcu  * Perform one read operation.
4493d8c0d74SLaurentiu Palcu  */
4503d8c0d74SLaurentiu Palcu static int dln2_spi_read_one(struct dln2_spi *dln2, u8 *data,
4513d8c0d74SLaurentiu Palcu 			     u16 data_len, u8 attr)
4523d8c0d74SLaurentiu Palcu {
4533d8c0d74SLaurentiu Palcu 	int ret;
4543d8c0d74SLaurentiu Palcu 	struct {
4553d8c0d74SLaurentiu Palcu 		u8 port;
4563d8c0d74SLaurentiu Palcu 		__le16 size;
4573d8c0d74SLaurentiu Palcu 		u8 attr;
4583d8c0d74SLaurentiu Palcu 	} __packed tx;
4593d8c0d74SLaurentiu Palcu 	struct {
4603d8c0d74SLaurentiu Palcu 		__le16 size;
4613d8c0d74SLaurentiu Palcu 		u8 buf[DLN2_SPI_MAX_XFER_SIZE];
4623d8c0d74SLaurentiu Palcu 	} __packed *rx = dln2->buf;
4633d8c0d74SLaurentiu Palcu 	unsigned rx_len = sizeof(*rx);
4643d8c0d74SLaurentiu Palcu 
4653d8c0d74SLaurentiu Palcu 	BUILD_BUG_ON(sizeof(*rx) > DLN2_SPI_BUF_SIZE);
4663d8c0d74SLaurentiu Palcu 
4673d8c0d74SLaurentiu Palcu 	if (data_len > DLN2_SPI_MAX_XFER_SIZE)
4683d8c0d74SLaurentiu Palcu 		return -EINVAL;
4693d8c0d74SLaurentiu Palcu 
4703d8c0d74SLaurentiu Palcu 	tx.port = dln2->port;
4713d8c0d74SLaurentiu Palcu 	tx.size = cpu_to_le16(data_len);
4723d8c0d74SLaurentiu Palcu 	tx.attr = attr;
4733d8c0d74SLaurentiu Palcu 
4743d8c0d74SLaurentiu Palcu 	ret = dln2_transfer(dln2->pdev, DLN2_SPI_READ, &tx, sizeof(tx),
4753d8c0d74SLaurentiu Palcu 			    rx, &rx_len);
4763d8c0d74SLaurentiu Palcu 	if (ret < 0)
4773d8c0d74SLaurentiu Palcu 		return ret;
4783d8c0d74SLaurentiu Palcu 	if (rx_len < sizeof(rx->size) + data_len)
4793d8c0d74SLaurentiu Palcu 		return -EPROTO;
4803d8c0d74SLaurentiu Palcu 	if (le16_to_cpu(rx->size) != data_len)
4813d8c0d74SLaurentiu Palcu 		return -EPROTO;
4823d8c0d74SLaurentiu Palcu 
4833d8c0d74SLaurentiu Palcu 	dln2_spi_copy_from_buf(data, rx->buf, data_len, dln2->bpw);
4843d8c0d74SLaurentiu Palcu 
4853d8c0d74SLaurentiu Palcu 	return 0;
4863d8c0d74SLaurentiu Palcu }
4873d8c0d74SLaurentiu Palcu 
4883d8c0d74SLaurentiu Palcu /*
4893d8c0d74SLaurentiu Palcu  * Perform one write & read operation.
4903d8c0d74SLaurentiu Palcu  */
4913d8c0d74SLaurentiu Palcu static int dln2_spi_read_write_one(struct dln2_spi *dln2, const u8 *tx_data,
4923d8c0d74SLaurentiu Palcu 				   u8 *rx_data, u16 data_len, u8 attr)
4933d8c0d74SLaurentiu Palcu {
4943d8c0d74SLaurentiu Palcu 	int ret;
4953d8c0d74SLaurentiu Palcu 	struct {
4963d8c0d74SLaurentiu Palcu 		u8 port;
4973d8c0d74SLaurentiu Palcu 		__le16 size;
4983d8c0d74SLaurentiu Palcu 		u8 attr;
4993d8c0d74SLaurentiu Palcu 		u8 buf[DLN2_SPI_MAX_XFER_SIZE];
5003d8c0d74SLaurentiu Palcu 	} __packed *tx;
5013d8c0d74SLaurentiu Palcu 	struct {
5023d8c0d74SLaurentiu Palcu 		__le16 size;
5033d8c0d74SLaurentiu Palcu 		u8 buf[DLN2_SPI_MAX_XFER_SIZE];
5043d8c0d74SLaurentiu Palcu 	} __packed *rx;
5053d8c0d74SLaurentiu Palcu 	unsigned tx_len, rx_len;
5063d8c0d74SLaurentiu Palcu 
5073d8c0d74SLaurentiu Palcu 	BUILD_BUG_ON(sizeof(*tx) > DLN2_SPI_BUF_SIZE ||
5083d8c0d74SLaurentiu Palcu 		     sizeof(*rx) > DLN2_SPI_BUF_SIZE);
5093d8c0d74SLaurentiu Palcu 
5103d8c0d74SLaurentiu Palcu 	if (data_len > DLN2_SPI_MAX_XFER_SIZE)
5113d8c0d74SLaurentiu Palcu 		return -EINVAL;
5123d8c0d74SLaurentiu Palcu 
5133d8c0d74SLaurentiu Palcu 	/*
5143d8c0d74SLaurentiu Palcu 	 * Since this is a pseudo full-duplex communication, we're perfectly
5153d8c0d74SLaurentiu Palcu 	 * safe to use the same buffer for both tx and rx. When DLN2 sends the
5163d8c0d74SLaurentiu Palcu 	 * response back, with the rx data, we don't need the tx buffer anymore.
5173d8c0d74SLaurentiu Palcu 	 */
5183d8c0d74SLaurentiu Palcu 	tx = dln2->buf;
5193d8c0d74SLaurentiu Palcu 	rx = dln2->buf;
5203d8c0d74SLaurentiu Palcu 
5213d8c0d74SLaurentiu Palcu 	tx->port = dln2->port;
5223d8c0d74SLaurentiu Palcu 	tx->size = cpu_to_le16(data_len);
5233d8c0d74SLaurentiu Palcu 	tx->attr = attr;
5243d8c0d74SLaurentiu Palcu 
5253d8c0d74SLaurentiu Palcu 	dln2_spi_copy_to_buf(tx->buf, tx_data, data_len, dln2->bpw);
5263d8c0d74SLaurentiu Palcu 
5273d8c0d74SLaurentiu Palcu 	tx_len = sizeof(*tx) + data_len - DLN2_SPI_MAX_XFER_SIZE;
5283d8c0d74SLaurentiu Palcu 	rx_len = sizeof(*rx);
5293d8c0d74SLaurentiu Palcu 
5303d8c0d74SLaurentiu Palcu 	ret = dln2_transfer(dln2->pdev, DLN2_SPI_READ_WRITE, tx, tx_len,
5313d8c0d74SLaurentiu Palcu 			    rx, &rx_len);
5323d8c0d74SLaurentiu Palcu 	if (ret < 0)
5333d8c0d74SLaurentiu Palcu 		return ret;
5343d8c0d74SLaurentiu Palcu 	if (rx_len < sizeof(rx->size) + data_len)
5353d8c0d74SLaurentiu Palcu 		return -EPROTO;
5363d8c0d74SLaurentiu Palcu 	if (le16_to_cpu(rx->size) != data_len)
5373d8c0d74SLaurentiu Palcu 		return -EPROTO;
5383d8c0d74SLaurentiu Palcu 
5393d8c0d74SLaurentiu Palcu 	dln2_spi_copy_from_buf(rx_data, rx->buf, data_len, dln2->bpw);
5403d8c0d74SLaurentiu Palcu 
5413d8c0d74SLaurentiu Palcu 	return 0;
5423d8c0d74SLaurentiu Palcu }
5433d8c0d74SLaurentiu Palcu 
5443d8c0d74SLaurentiu Palcu /*
5453d8c0d74SLaurentiu Palcu  * Read/Write wrapper. It will automatically split an operation into multiple
5463d8c0d74SLaurentiu Palcu  * single ones due to device buffer constraints.
5473d8c0d74SLaurentiu Palcu  */
5483d8c0d74SLaurentiu Palcu static int dln2_spi_rdwr(struct dln2_spi *dln2, const u8 *tx_data,
5493d8c0d74SLaurentiu Palcu 			 u8 *rx_data, u16 data_len, u8 attr) {
5503d8c0d74SLaurentiu Palcu 	int ret;
5513d8c0d74SLaurentiu Palcu 	u16 len;
5523d8c0d74SLaurentiu Palcu 	u8 temp_attr;
5533d8c0d74SLaurentiu Palcu 	u16 remaining = data_len;
5543d8c0d74SLaurentiu Palcu 	u16 offset;
5553d8c0d74SLaurentiu Palcu 
5563d8c0d74SLaurentiu Palcu 	do {
5573d8c0d74SLaurentiu Palcu 		if (remaining > DLN2_SPI_MAX_XFER_SIZE) {
5583d8c0d74SLaurentiu Palcu 			len = DLN2_SPI_MAX_XFER_SIZE;
5593d8c0d74SLaurentiu Palcu 			temp_attr = DLN2_SPI_ATTR_LEAVE_SS_LOW;
5603d8c0d74SLaurentiu Palcu 		} else {
5613d8c0d74SLaurentiu Palcu 			len = remaining;
5623d8c0d74SLaurentiu Palcu 			temp_attr = attr;
5633d8c0d74SLaurentiu Palcu 		}
5643d8c0d74SLaurentiu Palcu 
5653d8c0d74SLaurentiu Palcu 		offset = data_len - remaining;
5663d8c0d74SLaurentiu Palcu 
5673d8c0d74SLaurentiu Palcu 		if (tx_data && rx_data) {
5683d8c0d74SLaurentiu Palcu 			ret = dln2_spi_read_write_one(dln2,
5693d8c0d74SLaurentiu Palcu 						      tx_data + offset,
5703d8c0d74SLaurentiu Palcu 						      rx_data + offset,
5713d8c0d74SLaurentiu Palcu 						      len, temp_attr);
5723d8c0d74SLaurentiu Palcu 		} else if (tx_data) {
5733d8c0d74SLaurentiu Palcu 			ret = dln2_spi_write_one(dln2,
5743d8c0d74SLaurentiu Palcu 						 tx_data + offset,
5753d8c0d74SLaurentiu Palcu 						 len, temp_attr);
5763d8c0d74SLaurentiu Palcu 		} else if (rx_data) {
5773d8c0d74SLaurentiu Palcu 			ret = dln2_spi_read_one(dln2,
5783d8c0d74SLaurentiu Palcu 						rx_data + offset,
5793d8c0d74SLaurentiu Palcu 						len, temp_attr);
5803d8c0d74SLaurentiu Palcu 		 } else {
5813d8c0d74SLaurentiu Palcu 			return -EINVAL;
5823d8c0d74SLaurentiu Palcu 		 }
5833d8c0d74SLaurentiu Palcu 
5843d8c0d74SLaurentiu Palcu 		if (ret < 0)
5853d8c0d74SLaurentiu Palcu 			return ret;
5863d8c0d74SLaurentiu Palcu 
5873d8c0d74SLaurentiu Palcu 		remaining -= len;
5883d8c0d74SLaurentiu Palcu 	} while (remaining);
5893d8c0d74SLaurentiu Palcu 
5903d8c0d74SLaurentiu Palcu 	return 0;
5913d8c0d74SLaurentiu Palcu }
5923d8c0d74SLaurentiu Palcu 
5933d8c0d74SLaurentiu Palcu static int dln2_spi_prepare_message(struct spi_master *master,
5943d8c0d74SLaurentiu Palcu 				    struct spi_message *message)
5953d8c0d74SLaurentiu Palcu {
5963d8c0d74SLaurentiu Palcu 	int ret;
5973d8c0d74SLaurentiu Palcu 	struct dln2_spi *dln2 = spi_master_get_devdata(master);
5983d8c0d74SLaurentiu Palcu 	struct spi_device *spi = message->spi;
5993d8c0d74SLaurentiu Palcu 
6003d8c0d74SLaurentiu Palcu 	if (dln2->cs != spi->chip_select) {
6013d8c0d74SLaurentiu Palcu 		ret = dln2_spi_cs_set_one(dln2, spi->chip_select);
6023d8c0d74SLaurentiu Palcu 		if (ret < 0)
6033d8c0d74SLaurentiu Palcu 			return ret;
6043d8c0d74SLaurentiu Palcu 
6053d8c0d74SLaurentiu Palcu 		dln2->cs = spi->chip_select;
6063d8c0d74SLaurentiu Palcu 	}
6073d8c0d74SLaurentiu Palcu 
6083d8c0d74SLaurentiu Palcu 	return 0;
6093d8c0d74SLaurentiu Palcu }
6103d8c0d74SLaurentiu Palcu 
6113d8c0d74SLaurentiu Palcu static int dln2_spi_transfer_setup(struct dln2_spi *dln2, u32 speed,
6123d8c0d74SLaurentiu Palcu 				   u8 bpw, u8 mode)
6133d8c0d74SLaurentiu Palcu {
6143d8c0d74SLaurentiu Palcu 	int ret;
6153d8c0d74SLaurentiu Palcu 	bool bus_setup_change;
6163d8c0d74SLaurentiu Palcu 
6173d8c0d74SLaurentiu Palcu 	bus_setup_change = dln2->speed != speed || dln2->mode != mode ||
6183d8c0d74SLaurentiu Palcu 			   dln2->bpw != bpw;
6193d8c0d74SLaurentiu Palcu 
6203d8c0d74SLaurentiu Palcu 	if (!bus_setup_change)
6213d8c0d74SLaurentiu Palcu 		return 0;
6223d8c0d74SLaurentiu Palcu 
6233d8c0d74SLaurentiu Palcu 	ret = dln2_spi_enable(dln2, false);
6243d8c0d74SLaurentiu Palcu 	if (ret < 0)
6253d8c0d74SLaurentiu Palcu 		return ret;
6263d8c0d74SLaurentiu Palcu 
6273d8c0d74SLaurentiu Palcu 	if (dln2->speed != speed) {
6283d8c0d74SLaurentiu Palcu 		ret = dln2_spi_set_speed(dln2, speed);
6293d8c0d74SLaurentiu Palcu 		if (ret < 0)
6303d8c0d74SLaurentiu Palcu 			return ret;
6313d8c0d74SLaurentiu Palcu 
6323d8c0d74SLaurentiu Palcu 		dln2->speed = speed;
6333d8c0d74SLaurentiu Palcu 	}
6343d8c0d74SLaurentiu Palcu 
6353d8c0d74SLaurentiu Palcu 	if (dln2->mode != mode) {
6363d8c0d74SLaurentiu Palcu 		ret = dln2_spi_set_mode(dln2, mode & 0x3);
6373d8c0d74SLaurentiu Palcu 		if (ret < 0)
6383d8c0d74SLaurentiu Palcu 			return ret;
6393d8c0d74SLaurentiu Palcu 
6403d8c0d74SLaurentiu Palcu 		dln2->mode = mode;
6413d8c0d74SLaurentiu Palcu 	}
6423d8c0d74SLaurentiu Palcu 
6433d8c0d74SLaurentiu Palcu 	if (dln2->bpw != bpw) {
6443d8c0d74SLaurentiu Palcu 		ret = dln2_spi_set_bpw(dln2, bpw);
6453d8c0d74SLaurentiu Palcu 		if (ret < 0)
6463d8c0d74SLaurentiu Palcu 			return ret;
6473d8c0d74SLaurentiu Palcu 
6483d8c0d74SLaurentiu Palcu 		dln2->bpw = bpw;
6493d8c0d74SLaurentiu Palcu 	}
6503d8c0d74SLaurentiu Palcu 
6512b5e368eSLaurentiu Palcu 	return dln2_spi_enable(dln2, true);
6523d8c0d74SLaurentiu Palcu }
6533d8c0d74SLaurentiu Palcu 
6543d8c0d74SLaurentiu Palcu static int dln2_spi_transfer_one(struct spi_master *master,
6553d8c0d74SLaurentiu Palcu 				 struct spi_device *spi,
6563d8c0d74SLaurentiu Palcu 				 struct spi_transfer *xfer)
6573d8c0d74SLaurentiu Palcu {
6583d8c0d74SLaurentiu Palcu 	struct dln2_spi *dln2 = spi_master_get_devdata(master);
6593d8c0d74SLaurentiu Palcu 	int status;
6603d8c0d74SLaurentiu Palcu 	u8 attr = 0;
6613d8c0d74SLaurentiu Palcu 
6623d8c0d74SLaurentiu Palcu 	status = dln2_spi_transfer_setup(dln2, xfer->speed_hz,
6633d8c0d74SLaurentiu Palcu 					 xfer->bits_per_word,
6643d8c0d74SLaurentiu Palcu 					 spi->mode);
6653d8c0d74SLaurentiu Palcu 	if (status < 0) {
6663d8c0d74SLaurentiu Palcu 		dev_err(&dln2->pdev->dev, "Cannot setup transfer\n");
6673d8c0d74SLaurentiu Palcu 		return status;
6683d8c0d74SLaurentiu Palcu 	}
6693d8c0d74SLaurentiu Palcu 
6703d8c0d74SLaurentiu Palcu 	if (!xfer->cs_change && !spi_transfer_is_last(master, xfer))
6713d8c0d74SLaurentiu Palcu 		attr = DLN2_SPI_ATTR_LEAVE_SS_LOW;
6723d8c0d74SLaurentiu Palcu 
6733d8c0d74SLaurentiu Palcu 	status = dln2_spi_rdwr(dln2, xfer->tx_buf, xfer->rx_buf,
6743d8c0d74SLaurentiu Palcu 			       xfer->len, attr);
6753d8c0d74SLaurentiu Palcu 	if (status < 0)
6763d8c0d74SLaurentiu Palcu 		dev_err(&dln2->pdev->dev, "write/read failed!\n");
6773d8c0d74SLaurentiu Palcu 
6783d8c0d74SLaurentiu Palcu 	return status;
6793d8c0d74SLaurentiu Palcu }
6803d8c0d74SLaurentiu Palcu 
6813d8c0d74SLaurentiu Palcu static int dln2_spi_probe(struct platform_device *pdev)
6823d8c0d74SLaurentiu Palcu {
6833d8c0d74SLaurentiu Palcu 	struct spi_master *master;
6843d8c0d74SLaurentiu Palcu 	struct dln2_spi *dln2;
6853d8c0d74SLaurentiu Palcu 	struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev);
6863d8c0d74SLaurentiu Palcu 	int ret;
6873d8c0d74SLaurentiu Palcu 
6883d8c0d74SLaurentiu Palcu 	master = spi_alloc_master(&pdev->dev, sizeof(*dln2));
6893d8c0d74SLaurentiu Palcu 	if (!master)
6903d8c0d74SLaurentiu Palcu 		return -ENOMEM;
6913d8c0d74SLaurentiu Palcu 
6923d8c0d74SLaurentiu Palcu 	platform_set_drvdata(pdev, master);
6933d8c0d74SLaurentiu Palcu 
6943d8c0d74SLaurentiu Palcu 	dln2 = spi_master_get_devdata(master);
6953d8c0d74SLaurentiu Palcu 
6963d8c0d74SLaurentiu Palcu 	dln2->buf = devm_kmalloc(&pdev->dev, DLN2_SPI_BUF_SIZE, GFP_KERNEL);
6973d8c0d74SLaurentiu Palcu 	if (!dln2->buf) {
6983d8c0d74SLaurentiu Palcu 		ret = -ENOMEM;
6993d8c0d74SLaurentiu Palcu 		goto exit_free_master;
7003d8c0d74SLaurentiu Palcu 	}
7013d8c0d74SLaurentiu Palcu 
7023d8c0d74SLaurentiu Palcu 	dln2->master = master;
7033d8c0d74SLaurentiu Palcu 	dln2->pdev = pdev;
7043d8c0d74SLaurentiu Palcu 	dln2->port = pdata->port;
7053d8c0d74SLaurentiu Palcu 	/* cs/mode can never be 0xff, so the first transfer will set them */
7063d8c0d74SLaurentiu Palcu 	dln2->cs = 0xff;
7073d8c0d74SLaurentiu Palcu 	dln2->mode = 0xff;
7083d8c0d74SLaurentiu Palcu 
7093d8c0d74SLaurentiu Palcu 	/* disable SPI module before continuing with the setup */
7103d8c0d74SLaurentiu Palcu 	ret = dln2_spi_enable(dln2, false);
7113d8c0d74SLaurentiu Palcu 	if (ret < 0) {
7123d8c0d74SLaurentiu Palcu 		dev_err(&pdev->dev, "Failed to disable SPI module\n");
7133d8c0d74SLaurentiu Palcu 		goto exit_free_master;
7143d8c0d74SLaurentiu Palcu 	}
7153d8c0d74SLaurentiu Palcu 
7163d8c0d74SLaurentiu Palcu 	ret = dln2_spi_get_cs_num(dln2, &master->num_chipselect);
7173d8c0d74SLaurentiu Palcu 	if (ret < 0) {
7183d8c0d74SLaurentiu Palcu 		dev_err(&pdev->dev, "Failed to get number of CS pins\n");
7193d8c0d74SLaurentiu Palcu 		goto exit_free_master;
7203d8c0d74SLaurentiu Palcu 	}
7213d8c0d74SLaurentiu Palcu 
7223d8c0d74SLaurentiu Palcu 	ret = dln2_spi_get_speed_range(dln2,
7233d8c0d74SLaurentiu Palcu 				       &master->min_speed_hz,
7243d8c0d74SLaurentiu Palcu 				       &master->max_speed_hz);
7253d8c0d74SLaurentiu Palcu 	if (ret < 0) {
7263d8c0d74SLaurentiu Palcu 		dev_err(&pdev->dev, "Failed to read bus min/max freqs\n");
7273d8c0d74SLaurentiu Palcu 		goto exit_free_master;
7283d8c0d74SLaurentiu Palcu 	}
7293d8c0d74SLaurentiu Palcu 
7303d8c0d74SLaurentiu Palcu 	ret = dln2_spi_get_supported_frame_sizes(dln2,
7313d8c0d74SLaurentiu Palcu 						 &master->bits_per_word_mask);
7323d8c0d74SLaurentiu Palcu 	if (ret < 0) {
7333d8c0d74SLaurentiu Palcu 		dev_err(&pdev->dev, "Failed to read supported frame sizes\n");
7343d8c0d74SLaurentiu Palcu 		goto exit_free_master;
7353d8c0d74SLaurentiu Palcu 	}
7363d8c0d74SLaurentiu Palcu 
7373d8c0d74SLaurentiu Palcu 	ret = dln2_spi_cs_enable_all(dln2, true);
7383d8c0d74SLaurentiu Palcu 	if (ret < 0) {
7393d8c0d74SLaurentiu Palcu 		dev_err(&pdev->dev, "Failed to enable CS pins\n");
7403d8c0d74SLaurentiu Palcu 		goto exit_free_master;
7413d8c0d74SLaurentiu Palcu 	}
7423d8c0d74SLaurentiu Palcu 
7433d8c0d74SLaurentiu Palcu 	master->bus_num = -1;
7443d8c0d74SLaurentiu Palcu 	master->mode_bits = SPI_CPOL | SPI_CPHA;
7453d8c0d74SLaurentiu Palcu 	master->prepare_message = dln2_spi_prepare_message;
7463d8c0d74SLaurentiu Palcu 	master->transfer_one = dln2_spi_transfer_one;
7473d8c0d74SLaurentiu Palcu 	master->auto_runtime_pm = true;
7483d8c0d74SLaurentiu Palcu 
7493d8c0d74SLaurentiu Palcu 	/* enable SPI module, we're good to go */
7503d8c0d74SLaurentiu Palcu 	ret = dln2_spi_enable(dln2, true);
7513d8c0d74SLaurentiu Palcu 	if (ret < 0) {
7523d8c0d74SLaurentiu Palcu 		dev_err(&pdev->dev, "Failed to enable SPI module\n");
7533d8c0d74SLaurentiu Palcu 		goto exit_free_master;
7543d8c0d74SLaurentiu Palcu 	}
7553d8c0d74SLaurentiu Palcu 
7563d8c0d74SLaurentiu Palcu 	pm_runtime_set_autosuspend_delay(&pdev->dev,
7573d8c0d74SLaurentiu Palcu 					 DLN2_RPM_AUTOSUSPEND_TIMEOUT);
7583d8c0d74SLaurentiu Palcu 	pm_runtime_use_autosuspend(&pdev->dev);
7593d8c0d74SLaurentiu Palcu 	pm_runtime_set_active(&pdev->dev);
7603d8c0d74SLaurentiu Palcu 	pm_runtime_enable(&pdev->dev);
7613d8c0d74SLaurentiu Palcu 
7623d8c0d74SLaurentiu Palcu 	ret = devm_spi_register_master(&pdev->dev, master);
7633d8c0d74SLaurentiu Palcu 	if (ret < 0) {
7643d8c0d74SLaurentiu Palcu 		dev_err(&pdev->dev, "Failed to register master\n");
7653d8c0d74SLaurentiu Palcu 		goto exit_register;
7663d8c0d74SLaurentiu Palcu 	}
7673d8c0d74SLaurentiu Palcu 
7683d8c0d74SLaurentiu Palcu 	return ret;
7693d8c0d74SLaurentiu Palcu 
7703d8c0d74SLaurentiu Palcu exit_register:
7713d8c0d74SLaurentiu Palcu 	pm_runtime_disable(&pdev->dev);
7723d8c0d74SLaurentiu Palcu 	pm_runtime_set_suspended(&pdev->dev);
7733d8c0d74SLaurentiu Palcu 
7743d8c0d74SLaurentiu Palcu 	if (dln2_spi_enable(dln2, false) < 0)
7753d8c0d74SLaurentiu Palcu 		dev_err(&pdev->dev, "Failed to disable SPI module\n");
7763d8c0d74SLaurentiu Palcu exit_free_master:
7773d8c0d74SLaurentiu Palcu 	spi_master_put(master);
7783d8c0d74SLaurentiu Palcu 
7793d8c0d74SLaurentiu Palcu 	return ret;
7803d8c0d74SLaurentiu Palcu }
7813d8c0d74SLaurentiu Palcu 
7823d8c0d74SLaurentiu Palcu static int dln2_spi_remove(struct platform_device *pdev)
7833d8c0d74SLaurentiu Palcu {
7843d8c0d74SLaurentiu Palcu 	struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
7853d8c0d74SLaurentiu Palcu 	struct dln2_spi *dln2 = spi_master_get_devdata(master);
7863d8c0d74SLaurentiu Palcu 
7873d8c0d74SLaurentiu Palcu 	pm_runtime_disable(&pdev->dev);
7883d8c0d74SLaurentiu Palcu 
7893d8c0d74SLaurentiu Palcu 	if (dln2_spi_enable(dln2, false) < 0)
7903d8c0d74SLaurentiu Palcu 		dev_err(&pdev->dev, "Failed to disable SPI module\n");
7913d8c0d74SLaurentiu Palcu 
7923d8c0d74SLaurentiu Palcu 	return 0;
7933d8c0d74SLaurentiu Palcu }
7943d8c0d74SLaurentiu Palcu 
7953d8c0d74SLaurentiu Palcu #ifdef CONFIG_PM_SLEEP
7963d8c0d74SLaurentiu Palcu static int dln2_spi_suspend(struct device *dev)
7973d8c0d74SLaurentiu Palcu {
7983d8c0d74SLaurentiu Palcu 	int ret;
7993d8c0d74SLaurentiu Palcu 	struct spi_master *master = dev_get_drvdata(dev);
8003d8c0d74SLaurentiu Palcu 	struct dln2_spi *dln2 = spi_master_get_devdata(master);
8013d8c0d74SLaurentiu Palcu 
8023d8c0d74SLaurentiu Palcu 	ret = spi_master_suspend(master);
8033d8c0d74SLaurentiu Palcu 	if (ret < 0)
8043d8c0d74SLaurentiu Palcu 		return ret;
8053d8c0d74SLaurentiu Palcu 
8063d8c0d74SLaurentiu Palcu 	if (!pm_runtime_suspended(dev)) {
8073d8c0d74SLaurentiu Palcu 		ret = dln2_spi_enable(dln2, false);
8083d8c0d74SLaurentiu Palcu 		if (ret < 0)
8093d8c0d74SLaurentiu Palcu 			return ret;
8103d8c0d74SLaurentiu Palcu 	}
8113d8c0d74SLaurentiu Palcu 
8123d8c0d74SLaurentiu Palcu 	/*
8133d8c0d74SLaurentiu Palcu 	 * USB power may be cut off during sleep. Resetting the following
8143d8c0d74SLaurentiu Palcu 	 * parameters will force the board to be set up before first transfer.
8153d8c0d74SLaurentiu Palcu 	 */
8163d8c0d74SLaurentiu Palcu 	dln2->cs = 0xff;
8173d8c0d74SLaurentiu Palcu 	dln2->speed = 0;
8183d8c0d74SLaurentiu Palcu 	dln2->bpw = 0;
8193d8c0d74SLaurentiu Palcu 	dln2->mode = 0xff;
8203d8c0d74SLaurentiu Palcu 
8213d8c0d74SLaurentiu Palcu 	return 0;
8223d8c0d74SLaurentiu Palcu }
8233d8c0d74SLaurentiu Palcu 
8243d8c0d74SLaurentiu Palcu static int dln2_spi_resume(struct device *dev)
8253d8c0d74SLaurentiu Palcu {
8263d8c0d74SLaurentiu Palcu 	int ret;
8273d8c0d74SLaurentiu Palcu 	struct spi_master *master = dev_get_drvdata(dev);
8283d8c0d74SLaurentiu Palcu 	struct dln2_spi *dln2 = spi_master_get_devdata(master);
8293d8c0d74SLaurentiu Palcu 
8303d8c0d74SLaurentiu Palcu 	if (!pm_runtime_suspended(dev)) {
8313d8c0d74SLaurentiu Palcu 		ret = dln2_spi_cs_enable_all(dln2, true);
8323d8c0d74SLaurentiu Palcu 		if (ret < 0)
8333d8c0d74SLaurentiu Palcu 			return ret;
8343d8c0d74SLaurentiu Palcu 
8353d8c0d74SLaurentiu Palcu 		ret = dln2_spi_enable(dln2, true);
8363d8c0d74SLaurentiu Palcu 		if (ret < 0)
8373d8c0d74SLaurentiu Palcu 			return ret;
8383d8c0d74SLaurentiu Palcu 	}
8393d8c0d74SLaurentiu Palcu 
8403d8c0d74SLaurentiu Palcu 	return spi_master_resume(master);
8413d8c0d74SLaurentiu Palcu }
8423d8c0d74SLaurentiu Palcu #endif /* CONFIG_PM_SLEEP */
8433d8c0d74SLaurentiu Palcu 
844b2e5dda1SMark Brown #ifdef CONFIG_PM
8453d8c0d74SLaurentiu Palcu static int dln2_spi_runtime_suspend(struct device *dev)
8463d8c0d74SLaurentiu Palcu {
8473d8c0d74SLaurentiu Palcu 	struct spi_master *master = dev_get_drvdata(dev);
8483d8c0d74SLaurentiu Palcu 	struct dln2_spi *dln2 = spi_master_get_devdata(master);
8493d8c0d74SLaurentiu Palcu 
8503d8c0d74SLaurentiu Palcu 	return dln2_spi_enable(dln2, false);
8513d8c0d74SLaurentiu Palcu }
8523d8c0d74SLaurentiu Palcu 
8533d8c0d74SLaurentiu Palcu static int dln2_spi_runtime_resume(struct device *dev)
8543d8c0d74SLaurentiu Palcu {
8553d8c0d74SLaurentiu Palcu 	struct spi_master *master = dev_get_drvdata(dev);
8563d8c0d74SLaurentiu Palcu 	struct dln2_spi *dln2 = spi_master_get_devdata(master);
8573d8c0d74SLaurentiu Palcu 
8583d8c0d74SLaurentiu Palcu 	return  dln2_spi_enable(dln2, true);
8593d8c0d74SLaurentiu Palcu }
8602b5e368eSLaurentiu Palcu #endif /* CONFIG_PM */
8613d8c0d74SLaurentiu Palcu 
8623d8c0d74SLaurentiu Palcu static const struct dev_pm_ops dln2_spi_pm = {
8633d8c0d74SLaurentiu Palcu 	SET_SYSTEM_SLEEP_PM_OPS(dln2_spi_suspend, dln2_spi_resume)
8643d8c0d74SLaurentiu Palcu 	SET_RUNTIME_PM_OPS(dln2_spi_runtime_suspend,
8653d8c0d74SLaurentiu Palcu 			   dln2_spi_runtime_resume, NULL)
8663d8c0d74SLaurentiu Palcu };
8673d8c0d74SLaurentiu Palcu 
8683d8c0d74SLaurentiu Palcu static struct platform_driver spi_dln2_driver = {
8693d8c0d74SLaurentiu Palcu 	.driver = {
8703d8c0d74SLaurentiu Palcu 		.name	= "dln2-spi",
8713d8c0d74SLaurentiu Palcu 		.pm	= &dln2_spi_pm,
8723d8c0d74SLaurentiu Palcu 	},
8733d8c0d74SLaurentiu Palcu 	.probe		= dln2_spi_probe,
8743d8c0d74SLaurentiu Palcu 	.remove		= dln2_spi_remove,
8753d8c0d74SLaurentiu Palcu };
8763d8c0d74SLaurentiu Palcu module_platform_driver(spi_dln2_driver);
8773d8c0d74SLaurentiu Palcu 
8783d8c0d74SLaurentiu Palcu MODULE_DESCRIPTION("Driver for the Diolan DLN2 SPI master interface");
8793d8c0d74SLaurentiu Palcu MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>");
8803d8c0d74SLaurentiu Palcu MODULE_LICENSE("GPL v2");
8813d8c0d74SLaurentiu Palcu MODULE_ALIAS("platform:dln2-spi");
882