1bbb336f3SSanjay R Mehta // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2bbb336f3SSanjay R Mehta //
3bbb336f3SSanjay R Mehta // AMD SPI controller driver
4bbb336f3SSanjay R Mehta //
5bbb336f3SSanjay R Mehta // Copyright (c) 2020, Advanced Micro Devices, Inc.
6bbb336f3SSanjay R Mehta //
7bbb336f3SSanjay R Mehta // Author: Sanjay R Mehta <sanju.mehta@amd.com>
8bbb336f3SSanjay R Mehta
9bbb336f3SSanjay R Mehta #include <linux/acpi.h>
10bbb336f3SSanjay R Mehta #include <linux/init.h>
11bbb336f3SSanjay R Mehta #include <linux/module.h>
12bbb336f3SSanjay R Mehta #include <linux/platform_device.h>
13bbb336f3SSanjay R Mehta #include <linux/delay.h>
14bbb336f3SSanjay R Mehta #include <linux/spi/spi.h>
15715bea35SAndré Almeida #include <linux/iopoll.h>
16bbb336f3SSanjay R Mehta
17bbb336f3SSanjay R Mehta #define AMD_SPI_CTRL0_REG 0x00
18bbb336f3SSanjay R Mehta #define AMD_SPI_EXEC_CMD BIT(16)
19bbb336f3SSanjay R Mehta #define AMD_SPI_FIFO_CLEAR BIT(20)
20bbb336f3SSanjay R Mehta #define AMD_SPI_BUSY BIT(31)
21bbb336f3SSanjay R Mehta
2220904355SAndré Almeida #define AMD_SPI_OPCODE_REG 0x45
2320904355SAndré Almeida #define AMD_SPI_CMD_TRIGGER_REG 0x47
2420904355SAndré Almeida #define AMD_SPI_TRIGGER_CMD BIT(7)
2520904355SAndré Almeida
26bbb336f3SSanjay R Mehta #define AMD_SPI_OPCODE_MASK 0xFF
27bbb336f3SSanjay R Mehta
28bbb336f3SSanjay R Mehta #define AMD_SPI_ALT_CS_REG 0x1D
29bbb336f3SSanjay R Mehta #define AMD_SPI_ALT_CS_MASK 0x3
30bbb336f3SSanjay R Mehta
31bbb336f3SSanjay R Mehta #define AMD_SPI_FIFO_BASE 0x80
32bbb336f3SSanjay R Mehta #define AMD_SPI_TX_COUNT_REG 0x48
33bbb336f3SSanjay R Mehta #define AMD_SPI_RX_COUNT_REG 0x4B
34bbb336f3SSanjay R Mehta #define AMD_SPI_STATUS_REG 0x4C
35bbb336f3SSanjay R Mehta
366ece49c5SCristian Ciocaltea #define AMD_SPI_FIFO_SIZE 70
37bbb336f3SSanjay R Mehta #define AMD_SPI_MEM_SIZE 200
38bbb336f3SSanjay R Mehta
393fe26121SLucas Tanure #define AMD_SPI_ENA_REG 0x20
403fe26121SLucas Tanure #define AMD_SPI_ALT_SPD_SHIFT 20
413fe26121SLucas Tanure #define AMD_SPI_ALT_SPD_MASK GENMASK(23, AMD_SPI_ALT_SPD_SHIFT)
423fe26121SLucas Tanure #define AMD_SPI_SPI100_SHIFT 0
433fe26121SLucas Tanure #define AMD_SPI_SPI100_MASK GENMASK(AMD_SPI_SPI100_SHIFT, AMD_SPI_SPI100_SHIFT)
443fe26121SLucas Tanure #define AMD_SPI_SPEED_REG 0x6C
453fe26121SLucas Tanure #define AMD_SPI_SPD7_SHIFT 8
463fe26121SLucas Tanure #define AMD_SPI_SPD7_MASK GENMASK(13, AMD_SPI_SPD7_SHIFT)
473fe26121SLucas Tanure
483fe26121SLucas Tanure #define AMD_SPI_MAX_HZ 100000000
493fe26121SLucas Tanure #define AMD_SPI_MIN_HZ 800000
503fe26121SLucas Tanure
5155861e36SCristian Ciocaltea /**
5255861e36SCristian Ciocaltea * enum amd_spi_versions - SPI controller versions
5355861e36SCristian Ciocaltea * @AMD_SPI_V1: AMDI0061 hardware version
5455861e36SCristian Ciocaltea * @AMD_SPI_V2: AMDI0062 hardware version
5555861e36SCristian Ciocaltea */
5620904355SAndré Almeida enum amd_spi_versions {
5755861e36SCristian Ciocaltea AMD_SPI_V1 = 1,
5855861e36SCristian Ciocaltea AMD_SPI_V2,
5920904355SAndré Almeida };
6020904355SAndré Almeida
613fe26121SLucas Tanure enum amd_spi_speed {
623fe26121SLucas Tanure F_66_66MHz,
633fe26121SLucas Tanure F_33_33MHz,
643fe26121SLucas Tanure F_22_22MHz,
653fe26121SLucas Tanure F_16_66MHz,
663fe26121SLucas Tanure F_100MHz,
673fe26121SLucas Tanure F_800KHz,
68bff6bef7SVitaly Rodionov SPI_SPD7 = 0x7,
693fe26121SLucas Tanure F_50MHz = 0x4,
703fe26121SLucas Tanure F_4MHz = 0x32,
713fe26121SLucas Tanure F_3_17MHz = 0x3F
723fe26121SLucas Tanure };
733fe26121SLucas Tanure
743fe26121SLucas Tanure /**
753fe26121SLucas Tanure * struct amd_spi_freq - Matches device speed with values to write in regs
763fe26121SLucas Tanure * @speed_hz: Device frequency
773fe26121SLucas Tanure * @enable_val: Value to be written to "enable register"
783fe26121SLucas Tanure * @spd7_val: Some frequencies requires to have a value written at SPISPEED register
793fe26121SLucas Tanure */
803fe26121SLucas Tanure struct amd_spi_freq {
813fe26121SLucas Tanure u32 speed_hz;
823fe26121SLucas Tanure u32 enable_val;
833fe26121SLucas Tanure u32 spd7_val;
843fe26121SLucas Tanure };
853fe26121SLucas Tanure
8655861e36SCristian Ciocaltea /**
8755861e36SCristian Ciocaltea * struct amd_spi - SPI driver instance
8855861e36SCristian Ciocaltea * @io_remap_addr: Start address of the SPI controller registers
8955861e36SCristian Ciocaltea * @version: SPI controller hardware version
903fe26121SLucas Tanure * @speed_hz: Device frequency
9155861e36SCristian Ciocaltea */
92bbb336f3SSanjay R Mehta struct amd_spi {
93bbb336f3SSanjay R Mehta void __iomem *io_remap_addr;
9420904355SAndré Almeida enum amd_spi_versions version;
953fe26121SLucas Tanure unsigned int speed_hz;
96bbb336f3SSanjay R Mehta };
97bbb336f3SSanjay R Mehta
amd_spi_readreg8(struct amd_spi * amd_spi,int idx)98ca8e8a18SLucas Tanure static inline u8 amd_spi_readreg8(struct amd_spi *amd_spi, int idx)
99bbb336f3SSanjay R Mehta {
100bbb336f3SSanjay R Mehta return ioread8((u8 __iomem *)amd_spi->io_remap_addr + idx);
101bbb336f3SSanjay R Mehta }
102bbb336f3SSanjay R Mehta
amd_spi_writereg8(struct amd_spi * amd_spi,int idx,u8 val)103ca8e8a18SLucas Tanure static inline void amd_spi_writereg8(struct amd_spi *amd_spi, int idx, u8 val)
104bbb336f3SSanjay R Mehta {
105bbb336f3SSanjay R Mehta iowrite8(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx));
106bbb336f3SSanjay R Mehta }
107bbb336f3SSanjay R Mehta
amd_spi_setclear_reg8(struct amd_spi * amd_spi,int idx,u8 set,u8 clear)108ca8e8a18SLucas Tanure static void amd_spi_setclear_reg8(struct amd_spi *amd_spi, int idx, u8 set, u8 clear)
109bbb336f3SSanjay R Mehta {
110ca8e8a18SLucas Tanure u8 tmp = amd_spi_readreg8(amd_spi, idx);
111bbb336f3SSanjay R Mehta
112bbb336f3SSanjay R Mehta tmp = (tmp & ~clear) | set;
113ca8e8a18SLucas Tanure amd_spi_writereg8(amd_spi, idx, tmp);
114bbb336f3SSanjay R Mehta }
115bbb336f3SSanjay R Mehta
amd_spi_readreg32(struct amd_spi * amd_spi,int idx)116ca8e8a18SLucas Tanure static inline u32 amd_spi_readreg32(struct amd_spi *amd_spi, int idx)
117bbb336f3SSanjay R Mehta {
118bbb336f3SSanjay R Mehta return ioread32((u8 __iomem *)amd_spi->io_remap_addr + idx);
119bbb336f3SSanjay R Mehta }
120bbb336f3SSanjay R Mehta
amd_spi_writereg32(struct amd_spi * amd_spi,int idx,u32 val)121ca8e8a18SLucas Tanure static inline void amd_spi_writereg32(struct amd_spi *amd_spi, int idx, u32 val)
122bbb336f3SSanjay R Mehta {
123bbb336f3SSanjay R Mehta iowrite32(val, ((u8 __iomem *)amd_spi->io_remap_addr + idx));
124bbb336f3SSanjay R Mehta }
125bbb336f3SSanjay R Mehta
amd_spi_setclear_reg32(struct amd_spi * amd_spi,int idx,u32 set,u32 clear)126ca8e8a18SLucas Tanure static inline void amd_spi_setclear_reg32(struct amd_spi *amd_spi, int idx, u32 set, u32 clear)
127bbb336f3SSanjay R Mehta {
128ca8e8a18SLucas Tanure u32 tmp = amd_spi_readreg32(amd_spi, idx);
129bbb336f3SSanjay R Mehta
130bbb336f3SSanjay R Mehta tmp = (tmp & ~clear) | set;
131ca8e8a18SLucas Tanure amd_spi_writereg32(amd_spi, idx, tmp);
132bbb336f3SSanjay R Mehta }
133bbb336f3SSanjay R Mehta
amd_spi_select_chip(struct amd_spi * amd_spi,u8 cs)1343b02d289SLucas Tanure static void amd_spi_select_chip(struct amd_spi *amd_spi, u8 cs)
135bbb336f3SSanjay R Mehta {
1363b02d289SLucas Tanure amd_spi_setclear_reg8(amd_spi, AMD_SPI_ALT_CS_REG, cs, AMD_SPI_ALT_CS_MASK);
137bbb336f3SSanjay R Mehta }
138bbb336f3SSanjay R Mehta
amd_spi_clear_chip(struct amd_spi * amd_spi,u8 chip_select)13920904355SAndré Almeida static inline void amd_spi_clear_chip(struct amd_spi *amd_spi, u8 chip_select)
14020904355SAndré Almeida {
14120904355SAndré Almeida amd_spi_writereg8(amd_spi, AMD_SPI_ALT_CS_REG, chip_select & ~AMD_SPI_ALT_CS_MASK);
14220904355SAndré Almeida }
14320904355SAndré Almeida
amd_spi_clear_fifo_ptr(struct amd_spi * amd_spi)144ca8e8a18SLucas Tanure static void amd_spi_clear_fifo_ptr(struct amd_spi *amd_spi)
145bbb336f3SSanjay R Mehta {
146ca8e8a18SLucas Tanure amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_FIFO_CLEAR, AMD_SPI_FIFO_CLEAR);
147bbb336f3SSanjay R Mehta }
148bbb336f3SSanjay R Mehta
amd_spi_set_opcode(struct amd_spi * amd_spi,u8 cmd_opcode)14920904355SAndré Almeida static int amd_spi_set_opcode(struct amd_spi *amd_spi, u8 cmd_opcode)
150bbb336f3SSanjay R Mehta {
15120904355SAndré Almeida switch (amd_spi->version) {
15220904355SAndré Almeida case AMD_SPI_V1:
15320904355SAndré Almeida amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, cmd_opcode,
15420904355SAndré Almeida AMD_SPI_OPCODE_MASK);
15520904355SAndré Almeida return 0;
15620904355SAndré Almeida case AMD_SPI_V2:
15720904355SAndré Almeida amd_spi_writereg8(amd_spi, AMD_SPI_OPCODE_REG, cmd_opcode);
15820904355SAndré Almeida return 0;
15920904355SAndré Almeida default:
16020904355SAndré Almeida return -ENODEV;
16120904355SAndré Almeida }
162bbb336f3SSanjay R Mehta }
163bbb336f3SSanjay R Mehta
amd_spi_set_rx_count(struct amd_spi * amd_spi,u8 rx_count)164ca8e8a18SLucas Tanure static inline void amd_spi_set_rx_count(struct amd_spi *amd_spi, u8 rx_count)
165bbb336f3SSanjay R Mehta {
166ca8e8a18SLucas Tanure amd_spi_setclear_reg8(amd_spi, AMD_SPI_RX_COUNT_REG, rx_count, 0xff);
167bbb336f3SSanjay R Mehta }
168bbb336f3SSanjay R Mehta
amd_spi_set_tx_count(struct amd_spi * amd_spi,u8 tx_count)169ca8e8a18SLucas Tanure static inline void amd_spi_set_tx_count(struct amd_spi *amd_spi, u8 tx_count)
170bbb336f3SSanjay R Mehta {
171ca8e8a18SLucas Tanure amd_spi_setclear_reg8(amd_spi, AMD_SPI_TX_COUNT_REG, tx_count, 0xff);
172bbb336f3SSanjay R Mehta }
173bbb336f3SSanjay R Mehta
amd_spi_busy_wait(struct amd_spi * amd_spi)174356b02f9SLucas Tanure static int amd_spi_busy_wait(struct amd_spi *amd_spi)
175bbb336f3SSanjay R Mehta {
176715bea35SAndré Almeida u32 val;
17720904355SAndré Almeida int reg;
178bbb336f3SSanjay R Mehta
17920904355SAndré Almeida switch (amd_spi->version) {
18020904355SAndré Almeida case AMD_SPI_V1:
18120904355SAndré Almeida reg = AMD_SPI_CTRL0_REG;
18220904355SAndré Almeida break;
18320904355SAndré Almeida case AMD_SPI_V2:
18420904355SAndré Almeida reg = AMD_SPI_STATUS_REG;
18520904355SAndré Almeida break;
18620904355SAndré Almeida default:
18720904355SAndré Almeida return -ENODEV;
18820904355SAndré Almeida }
18920904355SAndré Almeida
19020904355SAndré Almeida return readl_poll_timeout(amd_spi->io_remap_addr + reg, val,
19120904355SAndré Almeida !(val & AMD_SPI_BUSY), 20, 2000000);
192bbb336f3SSanjay R Mehta }
193bbb336f3SSanjay R Mehta
amd_spi_execute_opcode(struct amd_spi * amd_spi)194777a2cbbSLucas Tanure static int amd_spi_execute_opcode(struct amd_spi *amd_spi)
195bbb336f3SSanjay R Mehta {
196777a2cbbSLucas Tanure int ret;
197777a2cbbSLucas Tanure
198777a2cbbSLucas Tanure ret = amd_spi_busy_wait(amd_spi);
199777a2cbbSLucas Tanure if (ret)
200777a2cbbSLucas Tanure return ret;
201777a2cbbSLucas Tanure
20220904355SAndré Almeida switch (amd_spi->version) {
20320904355SAndré Almeida case AMD_SPI_V1:
204bbb336f3SSanjay R Mehta /* Set ExecuteOpCode bit in the CTRL0 register */
20520904355SAndré Almeida amd_spi_setclear_reg32(amd_spi, AMD_SPI_CTRL0_REG, AMD_SPI_EXEC_CMD,
20620904355SAndré Almeida AMD_SPI_EXEC_CMD);
207777a2cbbSLucas Tanure return 0;
20820904355SAndré Almeida case AMD_SPI_V2:
20920904355SAndré Almeida /* Trigger the command execution */
21020904355SAndré Almeida amd_spi_setclear_reg8(amd_spi, AMD_SPI_CMD_TRIGGER_REG,
21120904355SAndré Almeida AMD_SPI_TRIGGER_CMD, AMD_SPI_TRIGGER_CMD);
21220904355SAndré Almeida return 0;
21320904355SAndré Almeida default:
21420904355SAndré Almeida return -ENODEV;
21520904355SAndré Almeida }
216bbb336f3SSanjay R Mehta }
217bbb336f3SSanjay R Mehta
amd_spi_host_setup(struct spi_device * spi)218e9759d40SYang Yingliang static int amd_spi_host_setup(struct spi_device *spi)
219bbb336f3SSanjay R Mehta {
220e9759d40SYang Yingliang struct amd_spi *amd_spi = spi_controller_get_devdata(spi->controller);
221bbb336f3SSanjay R Mehta
222ca8e8a18SLucas Tanure amd_spi_clear_fifo_ptr(amd_spi);
223bbb336f3SSanjay R Mehta
224bbb336f3SSanjay R Mehta return 0;
225bbb336f3SSanjay R Mehta }
226bbb336f3SSanjay R Mehta
2273fe26121SLucas Tanure static const struct amd_spi_freq amd_spi_freq[] = {
2283fe26121SLucas Tanure { AMD_SPI_MAX_HZ, F_100MHz, 0},
2293fe26121SLucas Tanure { 66660000, F_66_66MHz, 0},
2303fe26121SLucas Tanure { 50000000, SPI_SPD7, F_50MHz},
2313fe26121SLucas Tanure { 33330000, F_33_33MHz, 0},
2323fe26121SLucas Tanure { 22220000, F_22_22MHz, 0},
2333fe26121SLucas Tanure { 16660000, F_16_66MHz, 0},
2343fe26121SLucas Tanure { 4000000, SPI_SPD7, F_4MHz},
2353fe26121SLucas Tanure { 3170000, SPI_SPD7, F_3_17MHz},
2363fe26121SLucas Tanure { AMD_SPI_MIN_HZ, F_800KHz, 0},
2373fe26121SLucas Tanure };
2383fe26121SLucas Tanure
amd_set_spi_freq(struct amd_spi * amd_spi,u32 speed_hz)2393fe26121SLucas Tanure static int amd_set_spi_freq(struct amd_spi *amd_spi, u32 speed_hz)
2403fe26121SLucas Tanure {
2413fe26121SLucas Tanure unsigned int i, spd7_val, alt_spd;
2423fe26121SLucas Tanure
2433fe26121SLucas Tanure if (speed_hz < AMD_SPI_MIN_HZ)
2443fe26121SLucas Tanure return -EINVAL;
2453fe26121SLucas Tanure
2463fe26121SLucas Tanure for (i = 0; i < ARRAY_SIZE(amd_spi_freq); i++)
2473fe26121SLucas Tanure if (speed_hz >= amd_spi_freq[i].speed_hz)
2483fe26121SLucas Tanure break;
2493fe26121SLucas Tanure
2509477420eSShreeya Patel if (amd_spi->speed_hz == amd_spi_freq[i].speed_hz)
2513fe26121SLucas Tanure return 0;
2523fe26121SLucas Tanure
2533fe26121SLucas Tanure amd_spi->speed_hz = amd_spi_freq[i].speed_hz;
2543fe26121SLucas Tanure
2553fe26121SLucas Tanure alt_spd = (amd_spi_freq[i].enable_val << AMD_SPI_ALT_SPD_SHIFT)
2563fe26121SLucas Tanure & AMD_SPI_ALT_SPD_MASK;
2573fe26121SLucas Tanure amd_spi_setclear_reg32(amd_spi, AMD_SPI_ENA_REG, alt_spd,
2583fe26121SLucas Tanure AMD_SPI_ALT_SPD_MASK);
2593fe26121SLucas Tanure
2603fe26121SLucas Tanure if (amd_spi->speed_hz == AMD_SPI_MAX_HZ)
2613fe26121SLucas Tanure amd_spi_setclear_reg32(amd_spi, AMD_SPI_ENA_REG, 1,
2623fe26121SLucas Tanure AMD_SPI_SPI100_MASK);
2633fe26121SLucas Tanure
2643fe26121SLucas Tanure if (amd_spi_freq[i].spd7_val) {
2653fe26121SLucas Tanure spd7_val = (amd_spi_freq[i].spd7_val << AMD_SPI_SPD7_SHIFT)
2663fe26121SLucas Tanure & AMD_SPI_SPD7_MASK;
2673fe26121SLucas Tanure amd_spi_setclear_reg32(amd_spi, AMD_SPI_SPEED_REG, spd7_val,
2683fe26121SLucas Tanure AMD_SPI_SPD7_MASK);
2693fe26121SLucas Tanure }
2703fe26121SLucas Tanure
2713fe26121SLucas Tanure return 0;
2723fe26121SLucas Tanure }
2733fe26121SLucas Tanure
amd_spi_fifo_xfer(struct amd_spi * amd_spi,struct spi_controller * host,struct spi_message * message)274bbb336f3SSanjay R Mehta static inline int amd_spi_fifo_xfer(struct amd_spi *amd_spi,
275e9759d40SYang Yingliang struct spi_controller *host,
276bbb336f3SSanjay R Mehta struct spi_message *message)
277bbb336f3SSanjay R Mehta {
278bbb336f3SSanjay R Mehta struct spi_transfer *xfer = NULL;
2793fe26121SLucas Tanure struct spi_device *spi = message->spi;
2809d08f700SCristian Ciocaltea u8 cmd_opcode = 0, fifo_pos = AMD_SPI_FIFO_BASE;
281bbb336f3SSanjay R Mehta u8 *buf = NULL;
282bbb336f3SSanjay R Mehta u32 i = 0;
283bbb336f3SSanjay R Mehta u32 tx_len = 0, rx_len = 0;
284bbb336f3SSanjay R Mehta
285bbb336f3SSanjay R Mehta list_for_each_entry(xfer, &message->transfers,
286bbb336f3SSanjay R Mehta transfer_list) {
2873fe26121SLucas Tanure if (xfer->speed_hz)
2883fe26121SLucas Tanure amd_set_spi_freq(amd_spi, xfer->speed_hz);
2893fe26121SLucas Tanure else
2903fe26121SLucas Tanure amd_set_spi_freq(amd_spi, spi->max_speed_hz);
2913fe26121SLucas Tanure
2929d08f700SCristian Ciocaltea if (xfer->tx_buf) {
293bbb336f3SSanjay R Mehta buf = (u8 *)xfer->tx_buf;
2949d08f700SCristian Ciocaltea if (!tx_len) {
295bbb336f3SSanjay R Mehta cmd_opcode = *(u8 *)xfer->tx_buf;
296bbb336f3SSanjay R Mehta buf++;
2979d08f700SCristian Ciocaltea xfer->len--;
2989d08f700SCristian Ciocaltea }
2999d08f700SCristian Ciocaltea tx_len += xfer->len;
300bbb336f3SSanjay R Mehta
301bbb336f3SSanjay R Mehta /* Write data into the FIFO. */
3029d08f700SCristian Ciocaltea for (i = 0; i < xfer->len; i++)
3039d08f700SCristian Ciocaltea amd_spi_writereg8(amd_spi, fifo_pos + i, buf[i]);
3049d08f700SCristian Ciocaltea
3059d08f700SCristian Ciocaltea fifo_pos += xfer->len;
306bbb336f3SSanjay R Mehta }
307bbb336f3SSanjay R Mehta
3089d08f700SCristian Ciocaltea /* Store no. of bytes to be received from FIFO */
3099d08f700SCristian Ciocaltea if (xfer->rx_buf)
3109d08f700SCristian Ciocaltea rx_len += xfer->len;
311bbb336f3SSanjay R Mehta }
3129d08f700SCristian Ciocaltea
3139d08f700SCristian Ciocaltea if (!buf) {
3149d08f700SCristian Ciocaltea message->status = -EINVAL;
3159d08f700SCristian Ciocaltea goto fin_msg;
3169d08f700SCristian Ciocaltea }
3179d08f700SCristian Ciocaltea
3189d08f700SCristian Ciocaltea amd_spi_set_opcode(amd_spi, cmd_opcode);
3199d08f700SCristian Ciocaltea amd_spi_set_tx_count(amd_spi, tx_len);
320ca8e8a18SLucas Tanure amd_spi_set_rx_count(amd_spi, rx_len);
3219d08f700SCristian Ciocaltea
322bbb336f3SSanjay R Mehta /* Execute command */
3239d08f700SCristian Ciocaltea message->status = amd_spi_execute_opcode(amd_spi);
3249d08f700SCristian Ciocaltea if (message->status)
3259d08f700SCristian Ciocaltea goto fin_msg;
3269d08f700SCristian Ciocaltea
3279d08f700SCristian Ciocaltea if (rx_len) {
3289d08f700SCristian Ciocaltea message->status = amd_spi_busy_wait(amd_spi);
3299d08f700SCristian Ciocaltea if (message->status)
3309d08f700SCristian Ciocaltea goto fin_msg;
3319d08f700SCristian Ciocaltea
3329d08f700SCristian Ciocaltea list_for_each_entry(xfer, &message->transfers, transfer_list)
3339d08f700SCristian Ciocaltea if (xfer->rx_buf) {
3349d08f700SCristian Ciocaltea buf = (u8 *)xfer->rx_buf;
335bbb336f3SSanjay R Mehta /* Read data from FIFO to receive buffer */
3369d08f700SCristian Ciocaltea for (i = 0; i < xfer->len; i++)
3379d08f700SCristian Ciocaltea buf[i] = amd_spi_readreg8(amd_spi, fifo_pos + i);
3389d08f700SCristian Ciocaltea fifo_pos += xfer->len;
339bbb336f3SSanjay R Mehta }
340bbb336f3SSanjay R Mehta }
341bbb336f3SSanjay R Mehta
342bbb336f3SSanjay R Mehta /* Update statistics */
343bbb336f3SSanjay R Mehta message->actual_length = tx_len + rx_len + 1;
34420904355SAndré Almeida
3459d08f700SCristian Ciocaltea fin_msg:
34620904355SAndré Almeida switch (amd_spi->version) {
34720904355SAndré Almeida case AMD_SPI_V1:
34820904355SAndré Almeida break;
34920904355SAndré Almeida case AMD_SPI_V2:
3509e264f3fSAmit Kumar Mahapatra via Alsa-devel amd_spi_clear_chip(amd_spi, spi_get_chipselect(message->spi, 0));
35120904355SAndré Almeida break;
35220904355SAndré Almeida default:
35320904355SAndré Almeida return -ENODEV;
35420904355SAndré Almeida }
35520904355SAndré Almeida
356e9759d40SYang Yingliang spi_finalize_current_message(host);
357bbb336f3SSanjay R Mehta
3589d08f700SCristian Ciocaltea return message->status;
359bbb336f3SSanjay R Mehta }
360bbb336f3SSanjay R Mehta
amd_spi_host_transfer(struct spi_controller * host,struct spi_message * msg)361e9759d40SYang Yingliang static int amd_spi_host_transfer(struct spi_controller *host,
362bbb336f3SSanjay R Mehta struct spi_message *msg)
363bbb336f3SSanjay R Mehta {
364e9759d40SYang Yingliang struct amd_spi *amd_spi = spi_controller_get_devdata(host);
365bbb336f3SSanjay R Mehta struct spi_device *spi = msg->spi;
366bbb336f3SSanjay R Mehta
3679e264f3fSAmit Kumar Mahapatra via Alsa-devel amd_spi_select_chip(amd_spi, spi_get_chipselect(spi, 0));
368bbb336f3SSanjay R Mehta
369bbb336f3SSanjay R Mehta /*
370bbb336f3SSanjay R Mehta * Extract spi_transfers from the spi message and
371bbb336f3SSanjay R Mehta * program the controller.
372bbb336f3SSanjay R Mehta */
373e9759d40SYang Yingliang return amd_spi_fifo_xfer(amd_spi, host, msg);
374bbb336f3SSanjay R Mehta }
375bbb336f3SSanjay R Mehta
amd_spi_max_transfer_size(struct spi_device * spi)3766ece49c5SCristian Ciocaltea static size_t amd_spi_max_transfer_size(struct spi_device *spi)
3776ece49c5SCristian Ciocaltea {
3786ece49c5SCristian Ciocaltea return AMD_SPI_FIFO_SIZE;
3796ece49c5SCristian Ciocaltea }
3806ece49c5SCristian Ciocaltea
amd_spi_probe(struct platform_device * pdev)381bbb336f3SSanjay R Mehta static int amd_spi_probe(struct platform_device *pdev)
382bbb336f3SSanjay R Mehta {
383bbb336f3SSanjay R Mehta struct device *dev = &pdev->dev;
384e9759d40SYang Yingliang struct spi_controller *host;
385bbb336f3SSanjay R Mehta struct amd_spi *amd_spi;
3862e063bb1SCristian Ciocaltea int err;
387bbb336f3SSanjay R Mehta
388e9759d40SYang Yingliang /* Allocate storage for host and driver private data */
389e9759d40SYang Yingliang host = devm_spi_alloc_host(dev, sizeof(struct amd_spi));
390e9759d40SYang Yingliang if (!host)
391e9759d40SYang Yingliang return dev_err_probe(dev, -ENOMEM, "Error allocating SPI host\n");
392bbb336f3SSanjay R Mehta
393e9759d40SYang Yingliang amd_spi = spi_controller_get_devdata(host);
3942ed6e3baSQing Zhang amd_spi->io_remap_addr = devm_platform_ioremap_resource(pdev, 0);
395deef4da8SCristian Ciocaltea if (IS_ERR(amd_spi->io_remap_addr))
396deef4da8SCristian Ciocaltea return dev_err_probe(dev, PTR_ERR(amd_spi->io_remap_addr),
397deef4da8SCristian Ciocaltea "ioremap of SPI registers failed\n");
398deef4da8SCristian Ciocaltea
399bbb336f3SSanjay R Mehta dev_dbg(dev, "io_remap_address: %p\n", amd_spi->io_remap_addr);
400bbb336f3SSanjay R Mehta
401*675b8e35SKrzysztof Kozlowski amd_spi->version = (uintptr_t) device_get_match_data(dev);
40220904355SAndré Almeida
403e9759d40SYang Yingliang /* Initialize the spi_controller fields */
404e9759d40SYang Yingliang host->bus_num = 0;
405e9759d40SYang Yingliang host->num_chipselect = 4;
406e9759d40SYang Yingliang host->mode_bits = 0;
407e9759d40SYang Yingliang host->flags = SPI_CONTROLLER_HALF_DUPLEX;
408e9759d40SYang Yingliang host->max_speed_hz = AMD_SPI_MAX_HZ;
409e9759d40SYang Yingliang host->min_speed_hz = AMD_SPI_MIN_HZ;
410e9759d40SYang Yingliang host->setup = amd_spi_host_setup;
411e9759d40SYang Yingliang host->transfer_one_message = amd_spi_host_transfer;
412e9759d40SYang Yingliang host->max_transfer_size = amd_spi_max_transfer_size;
413e9759d40SYang Yingliang host->max_message_size = amd_spi_max_transfer_size;
414bbb336f3SSanjay R Mehta
415bbb336f3SSanjay R Mehta /* Register the controller with SPI framework */
416e9759d40SYang Yingliang err = devm_spi_register_controller(dev, host);
4172e063bb1SCristian Ciocaltea if (err)
418deef4da8SCristian Ciocaltea return dev_err_probe(dev, err, "error registering SPI controller\n");
419bbb336f3SSanjay R Mehta
420deef4da8SCristian Ciocaltea return 0;
421bbb336f3SSanjay R Mehta }
422bbb336f3SSanjay R Mehta
42385ed0f63SLee Jones #ifdef CONFIG_ACPI
424bbb336f3SSanjay R Mehta static const struct acpi_device_id spi_acpi_match[] = {
42520904355SAndré Almeida { "AMDI0061", AMD_SPI_V1 },
42620904355SAndré Almeida { "AMDI0062", AMD_SPI_V2 },
427bbb336f3SSanjay R Mehta {},
428bbb336f3SSanjay R Mehta };
429bbb336f3SSanjay R Mehta MODULE_DEVICE_TABLE(acpi, spi_acpi_match);
43085ed0f63SLee Jones #endif
431bbb336f3SSanjay R Mehta
432bbb336f3SSanjay R Mehta static struct platform_driver amd_spi_driver = {
433bbb336f3SSanjay R Mehta .driver = {
434bbb336f3SSanjay R Mehta .name = "amd_spi",
4352b993ab7SAndré Almeida .acpi_match_table = ACPI_PTR(spi_acpi_match),
436bbb336f3SSanjay R Mehta },
437bbb336f3SSanjay R Mehta .probe = amd_spi_probe,
438bbb336f3SSanjay R Mehta };
439bbb336f3SSanjay R Mehta
440bbb336f3SSanjay R Mehta module_platform_driver(amd_spi_driver);
441bbb336f3SSanjay R Mehta
442bbb336f3SSanjay R Mehta MODULE_LICENSE("Dual BSD/GPL");
443bbb336f3SSanjay R Mehta MODULE_AUTHOR("Sanjay Mehta <sanju.mehta@amd.com>");
444bbb336f3SSanjay R Mehta MODULE_DESCRIPTION("AMD SPI Master Controller Driver");
445