drivers: samsung: Introduce exynos-speedy

Speedy is a serial communication bus that is typically used in
Exynos SoC devices for communicating with a PMIC. Implement
basic support for it, as well as add its first user - s2mps17 in
board-dreamlte.

Also, while at it, implement a simple readl function.

Signed-off-by: Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>
This commit is contained in:
Ivaylo Ivanov 2024-10-28 15:00:03 +02:00
parent 8b562d28fa
commit eba7aec49d
6 changed files with 331 additions and 5 deletions

View File

@ -3,11 +3,20 @@
* Copyright (c) 2022, Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>
*/
#include <board.h>
#include <stdint.h>
#include <string.h>
#include <drivers/framework.h>
#include <lib/simplefb.h>
#include <lib/debug.h>
#include <soc/exynos8895.h>
#include <drivers/samsung/exynos-speedy.h>
#define DECON_F_BASE 0x12860000
#define HW_SW_TRIG_CONTROL 0x70
/* DECON Register MAP */
#define HW_SW_TRIG_CONTROL 0x70
/* MMIO MAP */
#define DECON_F_BASE 0x12860000
#define SPEEDY_BASE 0x15b50000
void init_board_funcs(void *board)
{
@ -23,6 +32,94 @@ void init_board_funcs(void *board)
board_restruct->name = "DREAMLTE";
}
enum s2mps17_ldos {
S2MPS17_LDO2,
S2MPS17_LDO34,
S2MPS17_LDO35,
S2MPS17_END
};
struct s2mps17_data {
uint8_t ldo_ctrl_value;
uint32_t ldo_voltage_value;
};
/* Slave addr = 0xCC */
/* S2MPS17 Register MAP */
#define S2MPS17_PMIC_REG_L2CTRL 0x3e
#define S2MPS17_PMIC_REG_L34CTRL 0x5f
#define S2MPS17_PMIC_REG_L35CTRL 0x60
#define S2MPS17_BUCK_MIN1 300000
#define S2MPS17_BUCK_MIN2 600000
#define S2MPS17_LDO_MIN1 700000
#define S2MPS17_LDO_MIN2 400000
#define S2MPS17_LDO_MIN3 1800000
#define S2MPS17_LDO_MIN4 300000
#define S2MPS17_LDO_STEP1 12500
#define S2MPS17_LDO_STEP2 25000
#define S2MPS17_LDO_VSEL_MASK 0x3f
#define S2MPS17_BUCK_VSEL_MASK 0xff
#define S2MPS17_ENABLE_MASK (3 << 6)
#define S2MPS17_LDO2_VOLTAGE 2800000
#define S2MPS17_LDO34_VOLTAGE 1850000
#define S2MPS17_LDO35_VOLTAGE 3000000
#define _LDO(macro) S2MPS17_LDO##macro
#define _REG(ctrl) S2MPS17_PMIC_REG##ctrl
#define _ldo_ops(num) s2mps17_ldo_ops##num
#define _TIME(macro) S2MPS17_ENABLE_TIME##macro
static void s2mps17_setup(void)
{
int ret;
struct speedy_transaction s2mps17;
/* S2MPS17 configuration */
s2mps17.base = SPEEDY_BASE;
s2mps17.slave = 1;
/*
* Define LDO control register values
* Calculation formula: enable mask + (voltage to set - minimal voltage) / step value
*/
const struct s2mps17_data data[] = {
{
S2MPS17_PMIC_REG_L2CTRL, // vqmmc
S2MPS17_ENABLE_MASK + (S2MPS17_LDO2_VOLTAGE -
S2MPS17_LDO_MIN3) / S2MPS17_LDO_STEP2
}, {
S2MPS17_PMIC_REG_L34CTRL, // tsp_io
S2MPS17_ENABLE_MASK + (S2MPS17_LDO34_VOLTAGE -
S2MPS17_LDO_MIN1) / S2MPS17_LDO_STEP2
}, {
S2MPS17_PMIC_REG_L35CTRL, // tsp_avdd
S2MPS17_ENABLE_MASK + (S2MPS17_LDO35_VOLTAGE -
S2MPS17_LDO_MIN3) / S2MPS17_LDO_STEP2
}
};
/* Go ahead and enable the LDOs */
for (int i = 0; i < S2MPS17_END; i++) {
s2mps17.offset = data[i].ldo_ctrl_value;
s2mps17.val = data[i].ldo_voltage_value;
ret = speedy_write(&s2mps17);
if (ret)
goto handle_err;
}
return;
handle_err:
printk(KERN_ERR, "s2mps17: err\n");
return;
}
// Early initialization
int board_init(void)
{
@ -34,6 +131,8 @@ int board_init(void)
// Late initialization
int board_late_init(void)
{
s2mps17_setup();
return 0;
}

View File

@ -1,2 +1,3 @@
# the drivers registration framework
lib-y += framework.o
lib-y += samsung/exynos-speedy.o

View File

@ -0,0 +1,196 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2024, Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>
* Copyright (c) 2024 Markuss Broks <markuss.broks@gmail.com>
*/
#include <stdint.h>
#include <string.h>
#include <drivers/framework.h>
#include <drivers/samsung/exynos-speedy.h>
/* SPEEDY Register MAP */
#define SPEEDY_CTRL 0x000
#define SPEEDY_FIFO_CTRL 0x004
#define SPEEDY_CMD 0x008
#define SPEEDY_INT_ENABLE 0x00c
#define SPEEDY_INT_STATUS 0x010
#define SPEEDY_FIFO_STATUS 0x030
#define SPEEDY_TX_DATA 0x034
#define SPEEDY_RX_DATA 0x038
#define SPEEDY_PACKET_GAP_TIME 0x044
#define SPEEDY_TIMEOUT_COUNT 0x048
#define SPEEDY_FIFO_DEBUG 0x100
#define SPEEDY_CTRL_STATUS 0x104
/* SPEEDY_CTRL Register bits */
#define SPEEDY_ENABLE (1 << 0)
#define SPEEDY_TIMEOUT_CMD_DISABLE (1 << 1)
#define SPEEDY_TIMEOUT_STANDBY_DISABLE (1 << 2)
#define SPEEDY_TIMEOUT_DATA_DISABLE (1 << 3)
#define SPEEDY_ALWAYS_PULLUP_EN (1 << 7)
#define SPEEDY_DATA_WIDTH_8BIT (0 << 8)
#define SPEEDY_REMOTE_RESET_REQ (1 << 30)
#define SPEEDY_SW_RST (1 << 31)
/* SPEEDY_FIFO_CTRL Register bits */
#define SPEEDY_RX_TRIGGER_LEVEL(x) ((x) << 0)
#define SPEEDY_TX_TRIGGER_LEVEL(x) ((x) << 8)
#define SPEEDY_FIFO_DEBUG_INDEX (0 << 24)
#define SPEEDY_FIFO_RESET (1 << 31)
/* SPEEDY_CMD Register bits */
#define SPEEDY_BURST_LENGTH(x) ((x) << 0)
#define SPEEDY_BURST_FIXED (0 << 5)
#define SPEEDY_BURST_INCR (1 << 5)
#define SPEEDY_BURST_EXTENSION (2 << 5)
#define SPEEDY_ACCESS_BURST (0 << 19)
#define SPEEDY_ACCESS_RANDOM (1 << 19)
#define SPEEDY_DIRECTION_READ (0 << 20)
#define SPEEDY_DIRECTION_WRITE (1 << 20)
/* SPEEDY_INT_ENABLE Register bits */
#define SPEEDY_TRANSFER_DONE_EN (1 << 0)
#define SPEEDY_TIMEOUT_CMD_EN (1 << 1)
#define SPEEDY_TIMEOUT_STANDBY_EN (1 << 2)
#define SPEEDY_TIMEOUT_DATA_EN (1 << 3)
#define SPEEDY_FIFO_RX_ALMOST_FULL_EN (1 << 8)
#define SPEEDY_FIFO_TX_ALMOST_EMPTY_EN (1 << 4)
#define SPEEDY_RX_FIFO_INT_TRAILER_EN (1 << 9)
#define SPEEDY_RX_MODEBIT_ERR_EN (1 << 16)
#define SPEEDY_RX_GLITCH_ERR_EN (1 << 17)
#define SPEEDY_RX_ENDBIT_ERR_EN (1 << 18)
#define SPEEDY_TX_LINE_BUSY_ERR_EN (1 << 20)
#define SPEEDY_TX_STOPBIT_ERR_EN (1 << 21)
#define SPEEDY_REMOTE_RESET_REQ_EN (1 << 31)
/* SPEEDY_INT_STATUS Register bits */
#define SPEEDY_TRANSFER_DONE (1 << 0)
#define SPEEDY_TIMEOUT_CMD (1 << 1)
#define SPEEDY_TIMEOUT_STANDBY (1 << 2)
#define SPEEDY_TIMEOUT_DATA (1 << 3)
#define SPEEDY_FIFO_TX_ALMOST_EMPTY (1 << 4)
#define SPEEDY_FIFO_RX_ALMOST_FULL (1 << 8)
#define SPEEDY_RX_FIFO_INT_TRAILER (1 << 9)
#define SPEEDY_RX_MODEBIT_ERR (1 << 16)
#define SPEEDY_RX_GLITCH_ERR (1 << 17)
#define SPEEDY_RX_ENDBIT_ERR (1 << 18)
#define SPEEDY_TX_LINE_BUSY_ERR (1 << 20)
#define SPEEDY_TX_STOPBIT_ERR (1 << 21)
#define SPEEDY_REMOTE_RESET_REQ_STAT (1 << 31)
/* SPEEDY_FIFO_STATUS Register bits */
#define SPEEDY_VALID_DATA_CNT (0 << 0)
#define SPEEDY_FIFO_FULL (1 << 5)
#define SPEEDY_FIFO_EMPTY (1 << 6)
/* SPEEDY_PACKET_GAP_TIME Register bits */
#define SPEEDY_FIFO_TX_ALMOST_EMPTY (1 << 4)
#define SPEEDY_FSM_INIT (1 << 1)
#define SPEEDY_FSM_TX_CMD (1 << 2)
#define SPEEDY_FSM_STANDBY (1 << 3)
#define SPEEDY_FSM_DATA (1 << 4)
#define SPEEDY_FSM_TIMEOUT (1 << 5)
#define SPEEDY_FSM_TRANS_DONE (1 << 6)
#define SPEEDY_FSM_IO_RX_STAT_MASK (3 << 7)
#define SPEEDY_FSM_IO_TX_IDLE (1 << 9)
#define SPEEDY_FSM_IO_TX_GET_PACKET (1 << 10)
#define SPEEDY_FSM_IO_TX_PACKET (1 << 11)
#define SPEEDY_FSM_IO_TX_DONE (1 << 12)
#define SPEEDY_RX_LENGTH(n) ((n) << 0)
#define SPEEDY_TX_LENGTH(n) ((n) << 8)
#define SPEEDY_SLAVE(x) ((x & 0xf) << 15)
#define SPEEDY_ADDRESS(x) ((x & 0xff) << 7)
static int speedy_fifo_reset(unsigned long base)
{
writel(SPEEDY_FIFO_RESET, (void *)(base + SPEEDY_FIFO_CTRL));
/* TODO: Implement a proper delay func */
for (volatile int i = 0; i < 1000; i++);
return 0;
}
static int speedy_int_clear(unsigned long base)
{
writel(0xFFFFFFFF, (void *)(base + SPEEDY_INT_STATUS));
/* TODO: Implement a proper delay func */
for (volatile int i = 0; i < 1000; i++);
return 0;
}
int speedy_read(struct speedy_transaction *tr)
{
int ret = speedy_fifo_reset(tr->base);
if (ret)
return ret;
writel(SPEEDY_RX_LENGTH(1) | SPEEDY_TX_LENGTH(1),
(void *)(tr->base + SPEEDY_FIFO_CTRL));
unsigned int cmd = SPEEDY_ACCESS_RANDOM | SPEEDY_DIRECTION_READ |
SPEEDY_SLAVE(tr->slave) | SPEEDY_ADDRESS(tr->offset);
writel(SPEEDY_TRANSFER_DONE | SPEEDY_FIFO_RX_ALMOST_FULL_EN |
SPEEDY_RX_FIFO_INT_TRAILER_EN | SPEEDY_RX_MODEBIT_ERR_EN |
SPEEDY_RX_GLITCH_ERR_EN | SPEEDY_RX_ENDBIT_ERR_EN |
SPEEDY_REMOTE_RESET_REQ_EN,
(void *)(tr->base + SPEEDY_INT_ENABLE));
speedy_int_clear(tr->base);
writel(cmd, (void *)(tr->base + SPEEDY_CMD));
// Poll for completion
int timeout = 500000;
while (timeout-- > 0) {
unsigned int int_status;
int_status = readl((volatile uint32_t *)(tr->base +
SPEEDY_INT_STATUS));
if (int_status & SPEEDY_TRANSFER_DONE) {
speedy_int_clear(tr->base);
tr->val = readl((volatile uint32_t *)
(tr->base + SPEEDY_RX_DATA));
return 0;
}
}
return -1;
}
int speedy_write(struct speedy_transaction *tr)
{
int ret = speedy_fifo_reset(tr->base);
if (ret)
return ret;
writel(SPEEDY_RX_LENGTH(1) | SPEEDY_TX_LENGTH(1),
(void *)(tr->base + SPEEDY_FIFO_CTRL));
unsigned int cmd = SPEEDY_ACCESS_RANDOM | SPEEDY_DIRECTION_WRITE |
SPEEDY_SLAVE(tr->slave) | SPEEDY_ADDRESS(tr->offset);
writel(SPEEDY_TRANSFER_DONE_EN | SPEEDY_FIFO_TX_ALMOST_EMPTY_EN |
SPEEDY_TX_LINE_BUSY_ERR_EN | SPEEDY_TX_STOPBIT_ERR_EN |
SPEEDY_REMOTE_RESET_REQ_EN,
(void *)(tr->base + SPEEDY_INT_ENABLE));
speedy_int_clear(tr->base);
writel(cmd, (void *)(tr->base + SPEEDY_CMD));
writel(tr->val, (void *)(tr->base + SPEEDY_TX_DATA));
// Poll for completion
int timeout = 500000;
while (timeout-- > 0) {
unsigned int int_status;
int_status = readl((volatile uint32_t *)(tr->base +
SPEEDY_INT_STATUS));
if (int_status & SPEEDY_TRANSFER_DONE) {
speedy_int_clear(tr->base);
return 0;
}
}
return -1;
}

View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/*
* Copyright (c) 2024 Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>
* Copyright (c) 2024 Markuss Broks <markuss.broks@gmail.com>
*
* Bindings for exynos-speedy
*/
#ifndef EXYNOS_SPEEDY_H_
#define EXYNOS_SPEEDY_H_
struct speedy_transaction {
unsigned long base;
unsigned int slave;
unsigned int offset;
unsigned int val;
};
extern int speedy_read(struct speedy_transaction *tr);
extern int speedy_write(struct speedy_transaction *tr);
#endif /* EXYNOS_SPEEDY_H_ */

View File

@ -216,3 +216,8 @@ void writel(unsigned int value, void* address)
volatile unsigned int* ptr = (volatile unsigned int*)address;
*ptr = value;
}
uint32_t readl(volatile uint32_t *addr)
{
return *addr;
}

