xref: /rk3399_rockchip-uboot/drivers/spi/altera_spi.c (revision a821c4af79e4f5ce9b629b20473863397bbe9b10)
1661ba140SThomas Chou /*
2661ba140SThomas Chou  * Altera SPI driver
3661ba140SThomas Chou  *
4661ba140SThomas Chou  * based on bfin_spi.c
5661ba140SThomas Chou  * Copyright (c) 2005-2008 Analog Devices Inc.
6661ba140SThomas Chou  * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
7661ba140SThomas Chou  *
8e7b1e452SJagannadha Sutradharudu Teki  * SPDX-License-Identifier:	GPL-2.0+
9661ba140SThomas Chou  */
10661ba140SThomas Chou #include <common.h>
1115a56f9cSThomas Chou #include <dm.h>
1215a56f9cSThomas Chou #include <errno.h>
13661ba140SThomas Chou #include <malloc.h>
1415a56f9cSThomas Chou #include <fdtdec.h>
15bef87adfSJagan Teki #include <spi.h>
1615a56f9cSThomas Chou #include <asm/io.h>
1715a56f9cSThomas Chou 
1815a56f9cSThomas Chou DECLARE_GLOBAL_DATA_PTR;
19661ba140SThomas Chou 
20bef87adfSJagan Teki #define ALTERA_SPI_STATUS_RRDY_MSK	BIT(7)
21bef87adfSJagan Teki #define ALTERA_SPI_CONTROL_SSO_MSK	BIT(10)
22bef87adfSJagan Teki 
23cdcdad85SMarek Vasut #ifndef CONFIG_ALTERA_SPI_IDLE_VAL
24cdcdad85SMarek Vasut #define CONFIG_ALTERA_SPI_IDLE_VAL	0xff
25cdcdad85SMarek Vasut #endif
26cdcdad85SMarek Vasut 
27eef67029SMarek Vasut struct altera_spi_regs {
28eef67029SMarek Vasut 	u32	rxdata;
29eef67029SMarek Vasut 	u32	txdata;
30eef67029SMarek Vasut 	u32	status;
31eef67029SMarek Vasut 	u32	control;
32eef67029SMarek Vasut 	u32	_reserved;
33eef67029SMarek Vasut 	u32	slave_sel;
34eef67029SMarek Vasut };
35661ba140SThomas Chou 
3615a56f9cSThomas Chou struct altera_spi_platdata {
37eef67029SMarek Vasut 	struct altera_spi_regs *regs;
38661ba140SThomas Chou };
39661ba140SThomas Chou 
4015a56f9cSThomas Chou struct altera_spi_priv {
4115a56f9cSThomas Chou 	struct altera_spi_regs *regs;
4215a56f9cSThomas Chou };
4315a56f9cSThomas Chou 
spi_cs_activate(struct udevice * dev,uint cs)4415a56f9cSThomas Chou static void spi_cs_activate(struct udevice *dev, uint cs)
45661ba140SThomas Chou {
4615a56f9cSThomas Chou 	struct udevice *bus = dev->parent;
4715a56f9cSThomas Chou 	struct altera_spi_priv *priv = dev_get_priv(bus);
4815a56f9cSThomas Chou 	struct altera_spi_regs *const regs = priv->regs;
4915a56f9cSThomas Chou 
5015a56f9cSThomas Chou 	writel(1 << cs, &regs->slave_sel);
5115a56f9cSThomas Chou 	writel(ALTERA_SPI_CONTROL_SSO_MSK, &regs->control);
52661ba140SThomas Chou }
53661ba140SThomas Chou 
spi_cs_deactivate(struct udevice * dev)5415a56f9cSThomas Chou static void spi_cs_deactivate(struct udevice *dev)
55661ba140SThomas Chou {
5615a56f9cSThomas Chou 	struct udevice *bus = dev->parent;
5715a56f9cSThomas Chou 	struct altera_spi_priv *priv = dev_get_priv(bus);
5815a56f9cSThomas Chou 	struct altera_spi_regs *const regs = priv->regs;
5915a56f9cSThomas Chou 
6015a56f9cSThomas Chou 	writel(0, &regs->control);
6115a56f9cSThomas Chou 	writel(0, &regs->slave_sel);
62661ba140SThomas Chou }
63661ba140SThomas Chou 
altera_spi_claim_bus(struct udevice * dev)6415a56f9cSThomas Chou static int altera_spi_claim_bus(struct udevice *dev)
65661ba140SThomas Chou {
6615a56f9cSThomas Chou 	struct udevice *bus = dev->parent;
6715a56f9cSThomas Chou 	struct altera_spi_priv *priv = dev_get_priv(bus);
6815a56f9cSThomas Chou 	struct altera_spi_regs *const regs = priv->regs;
69661ba140SThomas Chou 
7015a56f9cSThomas Chou 	writel(0, &regs->control);
7115a56f9cSThomas Chou 	writel(0, &regs->slave_sel);
72661ba140SThomas Chou 
73661ba140SThomas Chou 	return 0;
74661ba140SThomas Chou }
75661ba140SThomas Chou 
altera_spi_release_bus(struct udevice * dev)7615a56f9cSThomas Chou static int altera_spi_release_bus(struct udevice *dev)
77661ba140SThomas Chou {
7815a56f9cSThomas Chou 	struct udevice *bus = dev->parent;
7915a56f9cSThomas Chou 	struct altera_spi_priv *priv = dev_get_priv(bus);
8015a56f9cSThomas Chou 	struct altera_spi_regs *const regs = priv->regs;
81661ba140SThomas Chou 
8215a56f9cSThomas Chou 	writel(0, &regs->slave_sel);
8315a56f9cSThomas Chou 
8415a56f9cSThomas Chou 	return 0;
85661ba140SThomas Chou }
86661ba140SThomas Chou 
altera_spi_xfer(struct udevice * dev,unsigned int bitlen,const void * dout,void * din,unsigned long flags)8715a56f9cSThomas Chou static int altera_spi_xfer(struct udevice *dev, unsigned int bitlen,
8815a56f9cSThomas Chou 			    const void *dout, void *din, unsigned long flags)
89661ba140SThomas Chou {
9015a56f9cSThomas Chou 	struct udevice *bus = dev->parent;
9115a56f9cSThomas Chou 	struct altera_spi_priv *priv = dev_get_priv(bus);
9215a56f9cSThomas Chou 	struct altera_spi_regs *const regs = priv->regs;
9315a56f9cSThomas Chou 	struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
9415a56f9cSThomas Chou 
95661ba140SThomas Chou 	/* assume spi core configured to do 8 bit transfers */
96bc76b821SMarek Vasut 	unsigned int bytes = bitlen / 8;
97bc76b821SMarek Vasut 	const unsigned char *txp = dout;
98bc76b821SMarek Vasut 	unsigned char *rxp = din;
99bc76b821SMarek Vasut 	uint32_t reg, data, start;
100661ba140SThomas Chou 
101661ba140SThomas Chou 	debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__,
10215a56f9cSThomas Chou 	      bus->seq, slave_plat->cs, bitlen, bytes, flags);
10337dcc130SMarek Vasut 
104661ba140SThomas Chou 	if (bitlen == 0)
105661ba140SThomas Chou 		goto done;
106661ba140SThomas Chou 
107661ba140SThomas Chou 	if (bitlen % 8) {
108661ba140SThomas Chou 		flags |= SPI_XFER_END;
109661ba140SThomas Chou 		goto done;
110661ba140SThomas Chou 	}
111661ba140SThomas Chou 
112661ba140SThomas Chou 	/* empty read buffer */
11315a56f9cSThomas Chou 	if (readl(&regs->status) & ALTERA_SPI_STATUS_RRDY_MSK)
11415a56f9cSThomas Chou 		readl(&regs->rxdata);
11537dcc130SMarek Vasut 
116661ba140SThomas Chou 	if (flags & SPI_XFER_BEGIN)
11715a56f9cSThomas Chou 		spi_cs_activate(dev, slave_plat->cs);
118661ba140SThomas Chou 
119661ba140SThomas Chou 	while (bytes--) {
120bc76b821SMarek Vasut 		if (txp)
121bc76b821SMarek Vasut 			data = *txp++;
122bc76b821SMarek Vasut 		else
123bc76b821SMarek Vasut 			data = CONFIG_ALTERA_SPI_IDLE_VAL;
12437dcc130SMarek Vasut 
125bc76b821SMarek Vasut 		debug("%s: tx:%x ", __func__, data);
12615a56f9cSThomas Chou 		writel(data, &regs->txdata);
12737dcc130SMarek Vasut 
12880d73338SMarek Vasut 		start = get_timer(0);
12980d73338SMarek Vasut 		while (1) {
13015a56f9cSThomas Chou 			reg = readl(&regs->status);
13180d73338SMarek Vasut 			if (reg & ALTERA_SPI_STATUS_RRDY_MSK)
13280d73338SMarek Vasut 				break;
13380d73338SMarek Vasut 			if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) {
13415a56f9cSThomas Chou 				debug("%s: Transmission timed out!\n", __func__);
13515a56f9cSThomas Chou 				return -1;
13680d73338SMarek Vasut 			}
13780d73338SMarek Vasut 		}
13837dcc130SMarek Vasut 
13915a56f9cSThomas Chou 		data = readl(&regs->rxdata);
140661ba140SThomas Chou 		if (rxp)
141bc76b821SMarek Vasut 			*rxp++ = data & 0xff;
14237dcc130SMarek Vasut 
143bc76b821SMarek Vasut 		debug("rx:%x\n", data);
144661ba140SThomas Chou 	}
14537dcc130SMarek Vasut 
146661ba140SThomas Chou done:
147661ba140SThomas Chou 	if (flags & SPI_XFER_END)
14815a56f9cSThomas Chou 		spi_cs_deactivate(dev);
149661ba140SThomas Chou 
150661ba140SThomas Chou 	return 0;
151661ba140SThomas Chou }
15215a56f9cSThomas Chou 
altera_spi_set_speed(struct udevice * bus,uint speed)15315a56f9cSThomas Chou static int altera_spi_set_speed(struct udevice *bus, uint speed)
15415a56f9cSThomas Chou {
15515a56f9cSThomas Chou 	return 0;
15615a56f9cSThomas Chou }
15715a56f9cSThomas Chou 
altera_spi_set_mode(struct udevice * bus,uint mode)15815a56f9cSThomas Chou static int altera_spi_set_mode(struct udevice *bus, uint mode)
15915a56f9cSThomas Chou {
16015a56f9cSThomas Chou 	return 0;
16115a56f9cSThomas Chou }
16215a56f9cSThomas Chou 
altera_spi_probe(struct udevice * bus)16315a56f9cSThomas Chou static int altera_spi_probe(struct udevice *bus)
16415a56f9cSThomas Chou {
16515a56f9cSThomas Chou 	struct altera_spi_platdata *plat = dev_get_platdata(bus);
16615a56f9cSThomas Chou 	struct altera_spi_priv *priv = dev_get_priv(bus);
16715a56f9cSThomas Chou 
16815a56f9cSThomas Chou 	priv->regs = plat->regs;
16915a56f9cSThomas Chou 
17015a56f9cSThomas Chou 	return 0;
17115a56f9cSThomas Chou }
17215a56f9cSThomas Chou 
altera_spi_ofdata_to_platdata(struct udevice * bus)17315a56f9cSThomas Chou static int altera_spi_ofdata_to_platdata(struct udevice *bus)
17415a56f9cSThomas Chou {
17515a56f9cSThomas Chou 	struct altera_spi_platdata *plat = dev_get_platdata(bus);
17615a56f9cSThomas Chou 
177*a821c4afSSimon Glass 	plat->regs = map_physmem(devfdt_get_addr(bus),
1787313e21aSThomas Chou 				 sizeof(struct altera_spi_regs),
1797313e21aSThomas Chou 				 MAP_NOCACHE);
18015a56f9cSThomas Chou 
18115a56f9cSThomas Chou 	return 0;
18215a56f9cSThomas Chou }
18315a56f9cSThomas Chou 
18415a56f9cSThomas Chou static const struct dm_spi_ops altera_spi_ops = {
18515a56f9cSThomas Chou 	.claim_bus	= altera_spi_claim_bus,
18615a56f9cSThomas Chou 	.release_bus	= altera_spi_release_bus,
18715a56f9cSThomas Chou 	.xfer		= altera_spi_xfer,
18815a56f9cSThomas Chou 	.set_speed	= altera_spi_set_speed,
18915a56f9cSThomas Chou 	.set_mode	= altera_spi_set_mode,
19015a56f9cSThomas Chou 	/*
19115a56f9cSThomas Chou 	 * cs_info is not needed, since we require all chip selects to be
19215a56f9cSThomas Chou 	 * in the device tree explicitly
19315a56f9cSThomas Chou 	 */
19415a56f9cSThomas Chou };
19515a56f9cSThomas Chou 
19615a56f9cSThomas Chou static const struct udevice_id altera_spi_ids[] = {
197ddf34c26SThomas Chou 	{ .compatible = "altr,spi-1.0" },
19815a56f9cSThomas Chou 	{}
19915a56f9cSThomas Chou };
20015a56f9cSThomas Chou 
20115a56f9cSThomas Chou U_BOOT_DRIVER(altera_spi) = {
20215a56f9cSThomas Chou 	.name	= "altera_spi",
20315a56f9cSThomas Chou 	.id	= UCLASS_SPI,
20415a56f9cSThomas Chou 	.of_match = altera_spi_ids,
20515a56f9cSThomas Chou 	.ops	= &altera_spi_ops,
20615a56f9cSThomas Chou 	.ofdata_to_platdata = altera_spi_ofdata_to_platdata,
20715a56f9cSThomas Chou 	.platdata_auto_alloc_size = sizeof(struct altera_spi_platdata),
20815a56f9cSThomas Chou 	.priv_auto_alloc_size = sizeof(struct altera_spi_priv),
20915a56f9cSThomas Chou 	.probe	= altera_spi_probe,
21015a56f9cSThomas Chou };
211