Skip to content

Commit a1f6d97

Browse files
Yuval Peresscfriedt
authored andcommitted
drivers: syscon: Add support for multiple regions
1. Add support for multiple syscon entries (as a side effect, this also fixed syscon.c implementations which weren't being linked to their syscon.h counterparts). 2. Add support for different width registers in syscon. 3. Add tests for syscon Signed-off-by: Yuval Peress <[email protected]>
1 parent 805b8ea commit a1f6d97

File tree

12 files changed

+361
-54
lines changed

12 files changed

+361
-54
lines changed

CODEOWNERS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@
194194
/drivers/bbram/* @yperess @sjg20 @jackrosenthal
195195
/drivers/bluetooth/ @joerchan @jhedberg @Vudentz
196196
/drivers/cache/ @carlocaione
197-
/drivers/syscon/ @carlocaione
197+
/drivers/syscon/ @carlocaione @yperess
198198
/drivers/can/ @alexanderwachter
199199
/drivers/can/*mcp2515* @karstenkoenig
200200
/drivers/can/*rcar* @julien-massot

drivers/syscon/Kconfig

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,9 @@ config SYSCON_GENERIC
3030
help
3131
Enable generic SYSCON (System Controller) driver
3232

33-
config SYSCON_GENERIC_INIT_PRIORITY_DEVICE
34-
int "SYSCON (System Controller) generic init device priority"
33+
config SYSCON_INIT_PRIORITY
34+
int "SYSCON (System Controller) driver init priority"
3535
default 50
36-
depends on SYSCON_GENERIC
3736
help
3837
This option controls the priority of the syscon device
3938
initialization. Higher priority ensures that the device is

drivers/syscon/syscon.c

Lines changed: 73 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -12,106 +12,136 @@
1212

1313
#include <drivers/syscon.h>
1414

15-
static const struct device *syscon_dev;
15+
#include "syscon_common.h"
1616

1717
struct syscon_generic_config {
1818
DEVICE_MMIO_ROM;
19-
};
20-
21-
static struct syscon_generic_config syscon_generic_config_0 = {
22-
DEVICE_MMIO_ROM_INIT(DT_DRV_INST(0)),
19+
uint8_t reg_width;
2320
};
2421

2522
struct syscon_generic_data {
2623
DEVICE_MMIO_RAM;
2724
size_t size;
2825
};
2926

30-
static struct syscon_generic_data syscon_generic_data_0;
31-
32-
uintptr_t syscon_generic_get_base(void)
27+
static int syscon_generic_get_base(const struct device *dev, uintptr_t *addr)
3328
{
34-
if (!syscon_dev) {
29+
if (!dev) {
3530
return -ENODEV;
3631
}
3732

38-
return DEVICE_MMIO_GET(syscon_dev);
39-
}
40-
41-
static int sanitize_reg(uint16_t *reg, size_t reg_size)
42-
{
43-
/* Avoid unaligned readings */
44-
*reg = ROUND_DOWN(*reg, sizeof(uint32_t));
45-
46-
/* Check for out-of-bounds readings */
47-
if (*reg >= reg_size) {
48-
return -EINVAL;
49-
}
50-
33+
*addr = DEVICE_MMIO_GET(dev);
5134
return 0;
5235
}
5336

