Skip to content

Commit

Permalink
soc: samsung: usi: implement support for USIv1 and exynos8895
Browse files Browse the repository at this point in the history
USIv1 IP-core is found on some ARM64 Exynos SoCs (like Exynos8895) and
provides selectable serial protocols (one of: HSI2C0, HSI2C1, HSI2C0_1,
SPI, UART, UART_HSI2C1).

USIv1, unlike USIv2, doesn't have any known register map. Underlying
protocols that it implements have no offset, like with Exynos850.
Desired protocol can be chosen via SW_CONF register from System
Register block of the same domain as USI.

In order to select a particular protocol, the protocol has to be
selected via the System Register. Unlike USIv2, there's no need for
any setup before the given protocol becomes accessible apart from
enabling the APB clock and the protocol operating clock.

Modify the existing driver in order to allow USIv1 instances in
Exynos8895 to probe and set their protocol. While we're at it,
make use of the new mode constants in place of the old ones.

Signed-off-by: Ivaylo Ivanov <ivo.ivanov.ivanov1@gmail.com>
Link: https://lore.kernel.org/r/20250204172803.3425496-4-ivo.ivanov.ivanov1@gmail.com
Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
  • Loading branch information
Ivaylo Ivanov authored and Krzysztof Kozlowski committed Feb 5, 2025
1 parent 11e7777 commit b88cd5c
Showing 1 changed file with 58 additions and 13 deletions.
71 changes: 58 additions & 13 deletions drivers/soc/samsung/exynos-usi.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@

#include <dt-bindings/soc/samsung,exynos-usi.h>

/* USIv1: System Register: SW_CONF register bits */
#define USI_V1_SW_CONF_NONE 0x0
#define USI_V1_SW_CONF_I2C0 0x1
#define USI_V1_SW_CONF_I2C1 0x2
#define USI_V1_SW_CONF_I2C0_1 0x3
#define USI_V1_SW_CONF_SPI 0x4
#define USI_V1_SW_CONF_UART 0x8
#define USI_V1_SW_CONF_UART_I2C1 0xa
#define USI_V1_SW_CONF_MASK (USI_V1_SW_CONF_I2C0 | USI_V1_SW_CONF_I2C1 | \
USI_V1_SW_CONF_I2C0_1 | USI_V1_SW_CONF_SPI | \
USI_V1_SW_CONF_UART | USI_V1_SW_CONF_UART_I2C1)

/* USIv2: System Register: SW_CONF register bits */
#define USI_V2_SW_CONF_NONE 0x0
#define USI_V2_SW_CONF_UART BIT(0)
Expand All @@ -34,7 +46,8 @@
#define USI_OPTION_CLKSTOP_ON BIT(2)

enum exynos_usi_ver {
USI_VER2 = 2,
USI_VER1 = 0,
USI_VER2,
};

