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