View File

@ -7,6 +7,7 @@
#define STRING_H_
#include "stddef.h"
#include "stdint.h"
#define LBLOCKSIZE (sizeof(long))
#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1))
@ -26,6 +27,7 @@ char *strchr (const char *s, int c);
char *strrchr (const char *s, int c);
long atol (const char *s);
void writel (unsigned int value, void* address);
uint32_t readl(volatile uint32_t *addr);
// C-driven optimized functions
void *memset (void *m, int c, size_t n);
@ -37,6 +39,7 @@ void *memset (void *m, int c, size_t n);
/* How many bytes are copied each iteration of the 4X unrolled loop. */
#define BIGBLOCKSIZE (sizeof(long) << 2)
static void *__optimized_memcpy (void *dst0, const void *src0, size_t len0) __attribute__((unused));
static void *__optimized_memcpy (void *dst0, const void *src0, size_t len0)
{
char *dst = dst0;
@ -63,9 +66,9 @@ static void *__optimized_memcpy (void *dst0, const void *src0, size_t len0)
len0 -= LBLOCKSIZE;
}
/* Pick up any residual with a byte copier. */
dst = (char*)aligned_dst;
src = (char*)aligned_src;
/* Pick up any residual with a byte copier. */
dst = (char*)aligned_dst;
src = (char*)aligned_src;
}
while (len0--)