blob: d581a9719bd5d9bca0d5c8255d98a92e9efb289a [file] [log] [blame]
/* ----------------------------------------------------------------------------
* ATMEL Microcontroller Software Support
* ----------------------------------------------------------------------------
* Copyright (c) 2013, Atmel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the disclaimer below.
*
* Atmel's name may not be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "chip.h"
#define MATRIX_AXIMX 1
#define MATRIX_H64MX 2
#define MATRIX_H32MX 3
#define SECURITY_TYPE_AS 1
#define SECURITY_TYPE_NS 2
#define SECURITY_TYPE_PS 3
struct peri_security {
unsigned int peri_id;
unsigned int matrix;
unsigned int security_type;
};
static const struct peri_security peri_security_array[] = {
/* SAIC */
{
.peri_id = ID_FIQ,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* SYS */
{
.peri_id = ID_SYS,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* ARM */
{
.peri_id = ID_ARM,
.matrix = MATRIX_H64MX,
.security_type = SECURITY_TYPE_AS,
},
/* PIT */
{
.peri_id = ID_PIT,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* WDT */
{
.peri_id = ID_WDT,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* PIOD */
{
.peri_id = ID_PIOD,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* USART0 */
{
.peri_id = ID_USART0,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* USART1 */
{
.peri_id = ID_USART1,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* XDMAC0 */
{
.peri_id = ID_XDMAC0,
.matrix = MATRIX_H64MX,
.security_type = SECURITY_TYPE_AS,
},
/* ICM */
{
.peri_id = ID_ICM,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* PKCC */
{
.peri_id = ID_PKCC,
.matrix = MATRIX_H64MX,
.security_type = SECURITY_TYPE_AS,
},
/* AES */
{
.peri_id = ID_AES,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* AESB */
{
.peri_id = ID_AESB,
.matrix = MATRIX_H64MX,
.security_type = SECURITY_TYPE_AS,
},
/* TDES */
{
.peri_id = ID_TDES,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* SHA */
{
.peri_id = ID_SHA,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* MPDDRC */
{
.peri_id = ID_MPDDRC,
.matrix = MATRIX_H64MX,
.security_type = SECURITY_TYPE_AS,
},
/* MATRIX1 */
{
.peri_id = ID_MATRIX1,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* MATRIX0 */
{
.peri_id = ID_MATRIX0,
.matrix = MATRIX_H64MX,
.security_type = SECURITY_TYPE_AS,
},
/* VDEC */
{
.peri_id = ID_VDEC,
.matrix = MATRIX_H64MX,
.security_type = SECURITY_TYPE_PS,
},
/* HSMC */
{
.peri_id = ID_HSMC,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* PIOA*/
{
.peri_id = ID_PIOA,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* PIOB*/
{
.peri_id = ID_PIOB,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* PIOC*/
{
.peri_id = ID_PIOC,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* PIOE*/
{
.peri_id = ID_PIOE,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* UART0 */
{
.peri_id = ID_UART0,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* UART1 */
{
.peri_id = ID_UART1,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* USART2 */
{
.peri_id = ID_USART2,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* USART3 */
{
.peri_id = ID_USART3,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* USART4 */
{
.peri_id = ID_USART4,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* TWI0 */
{
.peri_id = ID_TWI0,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* TWI1 */
{
.peri_id = ID_TWI1,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* TWI2 */
{
.peri_id = ID_TWI2,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* HSMCI0 */
{
.peri_id = ID_HSMCI0,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* HSMCI1 */
{
.peri_id = ID_HSMCI1,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* SPI0 */
{
.peri_id = ID_SPI0,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* SPI1 */
{
.peri_id = ID_SPI1,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* SPI2 */
{
.peri_id = ID_SPI2,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* TC0 */
{
.peri_id = ID_TC0,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* TC1 */
{
.peri_id = ID_TC1,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* TC2 */
{
.peri_id = ID_TC2,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* PWM */
{
.peri_id = ID_PWM,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* ADC */
{
.peri_id = ID_ADC,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* DBGU */
{
.peri_id = ID_DBGU,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* UHPHS */
{
.peri_id = ID_UHPHS,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* UDPHS */
{
.peri_id = ID_UDPHS,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* SSC0 */
{
.peri_id = ID_SSC0,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* SSC1 */
{
.peri_id = ID_SSC1,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* XDMAC1 */
{
.peri_id = ID_XDMAC1,
.matrix = MATRIX_H64MX,
.security_type = SECURITY_TYPE_NS,
},
/* LCDC */
{
.peri_id = ID_LCDC,
.matrix = MATRIX_H64MX,
.security_type = SECURITY_TYPE_PS,
},
/* ISI */
{
.peri_id = ID_ISI,
.matrix = MATRIX_H64MX,
.security_type = SECURITY_TYPE_PS,
},
/* TRNG */
{
.peri_id = ID_TRNG,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* GMAC */
{
.peri_id = ID_GMAC0,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* IRQ */
{
.peri_id = ID_IRQ,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_NS,
},
/* SFC */
{
.peri_id = ID_SFC,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* SECURAM */
{
.peri_id = ID_SECURAM,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* SMD */
{
.peri_id = ID_SMD,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* TWI3 */
{
.peri_id = ID_TWI3,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_PS,
},
/* CATB */
{
.peri_id = ID_CATB,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* SFR */
{
.peri_id = ID_SFR,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* AIC */
{
.peri_id = ID_AIC,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_NS,
},
/* SAIC */
{
.peri_id = ID_SAIC,
.matrix = MATRIX_H32MX,
.security_type = SECURITY_TYPE_AS,
},
/* L2CC */
{
.peri_id = ID_L2CC,
.matrix = MATRIX_H64MX,
.security_type = SECURITY_TYPE_AS,
},
};
void matrix_configure_slave_security(Matrix *matrix_base,
unsigned int slave,
unsigned int srtop_setting,
unsigned int srsplit_setting,
unsigned int ssr_setting)
{
matrix_base->MATRIX_SSR[slave] = ssr_setting;
matrix_base->MATRIX_SRTSR[slave] = srtop_setting;
matrix_base->MATRIX_SASSR[slave] = srsplit_setting;
}
static struct peri_security *get_peri_security(unsigned int peri_id)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(peri_security_array); i++) {
if (peri_id == peri_security_array[i].peri_id)
break;
}
return ((i == ARRAY_SIZE(peri_security_array))) ?
NULL : (struct peri_security *)&peri_security_array[i];
}
static int matrix_set_peri_security(unsigned int matrix, unsigned peri_id)
{
Matrix *base;
uint32_t spselr;
uint32_t idx;
uint32_t bit;
idx = peri_id / 32;
if (idx > 3)
return -1;
bit = (0x01 << (peri_id % 32));
if (matrix == MATRIX_H32MX)
base = MATRIX1;
else if (matrix == MATRIX_H64MX)
base = MATRIX0;
else
return -1;
spselr = base->MATRIX_SPSELR[idx];
spselr |= bit;
base->MATRIX_SPSELR[idx] = spselr;
return 0;
}
int matrix_configure_peri_security(unsigned int *peri_id_array,
unsigned int size)
{
unsigned int i;
unsigned int *peri_id_p;
unsigned int matrix;
unsigned int peri_id;
struct peri_security *periperal_sec;
int ret;
if ((peri_id_array == NULL) || (size == 0))
return -1;
peri_id_p = peri_id_array;
for (i = 0; i < size; i++) {
periperal_sec = get_peri_security(*peri_id_p);
if (periperal_sec == NULL)
return -1;
if (periperal_sec->security_type != SECURITY_TYPE_PS)
return -1;
matrix = periperal_sec->matrix;
peri_id = *peri_id_p;
ret = matrix_set_peri_security(matrix, peri_id);
if (ret)
return -1;
peri_id_p++;
}
return 0;
}
/*
* is_peripheral_secure - tell if the peripheral is in secure mode
* @periph_id: the peripheral id that is checked
*
* Check security of a particular peripheral by providing its ID.
* Note that a wrong preripheral ID leads to the "true" return code.
*/
int is_peripheral_secure(unsigned int periph_id)
{
struct peri_security *periperal_sec;
uint32_t matrix;
Matrix *base;
uint32_t mask;
if ((periph_id > ID_FIQ) && (periph_id < ID_PERIPH_COUNT)) {
/* special cases here */
if ((periph_id == ID_IRQ)
|| (periph_id == ID_AIC)
|| (periph_id == ID_XDMAC1))
return 0;
periperal_sec = get_peri_security(periph_id);
if (periperal_sec == NULL)
return -1;
matrix = periperal_sec->matrix;
if (matrix == MATRIX_H32MX)
base = MATRIX1;
else if (matrix == MATRIX_H64MX)
base = MATRIX0;
else
return -1;
mask = 1 << (periph_id % 32);
if (base->MATRIX_SPSELR[periph_id / 32] & mask)
return 0;
}
return 1;
}
int is_sys_clk_secure(unsigned int sys_mask)
{
unsigned int periph_id = sys_mask_to_per_id(sys_mask);
return is_peripheral_secure(periph_id);
}
int is_usb_hs_secure(void)
{
return (is_peripheral_secure(ID_UHPHS)
|| is_peripheral_secure(ID_UDPHS));
}
int is_usb_host_secure(void)
{
return is_peripheral_secure(ID_UHPHS);
}
int is_switching_clock_forbiden(unsigned int periph_id, unsigned int is_on, unsigned int *silent)
{
/* disable console clock : forbiden */
if ((periph_id == ID_USART3) && (is_on == 0)) {
/* keep it silent */
*silent = 1;
return 1;
} else {
return 0;
}
}
static int matrix_configure_slave(void)
{
unsigned int ddr_port;
unsigned int ssr_setting, sasplit_setting, srtop_setting;
/*
* Matrix 0 (H64MX)
*/
/* 0: Bridge from H64MX to AXIMX */
srtop_setting = 0xffffffff;
sasplit_setting = 0xffffffff;
ssr_setting = 0xffffffff;
matrix_configure_slave_security(MATRIX0,
H64MX_SLAVE_BRIDGE_TO_AXIMX,
srtop_setting,
sasplit_setting,
ssr_setting);
/* 1: H64MX Peripheral Bridge: Non-Secure */
srtop_setting = 0xffffffff;
sasplit_setting = 0xffffffff;
ssr_setting = 0xffffffff;
matrix_configure_slave_security(MATRIX0,
H64MX_SLAVE_PERI_BRIDGE,
srtop_setting,
sasplit_setting,
ssr_setting);
/* 2: Video Decoder 128K: Non-Secure */
srtop_setting = 0xffffffff;
sasplit_setting = 0xffffffff;
ssr_setting = 0xffffffff;
matrix_configure_slave_security(MATRIX0,
H64MX_SLAVE_VIDEO_DECODER,
srtop_setting,
sasplit_setting,
ssr_setting);
/* 4 ~ 10 DDR2 Port1 ~ 7: Non-Secure */
srtop_setting = MATRIX_SRTOP(0, MATRIX_SRTOP_VALUE_128M);
sasplit_setting = (MATRIX_SASPLIT(0, MATRIX_SASPLIT_VALUE_128M)
| MATRIX_SASPLIT(1, MATRIX_SASPLIT_VALUE_128M)
| MATRIX_SASPLIT(2, MATRIX_SASPLIT_VALUE_128M)
| MATRIX_SASPLIT(3, MATRIX_SASPLIT_VALUE_128M));
ssr_setting = (MATRIX_LANSECH_NS(0)
| MATRIX_LANSECH_NS(1)
| MATRIX_LANSECH_NS(2)
| MATRIX_LANSECH_NS(3)
| MATRIX_RDNSECH_NS(0)
| MATRIX_RDNSECH_NS(1)
| MATRIX_RDNSECH_NS(2)
| MATRIX_RDNSECH_NS(3)
| MATRIX_WRNSECH_NS(0)
| MATRIX_WRNSECH_NS(1)
| MATRIX_WRNSECH_NS(2)
| MATRIX_WRNSECH_NS(3));
/* DDR port 0 not used from NWd */
for (ddr_port = 1; ddr_port < 8; ddr_port++) {
matrix_configure_slave_security(MATRIX0,
(H64MX_SLAVE_DDR2_PORT_0 + ddr_port),
srtop_setting,
sasplit_setting,
ssr_setting);
}
/* 11: Internal SRAM 128K
* TOP0 is set to 128K
* SPLIT0 is set to 64K
* LANSECH0 is set to 0, the low area of region 0 is the Securable one
* RDNSECH0 is set to 0, region 0 Securable area is secured for reads.
* WRNSECH0 is set to 0, region 0 Securable area is secured for writes
*/
srtop_setting = MATRIX_SRTOP(0, MATRIX_SRTOP_VALUE_128K);
sasplit_setting = MATRIX_SASPLIT(0, MATRIX_SASPLIT_VALUE_64K);
ssr_setting = (MATRIX_LANSECH_S(0)
| MATRIX_RDNSECH_S(0)
| MATRIX_WRNSECH_S(0));
matrix_configure_slave_security(MATRIX0,
H64MX_SLAVE_INTERNAL_SRAM,
srtop_setting,
sasplit_setting,
ssr_setting);
/* 12: Bridge from H64MX to H32MX: Non-Secure */
srtop_setting = 0xffffffff;
sasplit_setting = 0xffffffff;
ssr_setting = 0xffffffff;
matrix_configure_slave_security(MATRIX0,
H64MX_SLAVE_BRIDGE_TO_H32MX,
srtop_setting,
sasplit_setting,
ssr_setting);
/*
* Matrix 1 (H32MX)
*/
/* 0: Bridge from H32MX to H64MX */
srtop_setting = 0xffffffff;
sasplit_setting = 0xffffffff;
ssr_setting = 0xffffffff;
matrix_configure_slave_security(MATRIX1,
H32MX_BRIDGE_TO_H64MX,
srtop_setting,
sasplit_setting,
ssr_setting);
/* 1: H32MX Peripheral Bridge 0 */
srtop_setting = 0xffffffff;
sasplit_setting = 0xffffffff;
ssr_setting = 0xffffffff;
matrix_configure_slave_security(MATRIX1,
H32MX_PERI_BRIDGE_0,
srtop_setting,
sasplit_setting,
ssr_setting);
/* 2: H32MX Peripheral Bridge 1 */
srtop_setting = 0xffffffff;
sasplit_setting = 0xffffffff;
ssr_setting = 0xffffffff;
matrix_configure_slave_security(MATRIX1,
H32MX_PERI_BRIDGE_1,
srtop_setting,
sasplit_setting,
ssr_setting);
/* 3: External Bus Interface: Non-Secure */
srtop_setting = 0xffffffff;
sasplit_setting = 0xffffffff;
ssr_setting = 0xffffffff;
matrix_configure_slave_security(MATRIX1,
H32MX_EXTERNAL_EBI,
srtop_setting,
sasplit_setting,
ssr_setting);
/* 4: NFC SRAM (4K): Non-Secure */
srtop_setting = 0xffffffff;
sasplit_setting = 0xffffffff;
ssr_setting = 0xffffffff;
matrix_configure_slave_security(MATRIX1,
H32MX_NFC_SRAM,
srtop_setting,
sasplit_setting,
ssr_setting);
/* 5: DPHS RAM(1M), UHP OHCI (1M), UHP EHCI (1M): Non-Secure */
srtop_setting = 0xffffffff;
sasplit_setting = 0xffffffff;
ssr_setting = 0xffffffff;
matrix_configure_slave_security(MATRIX1,
H32MX_USB,
srtop_setting,
sasplit_setting,
ssr_setting);
/* 6: Soft Modem (1M): Non-Secure */
srtop_setting = 0xffffffff;
sasplit_setting = 0xffffffff;
ssr_setting = 0xffffffff;
matrix_configure_slave_security(MATRIX1,
H32MX_SMD,
srtop_setting,
sasplit_setting,
ssr_setting);
return 0;
}
static unsigned int security_ps_peri_id[] = {
ID_VDEC,
ID_PIOA,
ID_PIOB,
ID_PIOC,
ID_PIOE,
ID_USART2,
ID_USART3,
ID_USART4,
ID_TWI0,
ID_TWI2,
ID_HSMCI0,
ID_HSMCI1,
ID_TC0,
ID_TC1,
ID_UHPHS,
ID_UDPHS,
ID_LCDC,
ID_GMAC0,
ID_SPI0,
ID_SPI1,
ID_SMD,
};
static int matrix_config_periheral(void)
{
unsigned int *peri_id = security_ps_peri_id;
unsigned int array_size = sizeof(security_ps_peri_id) / sizeof(unsigned int);
int ret;
ret = matrix_configure_peri_security(peri_id, array_size);
if (ret)
return -1;
return 0;
}
int matrix_init(void)
{
int ret;
/* Disable write protection */
MATRIX0->MATRIX_WPMR = MATRIX_WPMR_WPKEY(MATRIX_KEY_VAL);
MATRIX1->MATRIX_WPMR = MATRIX_WPMR_WPKEY(MATRIX_KEY_VAL);
ret = matrix_configure_slave();
if (ret)
return -1;
ret = matrix_config_periheral();
if (ret)
return -1;
return 0;
}