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