Skip to content

Commit

Permalink
drivers: samsung: Introduce exynos-speedy
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
ivoszbg committed Oct 28, 2024
1 parent 8b562d2 commit eba7aec
Show file tree
Hide file tree
Showing 6 changed files with 331 additions and 5 deletions.
103 changes: 101 additions & 2 deletions board/samsung/board-dreamlte.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,20 @@
* Copyright (c) 2022, Ivaylo Ivanov <[email protected]>
*/
#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)
{
Expand All @@ -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)
{
Expand All @@ -34,6 +131,8 @@ int board_init(void)
// Late initialization
int board_late_init(void)
{
s2mps17_setup();

return 0;
}

Expand Down
1 change: 1 addition & 0 deletions drivers/Makefile
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# the drivers registration framework
lib-y += framework.o
lib-y += samsung/exynos-speedy.o
196 changes: 196 additions & 0 deletions drivers/samsung/exynos-speedy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2024, Ivaylo Ivanov <[email protected]>
* Copyright (c) 2024 Markuss Broks <[email protected]>
*/
#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;
}
22 changes: 22 additions & 0 deletions include/drivers/samsung/exynos-speedy.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/*
* Copyright (c) 2024 Ivaylo Ivanov <[email protected]>
* Copyright (c) 2024 Markuss Broks <[email protected]>
*
* 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_ */
5 changes: 5 additions & 0 deletions lib/unic/string.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
9 changes: 6 additions & 3 deletions lib/unic/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#define STRING_H_

#include "stddef.h"
#include "stdint.h"

#define LBLOCKSIZE (sizeof(long))
#define UNALIGNED(X) ((long)X & (LBLOCKSIZE - 1))
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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--)
Expand Down

0 comments on commit eba7aec

Please sign in to comment.