xref: /rk3399_rockchip-uboot/drivers/spi/mxc_spi.c (revision d255bb0e78d1cac5b7c8c98cb77a095f5f16de0d)
138254f45SGuennadi Liakhovetski /*
238254f45SGuennadi Liakhovetski  * Copyright (C) 2008, Guennadi Liakhovetski <lg@denx.de>
338254f45SGuennadi Liakhovetski  *
438254f45SGuennadi Liakhovetski  * This program is free software; you can redistribute it and/or
538254f45SGuennadi Liakhovetski  * modify it under the terms of the GNU General Public License as
638254f45SGuennadi Liakhovetski  * published by the Free Software Foundation; either version 2 of
738254f45SGuennadi Liakhovetski  * the License, or (at your option) any later version.
838254f45SGuennadi Liakhovetski  *
938254f45SGuennadi Liakhovetski  * This program is distributed in the hope that it will be useful,
1038254f45SGuennadi Liakhovetski  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1138254f45SGuennadi Liakhovetski  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1238254f45SGuennadi Liakhovetski  * GNU General Public License for more details.
1338254f45SGuennadi Liakhovetski  *
1438254f45SGuennadi Liakhovetski  * You should have received a copy of the GNU General Public License
1538254f45SGuennadi Liakhovetski  * along with this program; if not, write to the Free Software
1638254f45SGuennadi Liakhovetski  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
1738254f45SGuennadi Liakhovetski  * MA 02111-1307 USA
1838254f45SGuennadi Liakhovetski  *
1938254f45SGuennadi Liakhovetski  */
2038254f45SGuennadi Liakhovetski 
2138254f45SGuennadi Liakhovetski #include <common.h>
22*d255bb0eSHaavard Skinnemoen #include <malloc.h>
2338254f45SGuennadi Liakhovetski #include <spi.h>
2438254f45SGuennadi Liakhovetski #include <asm/io.h>
2538254f45SGuennadi Liakhovetski 
2638254f45SGuennadi Liakhovetski #ifdef CONFIG_MX27
2738254f45SGuennadi Liakhovetski /* i.MX27 has a completely wrong register layout and register definitions in the
2838254f45SGuennadi Liakhovetski  * datasheet, the correct one is in the Freescale's Linux driver */
2938254f45SGuennadi Liakhovetski 
3038254f45SGuennadi Liakhovetski #error "i.MX27 CSPI not supported due to drastic differences in register definisions" \
3138254f45SGuennadi Liakhovetski "See linux mxc_spi driver from Freescale for details."
3238254f45SGuennadi Liakhovetski 
3338254f45SGuennadi Liakhovetski #else
3438254f45SGuennadi Liakhovetski 
3538254f45SGuennadi Liakhovetski #define MXC_CSPIRXDATA		0x00
3638254f45SGuennadi Liakhovetski #define MXC_CSPITXDATA		0x04
3738254f45SGuennadi Liakhovetski #define MXC_CSPICTRL		0x08
3838254f45SGuennadi Liakhovetski #define MXC_CSPIINT		0x0C
3938254f45SGuennadi Liakhovetski #define MXC_CSPIDMA		0x10
4038254f45SGuennadi Liakhovetski #define MXC_CSPISTAT		0x14
4138254f45SGuennadi Liakhovetski #define MXC_CSPIPERIOD		0x18
4238254f45SGuennadi Liakhovetski #define MXC_CSPITEST		0x1C
4338254f45SGuennadi Liakhovetski #define MXC_CSPIRESET		0x00
4438254f45SGuennadi Liakhovetski 
4538254f45SGuennadi Liakhovetski #define MXC_CSPICTRL_EN		(1 << 0)
4638254f45SGuennadi Liakhovetski #define MXC_CSPICTRL_MODE	(1 << 1)
4738254f45SGuennadi Liakhovetski #define MXC_CSPICTRL_XCH	(1 << 2)
4838254f45SGuennadi Liakhovetski #define MXC_CSPICTRL_SMC	(1 << 3)
4938254f45SGuennadi Liakhovetski #define MXC_CSPICTRL_POL	(1 << 4)
5038254f45SGuennadi Liakhovetski #define MXC_CSPICTRL_PHA	(1 << 5)
5138254f45SGuennadi Liakhovetski #define MXC_CSPICTRL_SSCTL	(1 << 6)
5238254f45SGuennadi Liakhovetski #define MXC_CSPICTRL_SSPOL	(1 << 7)
5338254f45SGuennadi Liakhovetski #define MXC_CSPICTRL_CHIPSELECT(x)	(((x) & 0x3) << 24)
5438254f45SGuennadi Liakhovetski #define MXC_CSPICTRL_BITCOUNT(x)	(((x) & 0x1f) << 8)
5538254f45SGuennadi Liakhovetski #define MXC_CSPICTRL_DATARATE(x)	(((x) & 0x7) << 16)
5638254f45SGuennadi Liakhovetski 
5738254f45SGuennadi Liakhovetski #define MXC_CSPIPERIOD_32KHZ	(1 << 15)
5838254f45SGuennadi Liakhovetski 
5938254f45SGuennadi Liakhovetski static unsigned long spi_bases[] = {
6038254f45SGuennadi Liakhovetski 	0x43fa4000,
6138254f45SGuennadi Liakhovetski 	0x50010000,
6238254f45SGuennadi Liakhovetski 	0x53f84000,
6338254f45SGuennadi Liakhovetski };
6438254f45SGuennadi Liakhovetski 
6538254f45SGuennadi Liakhovetski #endif
6638254f45SGuennadi Liakhovetski 
67*d255bb0eSHaavard Skinnemoen struct mxc_spi_slave {
68*d255bb0eSHaavard Skinnemoen 	struct spi_slave slave;
69*d255bb0eSHaavard Skinnemoen 	unsigned long	base;
70*d255bb0eSHaavard Skinnemoen 	u32		ctrl_reg;
7138254f45SGuennadi Liakhovetski };
72*d255bb0eSHaavard Skinnemoen 
73*d255bb0eSHaavard Skinnemoen static inline struct mxc_spi_slave *to_mxc_spi_slave(struct spi_slave *slave)
74*d255bb0eSHaavard Skinnemoen {
75*d255bb0eSHaavard Skinnemoen 	return container_of(slave, struct mxc_spi_slave, slave);
76*d255bb0eSHaavard Skinnemoen }
7738254f45SGuennadi Liakhovetski 
7838254f45SGuennadi Liakhovetski static inline u32 reg_read(unsigned long addr)
7938254f45SGuennadi Liakhovetski {
8038254f45SGuennadi Liakhovetski 	return *(volatile unsigned long*)addr;
8138254f45SGuennadi Liakhovetski }
8238254f45SGuennadi Liakhovetski 
8338254f45SGuennadi Liakhovetski static inline void reg_write(unsigned long addr, u32 val)
8438254f45SGuennadi Liakhovetski {
8538254f45SGuennadi Liakhovetski 	*(volatile unsigned long*)addr = val;
8638254f45SGuennadi Liakhovetski }
8738254f45SGuennadi Liakhovetski 
88*d255bb0eSHaavard Skinnemoen static u32 spi_xchg_single(struct spi_slave *slave, u32 data, int bitlen)
8938254f45SGuennadi Liakhovetski {
90*d255bb0eSHaavard Skinnemoen 	struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave);
91*d255bb0eSHaavard Skinnemoen 	unsigned int cfg_reg = reg_read(mxcs->base + MXC_CSPICTRL);
9238254f45SGuennadi Liakhovetski 
9338254f45SGuennadi Liakhovetski 	if (MXC_CSPICTRL_BITCOUNT(bitlen - 1) != (cfg_reg & MXC_CSPICTRL_BITCOUNT(31))) {
9438254f45SGuennadi Liakhovetski 		cfg_reg = (cfg_reg & ~MXC_CSPICTRL_BITCOUNT(31)) |
9538254f45SGuennadi Liakhovetski 			MXC_CSPICTRL_BITCOUNT(bitlen - 1);
96*d255bb0eSHaavard Skinnemoen 		reg_write(mxcs->base + MXC_CSPICTRL, cfg_reg);
9738254f45SGuennadi Liakhovetski 	}
9838254f45SGuennadi Liakhovetski 
99*d255bb0eSHaavard Skinnemoen 	reg_write(mxcs->base + MXC_CSPITXDATA, data);
10038254f45SGuennadi Liakhovetski 
10138254f45SGuennadi Liakhovetski 	cfg_reg |= MXC_CSPICTRL_XCH;
10238254f45SGuennadi Liakhovetski 
103*d255bb0eSHaavard Skinnemoen 	reg_write(mxcs->base + MXC_CSPICTRL, cfg_reg);
10438254f45SGuennadi Liakhovetski 
105*d255bb0eSHaavard Skinnemoen 	while (reg_read(mxcs->base + MXC_CSPICTRL) & MXC_CSPICTRL_XCH)
10638254f45SGuennadi Liakhovetski 		;
10738254f45SGuennadi Liakhovetski 
108*d255bb0eSHaavard Skinnemoen 	return reg_read(mxcs->base + MXC_CSPIRXDATA);
10938254f45SGuennadi Liakhovetski }
11038254f45SGuennadi Liakhovetski 
111*d255bb0eSHaavard Skinnemoen int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
112*d255bb0eSHaavard Skinnemoen 		void *din, unsigned long flags)
11338254f45SGuennadi Liakhovetski {
11438254f45SGuennadi Liakhovetski 	int n_blks = (bitlen + 31) / 32;
11538254f45SGuennadi Liakhovetski 	u32 *out_l, *in_l;
11638254f45SGuennadi Liakhovetski 	int i;
11738254f45SGuennadi Liakhovetski 
11838254f45SGuennadi Liakhovetski 	if ((int)dout & 3 || (int)din & 3) {
11938254f45SGuennadi Liakhovetski 		printf("Error: unaligned buffers in: %p, out: %p\n", din, dout);
12038254f45SGuennadi Liakhovetski 		return 1;
12138254f45SGuennadi Liakhovetski 	}
12238254f45SGuennadi Liakhovetski 
12338254f45SGuennadi Liakhovetski 	for (i = 0, in_l = (u32 *)din, out_l = (u32 *)dout;
12438254f45SGuennadi Liakhovetski 	     i < n_blks;
12538254f45SGuennadi Liakhovetski 	     i++, in_l++, out_l++, bitlen -= 32)
126*d255bb0eSHaavard Skinnemoen 		*in_l = spi_xchg_single(slave, *out_l, bitlen);
12738254f45SGuennadi Liakhovetski 
12838254f45SGuennadi Liakhovetski 	return 0;
12938254f45SGuennadi Liakhovetski }
13038254f45SGuennadi Liakhovetski 
13138254f45SGuennadi Liakhovetski void spi_init(void)
13238254f45SGuennadi Liakhovetski {
13338254f45SGuennadi Liakhovetski }
13438254f45SGuennadi Liakhovetski 
135*d255bb0eSHaavard Skinnemoen struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
136*d255bb0eSHaavard Skinnemoen 			unsigned int max_hz, unsigned int mode)
13738254f45SGuennadi Liakhovetski {
13838254f45SGuennadi Liakhovetski 	unsigned int ctrl_reg;
139*d255bb0eSHaavard Skinnemoen 	struct mxc_spi_slave *mxcs;
14038254f45SGuennadi Liakhovetski 
14138254f45SGuennadi Liakhovetski 	if (bus >= sizeof(spi_bases) / sizeof(spi_bases[0]) ||
142*d255bb0eSHaavard Skinnemoen 	    cs > 3)
143*d255bb0eSHaavard Skinnemoen 		return NULL;
14438254f45SGuennadi Liakhovetski 
145*d255bb0eSHaavard Skinnemoen 	ctrl_reg = MXC_CSPICTRL_CHIPSELECT(cs) |
14638254f45SGuennadi Liakhovetski 		MXC_CSPICTRL_BITCOUNT(31) |
14738254f45SGuennadi Liakhovetski 		MXC_CSPICTRL_DATARATE(7) | /* FIXME: calculate data rate */
14838254f45SGuennadi Liakhovetski 		MXC_CSPICTRL_EN |
14938254f45SGuennadi Liakhovetski 		MXC_CSPICTRL_MODE;
15038254f45SGuennadi Liakhovetski 
15138254f45SGuennadi Liakhovetski 	if (mode & SPI_CPHA)
15238254f45SGuennadi Liakhovetski 		ctrl_reg |= MXC_CSPICTRL_PHA;
15338254f45SGuennadi Liakhovetski 	if (!(mode & SPI_CPOL))
15438254f45SGuennadi Liakhovetski 		ctrl_reg |= MXC_CSPICTRL_POL;
15538254f45SGuennadi Liakhovetski 	if (mode & SPI_CS_HIGH)
15638254f45SGuennadi Liakhovetski 		ctrl_reg |= MXC_CSPICTRL_SSPOL;
15738254f45SGuennadi Liakhovetski 
158*d255bb0eSHaavard Skinnemoen 	mxcs = malloc(sizeof(struct mxc_spi_slave));
159*d255bb0eSHaavard Skinnemoen 	if (!mxcs)
160*d255bb0eSHaavard Skinnemoen 		return NULL;
161*d255bb0eSHaavard Skinnemoen 
162*d255bb0eSHaavard Skinnemoen 	mxcs->slave.bus = bus;
163*d255bb0eSHaavard Skinnemoen 	mxcs->slave.cs = cs;
164*d255bb0eSHaavard Skinnemoen 	mxcs->base = spi_bases[bus];
165*d255bb0eSHaavard Skinnemoen 	mxcs->ctrl_reg = ctrl_reg;
166*d255bb0eSHaavard Skinnemoen 
167*d255bb0eSHaavard Skinnemoen 	return &mxcs->slave;
168*d255bb0eSHaavard Skinnemoen }
169*d255bb0eSHaavard Skinnemoen 
170*d255bb0eSHaavard Skinnemoen void spi_free_slave(struct spi_slave *slave)
171*d255bb0eSHaavard Skinnemoen {
172*d255bb0eSHaavard Skinnemoen 	free(slave);
173*d255bb0eSHaavard Skinnemoen }
174*d255bb0eSHaavard Skinnemoen 
175*d255bb0eSHaavard Skinnemoen int spi_claim_bus(struct spi_slave *slave)
176*d255bb0eSHaavard Skinnemoen {
177*d255bb0eSHaavard Skinnemoen 	struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave);
178*d255bb0eSHaavard Skinnemoen 
179*d255bb0eSHaavard Skinnemoen 	reg_write(mxcs->base + MXC_CSPIRESET, 1);
18038254f45SGuennadi Liakhovetski 	udelay(1);
181*d255bb0eSHaavard Skinnemoen 	reg_write(mxcs->base + MXC_CSPICTRL, mxcs->ctrl_reg);
182*d255bb0eSHaavard Skinnemoen 	reg_write(mxcs->base + MXC_CSPIPERIOD,
18338254f45SGuennadi Liakhovetski 		  MXC_CSPIPERIOD_32KHZ);
184*d255bb0eSHaavard Skinnemoen 	reg_write(mxcs->base + MXC_CSPIINT, 0);
18538254f45SGuennadi Liakhovetski 
18638254f45SGuennadi Liakhovetski 	return 0;
18738254f45SGuennadi Liakhovetski }
188*d255bb0eSHaavard Skinnemoen 
189*d255bb0eSHaavard Skinnemoen void spi_release_bus(struct spi_slave *slave)
190*d255bb0eSHaavard Skinnemoen {
191*d255bb0eSHaavard Skinnemoen 	/* TODO: Shut the controller down */
192*d255bb0eSHaavard Skinnemoen }
193