xref: /openbmc/linux/drivers/spi/spi-amd.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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