1/* 2 * Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "soc/uart.h" 17#include "los_magickey.h" 18#include "los_task.h" 19#include "hdf_log.h" 20#include "osal_io.h" 21#include "osal_irq.h" 22#include "osal_time.h" 23#include "uart_if.h" 24#include "uart_pl011.h" 25 26#define HDF_LOG_TAG uart_pl011 27#define FIFO_SIZE 128 28#define UART_WAIT_MS 10 29#define IBRD_COEFFICIENTS 16 30#define FBRD_COEFFICIENTS 8 31static uint32_t Pl011Irq(uint32_t irq, void *data) 32{ 33 uint32_t status; 34 uint32_t fr; 35 char buf[FIFO_SIZE]; 36 uint32_t count = 0; 37 struct UartPl011Port *port = NULL; 38 struct UartDriverData *udd = (struct UartDriverData *)data; 39 40 UNUSED(irq); 41 if (udd == NULL || udd->private == NULL) { 42 HDF_LOGE("%s: invalid parame", __func__); 43 return HDF_FAILURE; 44 } 45 port = (struct UartPl011Port *)udd->private; 46 status = OSAL_READW(port->physBase + UART_MIS); 47 if (status & (UART_MIS_RX | UART_IMSC_TIMEOUT)) { 48 do { 49 fr = OSAL_READB(port->physBase + UART_FR); 50 if (fr & UART_FR_RXFE) { 51 break; 52 } 53 buf[count++] = OSAL_READB(port->physBase + UART_DR); 54 if (udd->num != CONSOLE_UART) { 55 continue; 56 } 57 if (CheckMagicKey(buf[count - 1], CONSOLE_SERIAL)) { 58 goto end; 59 } 60 } while (count < FIFO_SIZE); 61 udd->recv(udd, buf, count); 62 } 63end: 64 /* clear all interrupt */ 65 OSAL_WRITEW(0xFFFF, port->physBase + UART_CLR); 66 return HDF_SUCCESS; 67} 68 69static void Pl011ConfigBaudrate(const struct UartDriverData *udd, const struct UartPl011Port *port) 70{ 71 uint64_t tmp; 72 uint32_t value; 73 uint32_t divider; 74 uint32_t remainder; 75 uint32_t fraction; 76 77 tmp = (uint64_t)IBRD_COEFFICIENTS * (uint64_t)udd->baudrate; 78 if (tmp == 0 || tmp > UINT32_MAX) { 79 HDF_LOGE("%s: err, baudrate %u is invalid", __func__, udd->baudrate); 80 return; 81 } 82 83 value = IBRD_COEFFICIENTS * udd->baudrate; 84 divider = CONFIG_UART_CLK_INPUT / value; 85 remainder = CONFIG_UART_CLK_INPUT % value; 86 value = (FBRD_COEFFICIENTS * remainder) / udd->baudrate; 87 fraction = (value >> 1) + (value & 1); 88 OSAL_WRITEL(divider, port->physBase + UART_IBRD); 89 OSAL_WRITEL(fraction, port->physBase + UART_FBRD); 90} 91 92static void Pl011ConfigDataBits(const struct UartDriverData *udd, uint32_t *lcrh) 93{ 94 *lcrh &= ~UART_LCR_H_8_BIT; 95 switch (udd->attr.dataBits) { 96 case UART_ATTR_DATABIT_5: 97 *lcrh |= UART_LCR_H_5_BIT; 98 break; 99 case UART_ATTR_DATABIT_6: 100 *lcrh |= UART_LCR_H_6_BIT; 101 break; 102 case UART_ATTR_DATABIT_7: 103 *lcrh |= UART_LCR_H_7_BIT; 104 break; 105 case UART_ATTR_DATABIT_8: 106 default: 107 *lcrh |= UART_LCR_H_8_BIT; 108 break; 109 } 110} 111 112static void Pl011ConfigParity(const struct UartDriverData *udd, uint32_t *lcrh) 113{ 114 switch (udd->attr.parity) { 115 case UART_ATTR_PARITY_EVEN: 116 *lcrh |= UART_LCR_H_PEN; 117 *lcrh |= UART_LCR_H_EPS; 118 *lcrh |= UART_LCR_H_FIFO_EN; 119 break; 120 case UART_ATTR_PARITY_ODD: 121 *lcrh |= UART_LCR_H_PEN; 122 *lcrh &= ~UART_LCR_H_EPS; 123 *lcrh |= UART_LCR_H_FIFO_EN; 124 break; 125 case UART_ATTR_PARITY_MARK: 126 *lcrh |= UART_LCR_H_PEN; 127 *lcrh &= ~UART_LCR_H_EPS; 128 *lcrh |= UART_LCR_H_FIFO_EN; 129 *lcrh |= UART_LCR_H_SPS; 130 break; 131 case UART_ATTR_PARITY_SPACE: 132 *lcrh |= UART_LCR_H_PEN; 133 *lcrh |= UART_LCR_H_EPS; 134 *lcrh |= UART_LCR_H_FIFO_EN; 135 *lcrh |= UART_LCR_H_SPS; 136 break; 137 case UART_ATTR_PARITY_NONE: 138 default: 139 *lcrh &= ~UART_LCR_H_PEN; 140 *lcrh &= ~UART_LCR_H_SPS; 141 break; 142 } 143} 144 145static void Pl011ConfigStopBits(const struct UartDriverData *udd, uint32_t *lcrh) 146{ 147 switch (udd->attr.stopBits) { 148 case UART_ATTR_STOPBIT_2: 149 *lcrh |= UART_LCR_H_STP2; 150 break; 151 case UART_ATTR_STOPBIT_1: 152 default: 153 *lcrh &= ~UART_LCR_H_STP2; 154 break; 155 } 156} 157 158static void Pl011ConfigLCRH(const struct UartDriverData *udd, const struct UartPl011Port *port, uint32_t lcrh) 159{ 160 Pl011ConfigDataBits(udd, &lcrh); 161 lcrh &= ~UART_LCR_H_PEN; 162 lcrh &= ~UART_LCR_H_EPS; 163 lcrh &= ~UART_LCR_H_SPS; 164 Pl011ConfigParity(udd, &lcrh); 165 Pl011ConfigStopBits(udd, &lcrh); 166 if (udd->attr.fifoRxEn || udd->attr.fifoTxEn) { 167 lcrh |= UART_LCR_H_FIFO_EN; 168 } 169 OSAL_WRITEB(lcrh, port->physBase + UART_LCR_H); 170} 171 172static int32_t Pl011ConfigIn(struct UartDriverData *udd) 173{ 174 uint32_t cr; 175 uint32_t lcrh; 176 struct UartPl011Port *port = NULL; 177 178 port = (struct UartPl011Port *)udd->private; 179 if (port == NULL) { 180 HDF_LOGE("%s: port is NULL", __func__); 181 return HDF_ERR_INVALID_PARAM; 182 } 183 /* get CR */ 184 cr = OSAL_READW(port->physBase + UART_CR); 185 /* get LCR_H */ 186 lcrh = OSAL_READW(port->physBase + UART_LCR_H); 187 /* uart disable */ 188 OSAL_WRITEW(0, port->physBase + UART_CR); 189 /* config cts/rts */ 190 if (UART_ATTR_CTS_EN == udd->attr.cts) { 191 cr |= UART_CR_CTS; 192 } else { 193 cr &= ~UART_CR_CTS; 194 } 195 if (UART_ATTR_RTS_EN == udd->attr.rts) { 196 cr |= UART_CR_RTS; 197 } else { 198 cr &= ~UART_CR_RTS; 199 } 200 lcrh &= ~UART_LCR_H_FIFO_EN; 201 OSAL_WRITEB(lcrh, port->physBase + UART_LCR_H); 202 203 cr &= ~UART_CR_EN; 204 OSAL_WRITEW(cr, port->physBase + UART_CR); 205 206 /* set baud rate */ 207 Pl011ConfigBaudrate(udd, port); 208 209 /* config lcr_h */ 210 Pl011ConfigLCRH(udd, port, lcrh); 211 212 cr |= UART_CR_EN; 213 /* resume CR */ 214 OSAL_WRITEW(cr, port->physBase + UART_CR); 215 return HDF_SUCCESS; 216} 217 218static int32_t Pl011StartUp(struct UartDriverData *udd) 219{ 220 int32_t ret; 221 uint32_t cr; 222 struct UartPl011Port *port = NULL; 223 224 if (udd == NULL) { 225 HDF_LOGE("%s: udd is null", __func__); 226 return HDF_ERR_INVALID_PARAM; 227 } 228 port = (struct UartPl011Port *)udd->private; 229 if (port == NULL) { 230 HDF_LOGE("%s: port is null", __func__); 231 return HDF_ERR_INVALID_PARAM; 232 } 233 /* enable the clock */ 234 LOS_TaskLock(); 235 uart_clk_cfg(udd->num, true); 236 LOS_TaskUnlock(); 237 /* uart disable */ 238 OSAL_WRITEW(0, port->physBase + UART_CR); 239 OSAL_WRITEW(0xFF, port->physBase + UART_RSR); 240 /* clear all interrupt,set mask */ 241 OSAL_WRITEW(0xFFFF, port->physBase + UART_CLR); 242 /* mask all interrupt */ 243 OSAL_WRITEW(0x0, port->physBase + UART_IMSC); 244 /* interrupt trigger line RX: 4/8, TX 7/8 */ 245 OSAL_WRITEW(UART_IFLS_RX4_8 | UART_IFLS_TX7_8, port->physBase + UART_IFLS); 246 if (!(udd->flags & UART_FLG_DMA_RX)) { 247 if (!(port->flags & PL011_FLG_IRQ_REQUESTED)) { 248 ret = OsalRegisterIrq(port->irqNum, 0, Pl011Irq, "uart_pl011", udd); 249 if (ret == 0) { 250 port->flags |= PL011_FLG_IRQ_REQUESTED; 251 /* enable rx and timeout interrupt */ 252 OSAL_WRITEW(UART_IMSC_RX | UART_IMSC_TIMEOUT, port->physBase + UART_IMSC); 253 } 254 } 255 } 256 cr = OSAL_READW(port->physBase + UART_CR); 257 cr |= UART_CR_EN | UART_CR_RX_EN | UART_CR_TX_EN; 258 OSAL_WRITEL(cr, port->physBase + UART_CR); 259 ret = Pl011ConfigIn(udd); 260 return ret; 261} 262 263static int32_t Pl011ShutDown(struct UartDriverData *udd) 264{ 265 uint32_t reg_tmp; 266 struct UartPl011Port *port = NULL; 267 268 if (udd == NULL) { 269 HDF_LOGE("%s: udd is null", __func__); 270 return HDF_ERR_INVALID_PARAM; 271 } 272 port = (struct UartPl011Port *)udd->private; 273 if (port == NULL) { 274 HDF_LOGE("%s: port is null", __func__); 275 return HDF_ERR_INVALID_PARAM; 276 } 277 OSAL_WRITEW(0, port->physBase + UART_IMSC); 278 OSAL_WRITEW(0xFFFF, port->physBase + UART_CLR); 279 if (port->flags & PL011_FLG_IRQ_REQUESTED) { 280 OsalUnregisterIrq(port->irqNum, udd); 281 port->flags &= ~PL011_FLG_IRQ_REQUESTED; 282 } 283 284 reg_tmp = OSAL_READW(port->physBase + UART_CR); 285 reg_tmp &= ~UART_CR_TX_EN; 286 reg_tmp &= ~UART_CR_RX_EN; 287 reg_tmp &= ~UART_CR_EN; 288 OSAL_WRITEW(reg_tmp, port->physBase + UART_CR); 289 290 /* disable break and fifo */ 291 reg_tmp = OSAL_READW(port->physBase + UART_LCR_H); 292 reg_tmp &= ~(UART_LCR_H_BREAK); 293 reg_tmp &= ~(UART_LCR_H_FIFO_EN); 294 OSAL_WRITEW(reg_tmp, port->physBase + UART_LCR_H); 295 296 /* shut down the clock */ 297 LOS_TaskLock(); 298 uart_clk_cfg(udd->num, false); 299 LOS_TaskUnlock(); 300 return HDF_SUCCESS; 301} 302 303static int32_t Pl011StartTx(struct UartDriverData *udd, const char *buf, size_t count) 304{ 305 struct UartPl011Port *port = NULL; 306 307 if (udd == NULL || buf == NULL || count == 0) { 308 HDF_LOGE("%s: invalid parame", __func__); 309 return HDF_ERR_INVALID_PARAM; 310 } 311 port = (struct UartPl011Port *)udd->private; 312 if (port == NULL) { 313 HDF_LOGE("%s: port is null", __func__); 314 return HDF_ERR_INVALID_PARAM; 315 } 316 /* UART_WITH_LOCK: there is a spinlock in the function to write reg in order. */ 317 (void)UartPutsReg(port->physBase, buf, count, UART_WITH_LOCK); 318 return HDF_SUCCESS; 319} 320 321static int32_t Pl011Config(struct UartDriverData *udd) 322{ 323 uint32_t fr; 324 struct UartPl011Port *port = NULL; 325 326 if (udd == NULL) { 327 HDF_LOGE("%s: udd is null", __func__); 328 return HDF_ERR_INVALID_PARAM; 329 } 330 port = (struct UartPl011Port *)udd->private; 331 if (port == NULL) { 332 HDF_LOGE("%s: port is null", __func__); 333 return HDF_ERR_INVALID_PARAM; 334 } 335 /* wait for send finish */ 336 do { 337 fr = OSAL_READB(port->physBase + UART_FR); 338 if (!(fr & UART_FR_BUSY)) { 339 break; 340 } 341 OsalMSleep(UART_WAIT_MS); 342 } while (1); 343 return Pl011ConfigIn(udd); 344} 345 346struct UartOps g_pl011Uops = { 347 .StartUp = Pl011StartUp, 348 .ShutDown = Pl011ShutDown, 349 .StartTx = Pl011StartTx, 350 .Config = Pl011Config, 351 .DmaStartUp = NULL, 352 .DmaShutDown = NULL, 353}; 354 355int32_t Pl011Read(struct UartDriverData *udd, char *buf, size_t count) 356{ 357 uint32_t wp; 358 uint32_t rp; 359 uint32_t upperHalf; 360 uint32_t lowerHalf = 0; 361 unsigned long data; 362 363 if (udd == NULL || buf == NULL || count == 0 || udd->rxTransfer == NULL) { 364 HDF_LOGE("%s: invalid parameter", __func__); 365 return HDF_ERR_INVALID_PARAM; 366 } 367 wp = udd->rxTransfer->wp; 368 rp = udd->rxTransfer->rp; 369 data = (unsigned long)(udd->rxTransfer->data); 370 371 if (rp == wp) { 372 return 0; // buffer empty 373 } 374 375 if (rp < wp) { // rp behind 376 upperHalf = (count > (wp - rp)) ? (wp - rp) : count; 377 if (upperHalf > 0 && memcpy_s(buf, upperHalf, (void *)(data + rp), upperHalf) != EOK) { 378 return HDF_ERR_IO; 379 } 380 rp += upperHalf; 381 } else { // wp behind 382 count = (count > (BUF_SIZE - rp + wp)) ? (BUF_SIZE - rp + wp) : count; 383 upperHalf = (count > (BUF_SIZE - rp)) ? (BUF_SIZE - rp) : count; 384 lowerHalf = (count > (BUF_SIZE - rp)) ? (count - (BUF_SIZE - rp)) : 0; 385 if (upperHalf > 0 && memcpy_s(buf, upperHalf, (void *)(data + rp), upperHalf) != EOK) { 386 return HDF_ERR_IO; 387 } 388 if (lowerHalf > 0 && memcpy_s(buf + upperHalf, lowerHalf, (void *)(data), lowerHalf) != EOK) { 389 return HDF_ERR_IO; 390 } 391 rp += upperHalf; 392 if (rp >= BUF_SIZE) { 393 rp = lowerHalf; 394 } 395 } 396 udd->rxTransfer->rp = rp; 397 return (upperHalf + lowerHalf); 398} 399 400static int32_t Pl011Notify(struct wait_queue_head *wait) 401{ 402 if (wait == NULL) { 403 return HDF_ERR_INVALID_PARAM; 404 } 405 LOS_EventWrite(&wait->stEvent, 0x1); 406 notify_poll(wait); 407 return HDF_SUCCESS; 408} 409 410int32_t PL011UartRecvNotify(struct UartDriverData *udd, const char *buf, size_t count) 411{ 412 uint32_t wp; 413 uint32_t rp; 414 uint32_t upperHalf; 415 uint32_t lowerHalf = 0; 416 unsigned long data; 417 418 if (udd == NULL || buf == NULL || count == 0 || udd->rxTransfer == NULL) { 419 HDF_LOGE("%s: invalid parameter", __func__); 420 return HDF_ERR_INVALID_PARAM; 421 } 422 wp = udd->rxTransfer->wp; 423 rp = udd->rxTransfer->rp; 424 data = (unsigned long)(udd->rxTransfer->data); 425 426 if (wp < rp) { // wp behind 427 upperHalf = (count > (rp - wp - 1)) ? (rp - wp - 1) : count; 428 if (upperHalf > 0 && memcpy_s((void *)(data + wp), upperHalf, buf, upperHalf) != EOK) { 429 return HDF_ERR_IO; 430 } 431 wp += upperHalf; 432 } else { // rp behind 433 count = (count > ((BUF_SIZE - wp) + rp - 1)) ? (BUF_SIZE - wp) + rp - 1 : count; 434 upperHalf = (count > (BUF_SIZE - wp)) ? (BUF_SIZE - wp) : count; 435 lowerHalf = (count > (BUF_SIZE - wp)) ? (count - (BUF_SIZE - wp)) : 0; 436 if (upperHalf > 0 && memcpy_s((void *)(data + wp), upperHalf, buf, upperHalf) != EOK) { 437 return HDF_ERR_IO; 438 } 439 if (lowerHalf > 0 && memcpy_s((void *)(data), lowerHalf, buf + upperHalf, lowerHalf) != EOK) { 440 return HDF_ERR_IO; 441 } 442 wp += upperHalf; 443 if (wp >= BUF_SIZE) { 444 wp = lowerHalf; 445 } 446 } 447 448 if (Pl011Notify(&udd->wait) != HDF_SUCCESS) { 449 HDF_LOGE("%s: Pl011 notify err", __func__); 450 return HDF_FAILURE; 451 } 452 udd->rxTransfer->wp = wp; 453 return (upperHalf + lowerHalf); 454} 455 456bool PL011UartRxBufEmpty(struct UartDriverData *udd) 457{ 458 struct UartTransfer *transfer = udd->rxTransfer; 459 return (transfer->wp == transfer->rp); 460} 461 462struct UartOps *Pl011GetOps(void) 463{ 464 return &g_pl011Uops; 465} 466