]>
Commit | Line | Data |
---|---|---|
66ebea06 HG |
1 | /* |
2 | * (C) Copyright 2014 Hans de Goede <[email protected]> | |
3 | * | |
4 | * Based on allwinner u-boot sources rsb code which is: | |
5 | * (C) Copyright 2007-2013 | |
6 | * Allwinner Technology Co., Ltd. <www.allwinnertech.com> | |
7 | * lixiang <[email protected]> | |
8 | * | |
9 | * SPDX-License-Identifier: GPL-2.0+ | |
10 | */ | |
11 | ||
12 | #include <common.h> | |
13 | #include <errno.h> | |
14 | #include <asm/arch/cpu.h> | |
15 | #include <asm/arch/gpio.h> | |
16 | #include <asm/arch/prcm.h> | |
17 | #include <asm/arch/rsb.h> | |
18 | ||
37d46dd3 HG |
19 | static int rsb_set_device_mode(void); |
20 | ||
66ebea06 HG |
21 | static void rsb_cfg_io(void) |
22 | { | |
d35488c7 | 23 | #ifdef CONFIG_MACH_SUN8I |
487b3277 PK |
24 | sunxi_gpio_set_cfgpin(SUNXI_GPL(0), SUN8I_GPL_R_RSB); |
25 | sunxi_gpio_set_cfgpin(SUNXI_GPL(1), SUN8I_GPL_R_RSB); | |
66ebea06 HG |
26 | sunxi_gpio_set_pull(SUNXI_GPL(0), 1); |
27 | sunxi_gpio_set_pull(SUNXI_GPL(1), 1); | |
28 | sunxi_gpio_set_drv(SUNXI_GPL(0), 2); | |
29 | sunxi_gpio_set_drv(SUNXI_GPL(1), 2); | |
d35488c7 | 30 | #elif defined CONFIG_MACH_SUN9I |
487b3277 PK |
31 | sunxi_gpio_set_cfgpin(SUNXI_GPN(0), SUN9I_GPN_R_RSB); |
32 | sunxi_gpio_set_cfgpin(SUNXI_GPN(1), SUN9I_GPN_R_RSB); | |
d35488c7 HG |
33 | sunxi_gpio_set_pull(SUNXI_GPN(0), 1); |
34 | sunxi_gpio_set_pull(SUNXI_GPN(1), 1); | |
35 | sunxi_gpio_set_drv(SUNXI_GPN(0), 2); | |
36 | sunxi_gpio_set_drv(SUNXI_GPN(1), 2); | |
37 | #else | |
38 | #error unsupported MACH_SUNXI | |
39 | #endif | |
66ebea06 HG |
40 | } |
41 | ||
42 | static void rsb_set_clk(void) | |
43 | { | |
44 | struct sunxi_rsb_reg * const rsb = | |
45 | (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; | |
46 | u32 div = 0; | |
47 | u32 cd_odly = 0; | |
48 | ||
49 | /* Source is Hosc24M, set RSB clk to 3Mhz */ | |
50 | div = 24000000 / 3000000 / 2 - 1; | |
51 | cd_odly = div >> 1; | |
52 | if (!cd_odly) | |
53 | cd_odly = 1; | |
54 | ||
55 | writel((cd_odly << 8) | div, &rsb->ccr); | |
56 | } | |
57 | ||
37d46dd3 | 58 | int rsb_init(void) |
66ebea06 HG |
59 | { |
60 | struct sunxi_rsb_reg * const rsb = | |
61 | (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; | |
62 | ||
66ebea06 HG |
63 | /* Enable RSB and PIO clk, and de-assert their resets */ |
64 | prcm_apb0_enable(PRCM_APB0_GATE_PIO | PRCM_APB0_GATE_RSB); | |
65 | ||
dec7c842 CYT |
66 | /* Setup external pins */ |
67 | rsb_cfg_io(); | |
68 | ||
66ebea06 HG |
69 | writel(RSB_CTRL_SOFT_RST, &rsb->ctrl); |
70 | rsb_set_clk(); | |
37d46dd3 HG |
71 | |
72 | return rsb_set_device_mode(); | |
66ebea06 HG |
73 | } |
74 | ||
75 | static int rsb_await_trans(void) | |
76 | { | |
77 | struct sunxi_rsb_reg * const rsb = | |
78 | (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; | |
79 | unsigned long tmo = timer_get_us() + 1000000; | |
80 | u32 stat; | |
81 | int ret; | |
82 | ||
83 | while (1) { | |
84 | stat = readl(&rsb->stat); | |
85 | if (stat & RSB_STAT_LBSY_INT) { | |
86 | ret = -EBUSY; | |
87 | break; | |
88 | } | |
89 | if (stat & RSB_STAT_TERR_INT) { | |
90 | ret = -EIO; | |
91 | break; | |
92 | } | |
93 | if (stat & RSB_STAT_TOVER_INT) { | |
94 | ret = 0; | |
95 | break; | |
96 | } | |
97 | if (timer_get_us() > tmo) { | |
98 | ret = -ETIME; | |
99 | break; | |
100 | } | |
101 | } | |
102 | writel(stat, &rsb->stat); /* Clear status bits */ | |
103 | ||
104 | return ret; | |
105 | } | |
106 | ||
37d46dd3 | 107 | static int rsb_set_device_mode(void) |
66ebea06 HG |
108 | { |
109 | struct sunxi_rsb_reg * const rsb = | |
110 | (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; | |
111 | unsigned long tmo = timer_get_us() + 1000000; | |
112 | ||
37d46dd3 HG |
113 | writel(RSB_DMCR_DEVICE_MODE_START | RSB_DMCR_DEVICE_MODE_DATA, |
114 | &rsb->dmcr); | |
66ebea06 HG |
115 | |
116 | while (readl(&rsb->dmcr) & RSB_DMCR_DEVICE_MODE_START) { | |
117 | if (timer_get_us() > tmo) | |
118 | return -ETIME; | |
119 | } | |
120 | ||
121 | return rsb_await_trans(); | |
122 | } | |
123 | ||
124 | static int rsb_do_trans(void) | |
125 | { | |
126 | struct sunxi_rsb_reg * const rsb = | |
127 | (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; | |
128 | ||
129 | setbits_le32(&rsb->ctrl, RSB_CTRL_START_TRANS); | |
130 | return rsb_await_trans(); | |
131 | } | |
132 | ||
133 | int rsb_set_device_address(u16 device_addr, u16 runtime_addr) | |
134 | { | |
135 | struct sunxi_rsb_reg * const rsb = | |
136 | (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; | |
137 | ||
138 | writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_addr) | | |
139 | RSB_DEVADDR_DEVICE_ADDR(device_addr), &rsb->devaddr); | |
140 | writel(RSB_CMD_SET_RTSADDR, &rsb->cmd); | |
141 | ||
142 | return rsb_do_trans(); | |
143 | } | |
144 | ||
145 | int rsb_write(const u16 runtime_device_addr, const u8 reg_addr, u8 data) | |
146 | { | |
147 | struct sunxi_rsb_reg * const rsb = | |
148 | (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; | |
149 | ||
150 | writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr); | |
151 | writel(reg_addr, &rsb->addr); | |
152 | writel(data, &rsb->data); | |
153 | writel(RSB_CMD_BYTE_WRITE, &rsb->cmd); | |
154 | ||
155 | return rsb_do_trans(); | |
156 | } | |
157 | ||
158 | int rsb_read(const u16 runtime_device_addr, const u8 reg_addr, u8 *data) | |
159 | { | |
160 | struct sunxi_rsb_reg * const rsb = | |
161 | (struct sunxi_rsb_reg *)SUNXI_RSB_BASE; | |
162 | int ret; | |
163 | ||
164 | writel(RSB_DEVADDR_RUNTIME_ADDR(runtime_device_addr), &rsb->devaddr); | |
165 | writel(reg_addr, &rsb->addr); | |
166 | writel(RSB_CMD_BYTE_READ, &rsb->cmd); | |
167 | ||
168 | ret = rsb_do_trans(); | |
169 | if (ret) | |
170 | return ret; | |
171 | ||
172 | *data = readl(&rsb->data) & 0xff; | |
173 | ||
174 | return 0; | |
175 | } |