1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *  http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied.  See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  */
19 
20 
21 #include <assert.h>
22 #include <DA1469xAB.h>
23 #include <da1469x_config.h>
24 #include <da1469x_otp.h>
25 
26 #define OTPC_TIM1_REG_2MHz       0x09990001
27 #define OTPC_TIM1_REG_4MHz       0x09990003
28 #define OTPC_TIM1_REG_8MHz       0x09990007
29 #define OTPC_TIM1_REG_16MHz      0x0999000F
30 #define OTPC_TIM1_REG_32MHz      0x0999101F
31 #define OTPC_TIM1_REG_48MHz      0x0999202F
32 #define OTPC_TIM1_REG_96MHz      0x0999515F
33 
34 #define OTPC_TIM2_REG_DEFAULT    0xA4040409
35 
36 static inline void
da1469x_clock_amba_enable(uint32_t mask)37 da1469x_clock_amba_enable(uint32_t mask)
38 {
39     uint32_t primask;
40 
41     primask = DA1469X_IRQ_DISABLE();
42     CRG_TOP->CLK_AMBA_REG |= mask;
43     DA1469X_IRQ_ENABLE(primask);
44 }
45 
46 static inline void
da1469x_clock_amba_disable(uint32_t mask)47 da1469x_clock_amba_disable(uint32_t mask)
48 {
49     uint32_t primask;
50 
51     primask = DA1469X_IRQ_DISABLE();
52     CRG_TOP->CLK_AMBA_REG &= ~mask;
53     DA1469X_IRQ_ENABLE(primask);
54 }
55 
56 static inline void
da1469x_otp_tim1_adjust(int clk_speed)57 da1469x_otp_tim1_adjust(int clk_speed)
58 {
59     switch (clk_speed) {
60     default:
61         /* Unsupported access speed, fall-through to default 32MHz */
62         assert(0);
63     case 32000000:
64         OTPC->OTPC_TIM1_REG = OTPC_TIM1_REG_32MHz;
65         break;
66     /* 48MHz is not supported as PLL is only allowed when HDIV and PDIV are both 0 */
67     case 96000000:
68         OTPC->OTPC_TIM1_REG = OTPC_TIM1_REG_96MHz;
69         break;
70     case 2000000:
71         OTPC->OTPC_TIM1_REG = OTPC_TIM1_REG_2MHz;
72         break;
73     case 4000000:
74         OTPC->OTPC_TIM1_REG = OTPC_TIM1_REG_4MHz;
75         break;
76     case 8000000:
77         OTPC->OTPC_TIM1_REG = OTPC_TIM1_REG_8MHz;
78         break;
79     case 16000000:
80         OTPC->OTPC_TIM1_REG = OTPC_TIM1_REG_16MHz;
81         break;
82     }
83 }
84 
85 int
da1469x_otp_read(uint32_t offset,void * dst,uint32_t num_bytes)86 da1469x_otp_read(uint32_t offset, void *dst, uint32_t num_bytes)
87 {
88     uint32_t *src_addr = (uint32_t *)(MCU_OTP_M_BASE + offset);
89     uint32_t *dst_addr = dst;
90 
91     if (offset >= MCU_OTPM_SIZE || (offset + num_bytes) > MCU_OTPM_SIZE) {
92        return OTP_ERR_INVALID_ADDRESS;
93     }
94 
95     if (num_bytes & 0x3) {
96        return OTP_ERR_INVALID_SIZE_ALIGNMENT;
97     }
98 
99     /* Enable OTP clock and set mode to standby */
100     da1469x_clock_amba_enable(CRG_TOP_CLK_AMBA_REG_OTP_ENABLE_Msk);
101 
102     da1469x_otp_set_mode(OTPC_MODE_READ);
103 
104     for (; num_bytes; dst_addr++, src_addr++, num_bytes -= 4) {
105         *dst_addr = *src_addr;
106     }
107 
108     da1469x_otp_set_mode(OTPC_MODE_DSTBY);
109 
110     /* Disable OTP clock */
111     da1469x_clock_amba_disable(CRG_TOP_CLK_AMBA_REG_OTP_ENABLE_Msk);
112     return 0;
113 }
114 
115 int
da1469x_otp_write(uint32_t offset,const void * src,uint32_t num_bytes)116 da1469x_otp_write(uint32_t offset, const void *src, uint32_t num_bytes)
117 {
118     uint32_t *dst_addr = (uint32_t *)(MCU_OTP_M_BASE + offset);
119     const uint32_t *src_addr = src;
120     int ret = 0;
121 
122     if (offset >= MCU_OTPM_SIZE || (offset + num_bytes) > MCU_OTPM_SIZE) {
123        return OTP_ERR_INVALID_ADDRESS;
124     }
125 
126     if (num_bytes & 0x3) {
127        return OTP_ERR_INVALID_SIZE_ALIGNMENT;
128     }
129 
130     /* Enable OTP clock and set mode to standby */
131     da1469x_clock_amba_enable(CRG_TOP_CLK_AMBA_REG_OTP_ENABLE_Msk);
132 
133     do {
134         da1469x_otp_set_mode(OTPC_MODE_PROG);
135 
136         /* wait for programming to go idle and data buffer to be empty */
137         while (!(OTPC->OTPC_STAT_REG & OTPC_OTPC_STAT_REG_OTPC_STAT_PRDY_Msk));
138         while (!(OTPC->OTPC_STAT_REG &
139                  OTPC_OTPC_STAT_REG_OTPC_STAT_PBUF_EMPTY_Msk));
140 
141         /* fill data buffer with a word and trigger via the PADDR reg */
142         OTPC->OTPC_PWORD_REG = *src_addr;
143         OTPC->OTPC_PADDR_REG = ((uint32_t)dst_addr >> 2) &
144                                OTPC_OTPC_PADDR_REG_OTPC_PADDR_Msk;
145 
146         while (!(OTPC->OTPC_STAT_REG & OTPC_OTPC_STAT_REG_OTPC_STAT_PRDY_Msk));
147 
148         /* set mode to verify */
149         da1469x_otp_set_mode(OTPC_MODE_PVFY);
150 
151         /* read data and compare to source */
152         if (*dst_addr != *src_addr) {
153             ret = OTP_ERR_PROGRAM_VERIFY_FAILED;
154             goto fail_write;
155         }
156 
157         da1469x_otp_set_mode(OTPC_MODE_STBY);
158         num_bytes -= 4;
159         src_addr++;
160         dst_addr++;
161     } while (num_bytes);
162 
163 fail_write:
164 
165     /* Disable OTP clock */
166     da1469x_clock_amba_disable(CRG_TOP_CLK_AMBA_REG_OTP_ENABLE_Msk);
167 
168     return ret;
169 }
170 
171 void
da1469x_otp_set_speed(uint32_t clk_speed)172 da1469x_otp_set_speed(uint32_t clk_speed)
173 {
174     /* Enable OTP clock and set mode to standby */
175     da1469x_clock_amba_enable(CRG_TOP_CLK_AMBA_REG_OTP_ENABLE_Msk);
176 
177     da1469x_otp_set_mode(OTPC_MODE_DSTBY);
178 
179     da1469x_otp_tim1_adjust(clk_speed);
180 
181     /* Disable OTP clock */
182     da1469x_clock_amba_disable(CRG_TOP_CLK_AMBA_REG_OTP_ENABLE_Msk);
183 }
184 
185 
186 void
da1469x_otp_init(void)187 da1469x_otp_init(void)
188 {
189     /* Enable OTP clock and set mode to standby */
190     da1469x_clock_amba_enable(CRG_TOP_CLK_AMBA_REG_OTP_ENABLE_Msk);
191 
192     da1469x_otp_set_mode(OTPC_MODE_STBY);
193 
194     /* Set clk timing */
195     da1469x_otp_tim1_adjust(SystemCoreClock);
196 
197     OTPC->OTPC_TIM2_REG = OTPC_TIM2_REG_DEFAULT;
198 
199     /* Disable OTP clock */
200     da1469x_clock_amba_disable(CRG_TOP_CLK_AMBA_REG_OTP_ENABLE_Msk);
201 }
202