blob: 2dbe7a4fe83c96d0c15c61b203ff4c590eb012ed [file] [log] [blame] [edit]
/*
* Copyright (c) 2013-2022 ARM Limited. All rights reserved.
* Copyright (c) 2022 Raspberry Pi Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the License); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* This is a shim between the SW_DP functions and the PIO
* implementation used for Debugprobe. Instead of calling bitbash functions,
* hand off the bit sequences to a SM for asynchronous completion.
*/
#include <stdio.h>
#include "DAP_config.h"
#include "DAP.h"
#include "probe.h"
/* Slight hack - we're not bitbashing so we need to set baudrate off the DAP's delay cycles.
* Ideally we don't want calls to udiv everywhere... */
#define MAKE_KHZ(x) (CPU_CLOCK / (2000 * ((x) + 1)))
volatile uint32_t cached_delay = 0;
// Generate SWJ Sequence
// count: sequence bit count
// data: pointer to sequence bit data
// return: none
#if ((DAP_SWD != 0) || (DAP_JTAG != 0))
void SWJ_Sequence (uint32_t count, const uint8_t *data) {
uint32_t bits;
uint32_t n;
if (DAP_Data.clock_delay != cached_delay) {
probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay));
cached_delay = DAP_Data.clock_delay;
}
probe_debug("SWJ sequence count = %d FDB=0x%2x\n", count, data[0]);
n = count;
while (n > 0) {
if (n > 8)
bits = 8;
else
bits = n;
probe_write_bits(bits, *data++);
n -= bits;
}
}
#endif
// Generate SWD Sequence
// info: sequence information
// swdo: pointer to SWDIO generated data
// swdi: pointer to SWDIO captured data
// return: none
#if (DAP_SWD != 0)
void SWD_Sequence (uint32_t info, const uint8_t *swdo, uint8_t *swdi) {
uint32_t bits;
uint32_t n;
if (DAP_Data.clock_delay != cached_delay) {
probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay));
cached_delay = DAP_Data.clock_delay;
}
probe_debug("SWD sequence\n");
n = info & SWD_SEQUENCE_CLK;
if (n == 0U) {
n = 64U;
}
bits = n;
if (info & SWD_SEQUENCE_DIN) {
while (n > 0) {
if (n > 8)
bits = 8;
else
bits = n;
*swdi++ = probe_read_bits(bits);
n -= bits;
}
} else {
while (n > 0) {
if (n > 8)
bits = 8;
else
bits = n;
probe_write_bits(bits, *swdo++);
n -= bits;
}
}
}
#endif
#if (DAP_SWD != 0)
// SWD Transfer I/O
// request: A[3:2] RnW APnDP
// data: DATA[31:0]
// return: ACK[2:0]
uint8_t SWD_Transfer (uint32_t request, uint32_t *data) {
uint8_t prq = 0;
uint8_t ack;
uint8_t bit;
uint32_t val = 0;
uint32_t parity = 0;
uint32_t n;
if (DAP_Data.clock_delay != cached_delay) {
probe_set_swclk_freq(MAKE_KHZ(DAP_Data.clock_delay));
cached_delay = DAP_Data.clock_delay;
}
probe_debug("SWD_transfer\n");
/* Generate the request packet */
prq |= (1 << 0); /* Start Bit */
for (n = 1; n < 5; n++) {
bit = (request >> (n - 1)) & 0x1;
prq |= bit << n;
parity += bit;
}
prq |= (parity & 0x1) << 5; /* Parity Bit */
prq |= (0 << 6); /* Stop Bit */
prq |= (1 << 7); /* Park bit */
probe_write_bits(8, prq);
/* Turnaround (ignore read bits) */
ack = probe_read_bits(DAP_Data.swd_conf.turnaround + 3);
ack >>= DAP_Data.swd_conf.turnaround;
if (ack == DAP_TRANSFER_OK) {
/* Data transfer phase */
if (request & DAP_TRANSFER_RnW) {
/* Read RDATA[0:31] - note probe_read shifts to LSBs */
val = probe_read_bits(32);
bit = probe_read_bits(1);
parity = __builtin_popcount(val);
if ((parity ^ bit) & 1U) {
/* Parity error */
ack = DAP_TRANSFER_ERROR;
}
if (data)
*data = val;
probe_debug("Read %02x ack %02x 0x%08x parity %01x\n",
prq, ack, val, bit);
/* Turnaround for line idle */
probe_hiz_clocks(DAP_Data.swd_conf.turnaround);
} else {
/* Turnaround for write */
probe_hiz_clocks(DAP_Data.swd_conf.turnaround);
/* Write WDATA[0:31] */
val = *data;
probe_write_bits(32, val);
parity = __builtin_popcount(val);
/* Write Parity Bit */
probe_write_bits(1, parity & 0x1);
probe_debug("write %02x ack %02x 0x%08x parity %01x\n",
prq, ack, val, parity);
}
/* Capture Timestamp */
if (request & DAP_TRANSFER_TIMESTAMP) {
DAP_Data.timestamp = time_us_32();
}
/* Idle cycles - drive 0 for N clocks */
if (DAP_Data.transfer.idle_cycles) {
for (n = DAP_Data.transfer.idle_cycles; n; ) {
if (n > 256) {
probe_write_bits(256, 0);
n -= 256;
} else {
probe_write_bits(n, 0);
n -= n;
}
}
}
return ((uint8_t)ack);
}
if ((ack == DAP_TRANSFER_WAIT) || (ack == DAP_TRANSFER_FAULT)) {
if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) != 0U)) {
/* Dummy Read RDATA[0:31] + Parity */
probe_read_bits(33);
}
probe_hiz_clocks(DAP_Data.swd_conf.turnaround);
if (DAP_Data.swd_conf.data_phase && ((request & DAP_TRANSFER_RnW) == 0U)) {
/* Dummy Write WDATA[0:31] + Parity */
probe_write_bits(32, 0);
probe_write_bits(1, 0);
}
return ((uint8_t)ack);
}
/* Protocol error */
n = DAP_Data.swd_conf.turnaround + 32U + 1U;
/* Back off data phase */
probe_read_bits(n);
return ((uint8_t)ack);
}
#endif /* (DAP_SWD != 0) */