sunxi_rsb.c 3.19 KB
Newer Older
Andre Przywara's avatar
Andre Przywara committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * Copyright (c) 2017-2018 ARM Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <debug.h>
#include <delay_timer.h>
#include <errno.h>
#include <mmio.h>
#include <sunxi_mmap.h>

#define RSB_CTRL	0x00
#define RSB_CCR		0x04
#define RSB_INTE	0x08
#define RSB_STAT	0x0c
#define RSB_DADDR0	0x10
#define RSB_DLEN	0x18
#define RSB_DATA0	0x1c
#define RSB_LCR		0x24
#define RSB_PMCR	0x28
#define RSB_CMD		0x2c
#define RSB_SADDR	0x30

#define RSBCMD_SRTA	0xE8
#define RSBCMD_RD8	0x8B
#define RSBCMD_RD16	0x9C
#define RSBCMD_RD32	0xA6
#define RSBCMD_WR8	0x4E
#define RSBCMD_WR16	0x59
#define RSBCMD_WR32	0x63

#define MAX_TRIES	100000

static int rsb_wait_bit(const char *desc, unsigned int offset, uint32_t mask)
{
	uint32_t reg, tries = MAX_TRIES;

	do
		reg = mmio_read_32(SUNXI_R_RSB_BASE + offset);
	while ((reg & mask) && --tries);	/* transaction in progress */
	if (reg & mask) {
		ERROR("%s: timed out\n", desc);
		return -ETIMEDOUT;
	}

	return 0;
}

static int rsb_wait_stat(const char *desc)
{
	uint32_t reg;
	int ret = rsb_wait_bit(desc, RSB_CTRL, BIT(7));

	if (ret)
		return ret;

	reg = mmio_read_32(SUNXI_R_RSB_BASE + RSB_STAT);
	if (reg == 0x01)
		return 0;

	ERROR("%s: 0x%x\n", desc, reg);
	return -reg;
}

/* Initialize the RSB controller. */
int rsb_init_controller(void)
{
	mmio_write_32(SUNXI_R_RSB_BASE + RSB_CTRL, 0x01); /* soft reset */

	return rsb_wait_bit("RSB: reset controller", RSB_CTRL, BIT(0));
}

int rsb_read(uint8_t rt_addr, uint8_t reg_addr)
{
	int ret;

	mmio_write_32(SUNXI_R_RSB_BASE + RSB_CMD, RSBCMD_RD8); /* read a byte */
	mmio_write_32(SUNXI_R_RSB_BASE + RSB_SADDR, rt_addr << 16);
	mmio_write_32(SUNXI_R_RSB_BASE + RSB_DADDR0, reg_addr);
	mmio_write_32(SUNXI_R_RSB_BASE + RSB_CTRL, 0x80);/* start transaction */

	ret = rsb_wait_stat("RSB: read command");
	if (ret)
		return ret;

	return mmio_read_32(SUNXI_R_RSB_BASE + RSB_DATA0) & 0xff; /* result */
}

int rsb_write(uint8_t rt_addr, uint8_t reg_addr, uint8_t value)
{
	mmio_write_32(SUNXI_R_RSB_BASE + RSB_CMD, RSBCMD_WR8);	/* byte write */
	mmio_write_32(SUNXI_R_RSB_BASE + RSB_SADDR, rt_addr << 16);
	mmio_write_32(SUNXI_R_RSB_BASE + RSB_DADDR0, reg_addr);
	mmio_write_32(SUNXI_R_RSB_BASE + RSB_DATA0, value);
	mmio_write_32(SUNXI_R_RSB_BASE + RSB_CTRL, 0x80);/* start transaction */

	return rsb_wait_stat("RSB: write command");
}

int rsb_set_device_mode(uint32_t device_mode)
{
	mmio_write_32(SUNXI_R_RSB_BASE + RSB_PMCR,
		      (device_mode & 0x00ffffff) | BIT(31));

	return rsb_wait_bit("RSB: set device to RSB", RSB_PMCR, BIT(31));
}

int rsb_set_bus_speed(uint32_t source_freq, uint32_t bus_freq)
{
	uint32_t reg;

	if (bus_freq == 0)
		return -EINVAL;

	reg = source_freq / bus_freq;
	if (reg < 2)
		return -EINVAL;

	reg = reg / 2 - 1;
	reg |= (1U << 8);		/* one cycle of CD output delay */

	mmio_write_32(SUNXI_R_RSB_BASE + RSB_CCR, reg);

	return 0;
}

/* Initialize the RSB PMIC connection. */
int rsb_assign_runtime_address(uint16_t hw_addr, uint8_t rt_addr)
{
	mmio_write_32(SUNXI_R_RSB_BASE + RSB_SADDR, hw_addr | (rt_addr << 16));
	mmio_write_32(SUNXI_R_RSB_BASE + RSB_CMD, RSBCMD_SRTA);
	mmio_write_32(SUNXI_R_RSB_BASE + RSB_CTRL, 0x80);

	return rsb_wait_stat("RSB: set run-time address");
}