1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Driver for Khadas System control Microcontroller |
4 | * |
5 | * Copyright (C) 2020 BayLibre SAS |
6 | * |
7 | * Author(s): Neil Armstrong <narmstrong@baylibre.com> |
8 | */ |
9 | #include <linux/bitfield.h> |
10 | #include <linux/i2c.h> |
11 | #include <linux/mfd/core.h> |
12 | #include <linux/mfd/khadas-mcu.h> |
13 | #include <linux/module.h> |
14 | #include <linux/regmap.h> |
15 | |
16 | static bool khadas_mcu_reg_volatile(struct device *dev, unsigned int reg) |
17 | { |
18 | if (reg >= KHADAS_MCU_USER_DATA_0_REG && |
19 | reg < KHADAS_MCU_PWR_OFF_CMD_REG) |
20 | return true; |
21 | |
22 | switch (reg) { |
23 | case KHADAS_MCU_PWR_OFF_CMD_REG: |
24 | case KHADAS_MCU_PASSWD_START_REG: |
25 | case KHADAS_MCU_CHECK_VEN_PASSWD_REG: |
26 | case KHADAS_MCU_CHECK_USER_PASSWD_REG: |
27 | case KHADAS_MCU_WOL_INIT_START_REG: |
28 | case KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG: |
29 | return true; |
30 | default: |
31 | return false; |
32 | } |
33 | } |
34 | |
35 | static bool khadas_mcu_reg_writeable(struct device *dev, unsigned int reg) |
36 | { |
37 | switch (reg) { |
38 | case KHADAS_MCU_PASSWD_VEN_0_REG: |
39 | case KHADAS_MCU_PASSWD_VEN_1_REG: |
40 | case KHADAS_MCU_PASSWD_VEN_2_REG: |
41 | case KHADAS_MCU_PASSWD_VEN_3_REG: |
42 | case KHADAS_MCU_PASSWD_VEN_4_REG: |
43 | case KHADAS_MCU_PASSWD_VEN_5_REG: |
44 | case KHADAS_MCU_MAC_0_REG: |
45 | case KHADAS_MCU_MAC_1_REG: |
46 | case KHADAS_MCU_MAC_2_REG: |
47 | case KHADAS_MCU_MAC_3_REG: |
48 | case KHADAS_MCU_MAC_4_REG: |
49 | case KHADAS_MCU_MAC_5_REG: |
50 | case KHADAS_MCU_USID_0_REG: |
51 | case KHADAS_MCU_USID_1_REG: |
52 | case KHADAS_MCU_USID_2_REG: |
53 | case KHADAS_MCU_USID_3_REG: |
54 | case KHADAS_MCU_USID_4_REG: |
55 | case KHADAS_MCU_USID_5_REG: |
56 | case KHADAS_MCU_VERSION_0_REG: |
57 | case KHADAS_MCU_VERSION_1_REG: |
58 | case KHADAS_MCU_DEVICE_NO_0_REG: |
59 | case KHADAS_MCU_DEVICE_NO_1_REG: |
60 | case KHADAS_MCU_FACTORY_TEST_REG: |
61 | case KHADAS_MCU_SHUTDOWN_NORMAL_STATUS_REG: |
62 | return false; |
63 | default: |
64 | return true; |
65 | } |
66 | } |
67 | |
68 | static const struct regmap_config khadas_mcu_regmap_config = { |
69 | .reg_bits = 8, |
70 | .reg_stride = 1, |
71 | .val_bits = 8, |
72 | .max_register = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG, |
73 | .volatile_reg = khadas_mcu_reg_volatile, |
74 | .writeable_reg = khadas_mcu_reg_writeable, |
75 | .cache_type = REGCACHE_MAPLE, |
76 | }; |
77 | |
78 | static struct mfd_cell khadas_mcu_fan_cells[] = { |
79 | /* VIM1/2 Rev13+ and VIM3 only */ |
80 | { .name = "khadas-mcu-fan-ctrl" , }, |
81 | }; |
82 | |
83 | static struct mfd_cell khadas_mcu_cells[] = { |
84 | { .name = "khadas-mcu-user-mem" , }, |
85 | }; |
86 | |
87 | static int khadas_mcu_probe(struct i2c_client *client) |
88 | { |
89 | struct device *dev = &client->dev; |
90 | struct khadas_mcu *ddata; |
91 | int ret; |
92 | |
93 | ddata = devm_kzalloc(dev, size: sizeof(*ddata), GFP_KERNEL); |
94 | if (!ddata) |
95 | return -ENOMEM; |
96 | |
97 | i2c_set_clientdata(client, data: ddata); |
98 | |
99 | ddata->dev = dev; |
100 | |
101 | ddata->regmap = devm_regmap_init_i2c(client, &khadas_mcu_regmap_config); |
102 | if (IS_ERR(ptr: ddata->regmap)) { |
103 | ret = PTR_ERR(ptr: ddata->regmap); |
104 | dev_err(dev, "Failed to allocate register map: %d\n" , ret); |
105 | return ret; |
106 | } |
107 | |
108 | ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, |
109 | cells: khadas_mcu_cells, |
110 | ARRAY_SIZE(khadas_mcu_cells), |
111 | NULL, irq_base: 0, NULL); |
112 | if (ret) |
113 | return ret; |
114 | |
115 | if (of_property_present(np: dev->of_node, propname: "#cooling-cells" )) |
116 | return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, |
117 | cells: khadas_mcu_fan_cells, |
118 | ARRAY_SIZE(khadas_mcu_fan_cells), |
119 | NULL, irq_base: 0, NULL); |
120 | |
121 | return 0; |
122 | } |
123 | |
124 | #ifdef CONFIG_OF |
125 | static const struct of_device_id khadas_mcu_of_match[] = { |
126 | { .compatible = "khadas,mcu" , }, |
127 | {}, |
128 | }; |
129 | MODULE_DEVICE_TABLE(of, khadas_mcu_of_match); |
130 | #endif |
131 | |
132 | static struct i2c_driver khadas_mcu_driver = { |
133 | .driver = { |
134 | .name = "khadas-mcu-core" , |
135 | .of_match_table = of_match_ptr(khadas_mcu_of_match), |
136 | }, |
137 | .probe = khadas_mcu_probe, |
138 | }; |
139 | module_i2c_driver(khadas_mcu_driver); |
140 | |
141 | MODULE_DESCRIPTION("Khadas MCU core driver" ); |
142 | MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>" ); |
143 | MODULE_LICENSE("GPL v2" ); |
144 | |