54-
int syscon_generic_read_reg(uint16_t reg, uint32_t *val)
37+
static int syscon_generic_read_reg(const struct device *dev, uint16_t reg, uint32_t *val)
5538
{
39+
const struct syscon_generic_config *config;
5640
struct syscon_generic_data *data;
5741
uintptr_t base_address;
5842

59-
if (!syscon_dev) {
43+
if (!dev) {
6044
return -ENODEV;
6145
}
6246

63-
data = syscon_dev->data;
47+
data = dev->data;
48+
config = dev->config;
6449

6550
if (!val) {
6651
return -EINVAL;
6752
}
6853

69-
if (sanitize_reg(&reg, data->size)) {
54+
if (syscon_sanitize_reg(&reg, data->size, config->reg_width)) {
7055
return -EINVAL;
7156
}
7257

73-
base_address = DEVICE_MMIO_GET(syscon_dev);
74-
75-
*val = sys_read32(base_address + reg);
58+
base_address = DEVICE_MMIO_GET(dev);
59+
60+
switch (config->reg_width) {
61+
case 1:
62+
*val = sys_read8(base_address + reg);
63+
break;
64+
case 2:
65+
*val = sys_read16(base_address + reg);
66+
break;
67+
case 4:
68+
*val = sys_read32(base_address + reg);
69+
break;
70+
default:
71+
return -EINVAL;
72+
}
7673

7774
return 0;
7875
}
7976

80-
int syscon_generic_write_reg(uint16_t reg, uint32_t val)
77+
static int syscon_generic_write_reg(const struct device *dev, uint16_t reg, uint32_t val)
8178
{
79+
const struct syscon_generic_config *config;
8280
struct syscon_generic_data *data;
8381
uintptr_t base_address;
8482

85-
if (!syscon_dev) {
83+
if (!dev) {
8684
return -ENODEV;
8785
}
8886

89-
data = syscon_dev->data;
87+
data = dev->data;
88+
config = dev->config;
9089

91-
if (sanitize_reg(&reg, data->size)) {
90+
if (syscon_sanitize_reg(&reg, data->size, config->reg_width)) {
9291
return -EINVAL;
9392
}
9493

95-
base_address = DEVICE_MMIO_GET(syscon_dev);
96-
97-
sys_write32(val, (base_address + reg));
94+
base_address = DEVICE_MMIO_GET(dev);
95+
96+
switch (config->reg_width) {
97+
case 1:
98+
sys_write8(val, (base_address + reg));
99+
break;
100+
case 2:
101+
sys_write16(val, (base_address + reg));
102+
break;
103+
case 4:
104+
sys_write32(val, (base_address + reg));
105+
break;
106+
default:
107+
return -EINVAL;
108+
}
98109

99110
return 0;
100111
}
101112

102-
int syscon_generic_init(const struct device *dev)
113+
static int syscon_generic_get_size(const struct device *dev, size_t *size)
103114
{
104115
struct syscon_generic_data *data = dev->data;
105116

106-
syscon_dev = dev;
117+
*size = data->size;
118+
return 0;
119+
}
107120

108-
DEVICE_MMIO_MAP(syscon_dev, K_MEM_CACHE_NONE);
121+
static const struct syscon_driver_api syscon_generic_driver_api = {
122+
.read = syscon_generic_read_reg,
123+
.write = syscon_generic_write_reg,
124+
.get_base = syscon_generic_get_base,
125+
.get_size = syscon_generic_get_size,
126+
};
109127

110-
data->size = DT_REG_SIZE(DT_DRV_INST(0));
128+
static int syscon_generic_init(const struct device *dev)
129+
{
130+
DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
111131

112132
return 0;
113133
}
114134

115-
DEVICE_DT_INST_DEFINE(0, syscon_generic_init, NULL, &syscon_generic_data_0,
116-
&syscon_generic_config_0, PRE_KERNEL_1,
117-
CONFIG_SYSCON_GENERIC_INIT_PRIORITY_DEVICE, NULL);
135+
#define SYSCON_INIT(inst) \
136+
static const struct syscon_generic_config syscon_generic_config_##inst = { \
137+
DEVICE_MMIO_ROM_INIT(DT_DRV_INST(inst)), \
138+
.reg_width = DT_INST_PROP_OR(inst, reg_io_width, 4), \
139+
}; \
140+
static struct syscon_generic_data syscon_generic_data_##inst = { \
141+
.size = DT_REG_SIZE(DT_DRV_INST(inst)), \
142+
}; \
143+
DEVICE_DT_INST_DEFINE(inst, syscon_generic_init, NULL, &syscon_generic_data_##inst, \
144+
&syscon_generic_config_##inst, PRE_KERNEL_1, \
145+
CONFIG_SYSCON_INIT_PRIORITY, &syscon_generic_driver_api);
146+
147+
DT_INST_FOREACH_STATUS_OKAY(SYSCON_INIT);

drivers/syscon/syscon_common.h

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2021 Google LLC.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef DRIVERS_SYSCON_SYSCON_COMMON_H_
7+
#define DRIVERS_SYSCON_SYSCON_COMMON_H_
8+
9+
#include <sys/util.h>
10+
11+
#ifdef __cplusplus
12+
extern "C" {
13+
#endif
14+
15+
/**
16+
* @brief Align and check register address
17+
*
18+
* @param reg Pointer to the register address in question.
19+
* @param reg_size The size of the syscon register region.
20+
* @param reg_width The width of a single register (in bytes).
21+
* @return 0 if the register read is valid.
22+
* @return -EINVAL is the read is invalid.
23+
*/
24+
static inline int syscon_sanitize_reg(uint16_t *reg, size_t reg_size, uint8_t reg_width)
25+
{
26+
/* Avoid unaligned readings */
27+
*reg = ROUND_DOWN(*reg, reg_width);
28+
29+
/* Check for out-of-bounds readings */
30+
if (*reg >= reg_size) {
31+
return -EINVAL;
32+
}
33+
34+
return 0;
35+
}
36+
37+
#ifdef __cplusplus
38+
}
39+
#endif
40+
41+
#endif /* DRIVERS_SYSCON_SYSCON_COMMON_H_ */

dts/bindings/syscon/syscon.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,7 @@ include: base.yaml
1010
properties:
1111
reg:
1212
required: true
13+
reg-io-width:
14+
type: int
15+
required: false
16+
description: The width of the registers in the syscon region in bytes. Default is 4 bytes.

include/drivers/syscon.h

Lines changed: 100 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,37 +27,128 @@ extern "C" {
2727
#endif
2828

2929
/**
30-
* @brief Get the syscon base address
30+
* API template to get the base address of the syscon region.
3131
*
32-
* This function returns the syscon base address
32+
* @see syscon_get_base
33+
*/
34+
typedef int (*syscon_api_get_base)(const struct device *dev, uintptr_t *addr);
35+
36+
/**
37+
* API template to read a single register.
3338
*
34-
* @return 0 on error, the base address on success
39+
* @see syscon_read_reg
3540
*/
36-
uintptr_t syscon_get_base(void);
41+
typedef int (*syscon_api_read_reg)(const struct device *dev, uint16_t reg, uint32_t *val);
42+
43+
/**
44+
* API template to write a single register.
45+
*
46+
* @see syscon_write_reg
47+
*/
48+
typedef int (*syscon_api_write_reg)(const struct device *dev, uint16_t reg, uint32_t val);
49+
50+
/**
51+
* API template to get the size of the syscon register.
52+
*
53+
* @see syscon_get_size
54+
*/
55+
typedef int (*syscon_api_get_size)(const struct device *dev, size_t *size);
56+
57+
/**
58+
* @brief System Control (syscon) register driver API
59+
*/
60+
__subsystem struct syscon_driver_api {
61+
syscon_api_read_reg read;
62+
syscon_api_write_reg write;
63+
syscon_api_get_base get_base;
64+
syscon_api_get_size get_size;
65+
};
66+
67+
/**
68+
* @brief Get the syscon base address
69+
*
70+
* @param dev The device to get the register size for.
71+
* @param addr Where to write the base address.
72+
* @return 0 When addr was written to.
73+
*/
74+
__syscall int syscon_get_base(const struct device *dev, uintptr_t *addr);
75+
76+
static inline int z_impl_syscon_get_base(const struct device *dev, uintptr_t *addr)
77+
{
78+
const struct syscon_driver_api *api = (const struct syscon_driver_api *)dev->api;
79+
80+
if (api == NULL) {
81+
return -ENOTSUP;
82+
}
83+
84+
return api->get_base(dev, addr);
85+
}
86+
3787

3888
/**
3989
* @brief Read from syscon register
4090
*
4191
* This function reads from a specific register in the syscon area
4292
*
93+
* @param dev The device to get the register size for.
4394
* @param reg The register offset
4495
* @param val The returned value read from the syscon register
4596
*
4697
* @return 0 on success, negative on error
4798
*/
48-
int syscon_read_reg(uint16_t reg, uint32_t *val);
99+
__syscall int syscon_read_reg(const struct device *dev, uint16_t reg, uint32_t *val);
100+
101+
static inline int z_impl_syscon_read_reg(const struct device *dev, uint16_t reg, uint32_t *val)
102+
{
103+
const struct syscon_driver_api *api = (const struct syscon_driver_api *)dev->api;
104+
105+
if (api == NULL) {
106+
return -ENOTSUP;
107+
}
108+
109+
return api->read(dev, reg, val);
110+
}
111+
49112

50113
/**
51114
* @brief Write to syscon register
52115
*
53116
* This function writes to a specific register in the syscon area
54117
*
118+
* @param dev The device to get the register size for.
55119
* @param reg The register offset
56120
* @param val The value to be written in the register
57121
*
58122
* @return 0 on success, negative on error
59123
*/
60-
int syscon_write_reg(uint16_t reg, uint32_t val);
124+
__syscall int syscon_write_reg(const struct device *dev, uint16_t reg, uint32_t val);
125+
126+
static inline int z_impl_syscon_write_reg(const struct device *dev, uint16_t reg, uint32_t val)
127+
{
128+
const struct syscon_driver_api *api = (const struct syscon_driver_api *)dev->api;
129+
130+
if (api == NULL) {
131+
return -ENOTSUP;
132+
}
133+
134+
return api->write(dev, reg, val);
135+
}
136+
137+
/**
138+
* Get the size of the syscon register in bytes.
139+
*
140+
* @param dev The device to get the register size for.
141+
* @param size Pointer to write the size to.
142+
* @return 0 for success.
143+
*/
144+
__syscall int syscon_get_size(const struct device *dev, size_t *size);
145+
146+
static inline int z_impl_syscon_get_size(const struct device *dev, size_t *size)
147+
{
148+
const struct syscon_driver_api *api = (const struct syscon_driver_api *)dev->api;
149+
150+
return api->get_size(dev, size);
151+
}
61152

62153
/**
63154
* @}
@@ -67,4 +158,6 @@ int syscon_write_reg(uint16_t reg, uint32_t val);
67158
}
68159
#endif
69160

70-
#endif /* ZEPHYR_INCLUDE_DRIVERS_SYSCON_H_ */
161+
#include <syscalls/syscon.h>
162+
163+
#endif /* ZEPHYR_INCLUDE_DRIVERS_SYSCON_H_ */

0 commit comments

Comments
 (0)