struct exynos_usi_variant {
Expand Down Expand Up @@ -66,19 +79,39 @@ struct exynos_usi_mode {
unsigned int val; /* mode register value */
};

static const struct exynos_usi_mode exynos_usi_modes[] = {
[USI_V2_NONE] = { .name = "none", .val = USI_V2_SW_CONF_NONE },
[USI_V2_UART] = { .name = "uart", .val = USI_V2_SW_CONF_UART },
[USI_V2_SPI] = { .name = "spi", .val = USI_V2_SW_CONF_SPI },
[USI_V2_I2C] = { .name = "i2c", .val = USI_V2_SW_CONF_I2C },
#define USI_MODES_MAX (USI_MODE_UART_I2C1 + 1)
static const struct exynos_usi_mode exynos_usi_modes[][USI_MODES_MAX] = {
[USI_VER1] = {
[USI_MODE_NONE] = { .name = "none", .val = USI_V1_SW_CONF_NONE },
[USI_MODE_UART] = { .name = "uart", .val = USI_V1_SW_CONF_UART },
[USI_MODE_SPI] = { .name = "spi", .val = USI_V1_SW_CONF_SPI },
[USI_MODE_I2C] = { .name = "i2c", .val = USI_V1_SW_CONF_I2C0 },
[USI_MODE_I2C1] = { .name = "i2c1", .val = USI_V1_SW_CONF_I2C1 },
[USI_MODE_I2C0_1] = { .name = "i2c0_1", .val = USI_V1_SW_CONF_I2C0_1 },
[USI_MODE_UART_I2C1] = { .name = "uart_i2c1", .val = USI_V1_SW_CONF_UART_I2C1 },
}, [USI_VER2] = {
[USI_MODE_NONE] = { .name = "none", .val = USI_V2_SW_CONF_NONE },
[USI_MODE_UART] = { .name = "uart", .val = USI_V2_SW_CONF_UART },
[USI_MODE_SPI] = { .name = "spi", .val = USI_V2_SW_CONF_SPI },
[USI_MODE_I2C] = { .name = "i2c", .val = USI_V2_SW_CONF_I2C },
},
};

static const char * const exynos850_usi_clk_names[] = { "pclk", "ipclk" };
static const struct exynos_usi_variant exynos850_usi_data = {
.ver = USI_VER2,
.sw_conf_mask = USI_V2_SW_CONF_MASK,
.min_mode = USI_V2_NONE,
.max_mode = USI_V2_I2C,
.min_mode = USI_MODE_NONE,
.max_mode = USI_MODE_I2C,
.num_clks = ARRAY_SIZE(exynos850_usi_clk_names),
.clk_names = exynos850_usi_clk_names,
};

static const struct exynos_usi_variant exynos8895_usi_data = {
.ver = USI_VER1,
.sw_conf_mask = USI_V1_SW_CONF_MASK,
.min_mode = USI_MODE_NONE,
.max_mode = USI_MODE_UART_I2C1,
.num_clks = ARRAY_SIZE(exynos850_usi_clk_names),
.clk_names = exynos850_usi_clk_names,
};
Expand All @@ -87,6 +120,9 @@ static const struct of_device_id exynos_usi_dt_match[] = {
{
.compatible = "samsung,exynos850-usi",
.data = &exynos850_usi_data,
}, {
.compatible = "samsung,exynos8895-usi",
.data = &exynos8895_usi_data,
},
{ } /* sentinel */
};
Expand All @@ -109,14 +145,15 @@ static int exynos_usi_set_sw_conf(struct exynos_usi *usi, size_t mode)
if (mode < usi->data->min_mode || mode > usi->data->max_mode)
return -EINVAL;

val = exynos_usi_modes[mode].val;
val = exynos_usi_modes[usi->data->ver][mode].val;
ret = regmap_update_bits(usi->sysreg, usi->sw_conf,
usi->data->sw_conf_mask, val);
if (ret)
return ret;

usi->mode = mode;
dev_dbg(usi->dev, "protocol: %s\n", exynos_usi_modes[usi->mode].name);
dev_dbg(usi->dev, "protocol: %s\n",
exynos_usi_modes[usi->data->ver][usi->mode].name);

return 0;
}
Expand Down Expand Up @@ -168,10 +205,13 @@ static int exynos_usi_configure(struct exynos_usi *usi)
if (ret)
return ret;

if (usi->data->ver == USI_VER2)
return exynos_usi_enable(usi);
if (usi->data->ver == USI_VER1)
ret = clk_bulk_prepare_enable(usi->data->num_clks,
usi->clks);
else if (usi->data->ver == USI_VER2)
ret = exynos_usi_enable(usi);

return 0;
return ret;
}

static void exynos_usi_unconfigure(void *data)
Expand All @@ -180,6 +220,11 @@ static void exynos_usi_unconfigure(void *data)
u32 val;
int ret;

if (usi->data->ver == USI_VER1) {
clk_bulk_disable_unprepare(usi->data->num_clks, usi->clks);
return;
}

ret = clk_bulk_prepare_enable(usi->data->num_clks, usi->clks);
if (ret)
return;
Expand Down

0 comments on commit b88cd5c

Please sign in to comment.