Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branches 'ib-dt-mfd-input-leds-power-rtc-6.13' and 'ib-mfd-gpio-i2c-watchdog-6.13' into ibs-for-mfd-merged

+1323
+9
MAINTAINERS
··· 5760 5760 F: include/linux/configfs.h 5761 5761 F: samples/configfs/ 5762 5762 5763 + CONGATEC BOARD CONTROLLER MFD DRIVER 5764 + M: Thomas Richard <thomas.richard@bootlin.com> 5765 + S: Maintained 5766 + F: drivers/gpio/gpio-cgbc.c 5767 + F: drivers/i2c/busses/i2c-cgbc.c 5768 + F: drivers/mfd/cgbc-core.c 5769 + F: drivers/watchdog/cgbc_wdt.c 5770 + F: include/linux/mfd/cgbc.h 5771 + 5763 5772 CONSOLE SUBSYSTEM 5764 5773 M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> 5765 5774 S: Supported
+10
drivers/gpio/Kconfig
··· 1287 1287 This driver can also be built as a module. If so, the module 1288 1288 will be called gpio-bd9571mwv. 1289 1289 1290 + config GPIO_CGBC 1291 + tristate "Congatec Board Controller GPIO support" 1292 + depends on MFD_CGBC 1293 + help 1294 + Select this option to enable GPIO support for the Congatec Board 1295 + Controller. 1296 + 1297 + This driver can also be built as a module. If so, the module will be 1298 + called gpio-cgbc. 1299 + 1290 1300 config GPIO_CROS_EC 1291 1301 tristate "ChromeOS EC GPIO support" 1292 1302 depends on CROS_EC
+1
drivers/gpio/Makefile
··· 45 45 obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o 46 46 obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o 47 47 obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o 48 + obj-$(CONFIG_GPIO_CGBC) += gpio-cgbc.o 48 49 obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o 49 50 obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o 50 51 obj-$(CONFIG_GPIO_CROS_EC) += gpio-cros-ec.o
+196
drivers/gpio/gpio-cgbc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Congatec Board Controller GPIO driver 4 + * 5 + * Copyright (C) 2024 Bootlin 6 + * Author: Thomas Richard <thomas.richard@bootlin.com> 7 + */ 8 + 9 + #include <linux/gpio/driver.h> 10 + #include <linux/mfd/cgbc.h> 11 + #include <linux/module.h> 12 + #include <linux/mutex.h> 13 + #include <linux/platform_device.h> 14 + 15 + #define CGBC_GPIO_NGPIO 14 16 + 17 + #define CGBC_GPIO_CMD_GET 0x64 18 + #define CGBC_GPIO_CMD_SET 0x65 19 + #define CGBC_GPIO_CMD_DIR_GET 0x66 20 + #define CGBC_GPIO_CMD_DIR_SET 0x67 21 + 22 + struct cgbc_gpio_data { 23 + struct gpio_chip chip; 24 + struct cgbc_device_data *cgbc; 25 + struct mutex lock; 26 + }; 27 + 28 + static int cgbc_gpio_cmd(struct cgbc_device_data *cgbc, 29 + u8 cmd0, u8 cmd1, u8 cmd2, u8 *value) 30 + { 31 + u8 cmd[3] = {cmd0, cmd1, cmd2}; 32 + 33 + return cgbc_command(cgbc, cmd, sizeof(cmd), value, 1, NULL); 34 + } 35 + 36 + static int cgbc_gpio_get(struct gpio_chip *chip, unsigned int offset) 37 + { 38 + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 39 + struct cgbc_device_data *cgbc = gpio->cgbc; 40 + int ret; 41 + u8 val; 42 + 43 + scoped_guard(mutex, &gpio->lock) 44 + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val); 45 + 46 + offset %= 8; 47 + 48 + if (ret) 49 + return ret; 50 + else 51 + return (int)(val & (u8)BIT(offset)); 52 + } 53 + 54 + static void __cgbc_gpio_set(struct gpio_chip *chip, 55 + unsigned int offset, int value) 56 + { 57 + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 58 + struct cgbc_device_data *cgbc = gpio->cgbc; 59 + u8 val; 60 + int ret; 61 + 62 + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val); 63 + if (ret) 64 + return; 65 + 66 + if (value) 67 + val |= BIT(offset % 8); 68 + else 69 + val &= ~(BIT(offset % 8)); 70 + 71 + cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_SET, (offset > 7) ? 1 : 0, val, &val); 72 + } 73 + 74 + static void cgbc_gpio_set(struct gpio_chip *chip, 75 + unsigned int offset, int value) 76 + { 77 + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 78 + 79 + scoped_guard(mutex, &gpio->lock) 80 + __cgbc_gpio_set(chip, offset, value); 81 + } 82 + 83 + static int cgbc_gpio_direction_set(struct gpio_chip *chip, 84 + unsigned int offset, int direction) 85 + { 86 + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 87 + struct cgbc_device_data *cgbc = gpio->cgbc; 88 + int ret; 89 + u8 val; 90 + 91 + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val); 92 + if (ret) 93 + goto end; 94 + 95 + if (direction == GPIO_LINE_DIRECTION_IN) 96 + val &= ~(BIT(offset % 8)); 97 + else 98 + val |= BIT(offset % 8); 99 + 100 + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_SET, (offset > 7) ? 1 : 0, val, &val); 101 + 102 + end: 103 + return ret; 104 + } 105 + 106 + static int cgbc_gpio_direction_input(struct gpio_chip *chip, 107 + unsigned int offset) 108 + { 109 + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 110 + 111 + guard(mutex)(&gpio->lock); 112 + return cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_IN); 113 + } 114 + 115 + static int cgbc_gpio_direction_output(struct gpio_chip *chip, 116 + unsigned int offset, int value) 117 + { 118 + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 119 + 120 + guard(mutex)(&gpio->lock); 121 + 122 + __cgbc_gpio_set(chip, offset, value); 123 + return cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_OUT); 124 + } 125 + 126 + static int cgbc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) 127 + { 128 + struct cgbc_gpio_data *gpio = gpiochip_get_data(chip); 129 + struct cgbc_device_data *cgbc = gpio->cgbc; 130 + int ret; 131 + u8 val; 132 + 133 + scoped_guard(mutex, &gpio->lock) 134 + ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val); 135 + 136 + if (ret) 137 + return ret; 138 + 139 + if (val & BIT(offset % 8)) 140 + return GPIO_LINE_DIRECTION_OUT; 141 + else 142 + return GPIO_LINE_DIRECTION_IN; 143 + } 144 + 145 + static int cgbc_gpio_probe(struct platform_device *pdev) 146 + { 147 + struct device *dev = &pdev->dev; 148 + struct cgbc_device_data *cgbc = dev_get_drvdata(dev->parent); 149 + struct cgbc_gpio_data *gpio; 150 + struct gpio_chip *chip; 151 + int ret; 152 + 153 + gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); 154 + if (!gpio) 155 + return -ENOMEM; 156 + 157 + gpio->cgbc = cgbc; 158 + 159 + platform_set_drvdata(pdev, gpio); 160 + 161 + chip = &gpio->chip; 162 + chip->label = dev_name(&pdev->dev); 163 + chip->owner = THIS_MODULE; 164 + chip->parent = dev; 165 + chip->base = -1; 166 + chip->direction_input = cgbc_gpio_direction_input; 167 + chip->direction_output = cgbc_gpio_direction_output; 168 + chip->get_direction = cgbc_gpio_get_direction; 169 + chip->get = cgbc_gpio_get; 170 + chip->set = cgbc_gpio_set; 171 + chip->ngpio = CGBC_GPIO_NGPIO; 172 + 173 + ret = devm_mutex_init(dev, &gpio->lock); 174 + if (ret) 175 + return ret; 176 + 177 + ret = devm_gpiochip_add_data(dev, chip, gpio); 178 + if (ret) 179 + return dev_err_probe(dev, ret, "Could not register GPIO chip\n"); 180 + 181 + return 0; 182 + } 183 + 184 + static struct platform_driver cgbc_gpio_driver = { 185 + .driver = { 186 + .name = "cgbc-gpio", 187 + }, 188 + .probe = cgbc_gpio_probe, 189 + }; 190 + 191 + module_platform_driver(cgbc_gpio_driver); 192 + 193 + MODULE_DESCRIPTION("Congatec Board Controller GPIO Driver"); 194 + MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>"); 195 + MODULE_LICENSE("GPL"); 196 + MODULE_ALIAS("platform:cgbc-gpio");
+10
drivers/i2c/busses/Kconfig
··· 535 535 This driver can also be built as a module. If so, the module 536 536 will be called i2c-cbus-gpio. 537 537 538 + config I2C_CGBC 539 + tristate "Congatec I2C Controller" 540 + depends on MFD_CGBC 541 + help 542 + This driver supports the 2 I2C interfaces on the Congatec Board 543 + Controller. 544 + 545 + This driver can also be built as a module. If so, the module will 546 + be called i2c-cgbc.ko. 547 + 538 548 config I2C_CPM 539 549 tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)" 540 550 depends on CPM1 || CPM2
+1
drivers/i2c/busses/Makefile
··· 50 50 obj-$(CONFIG_I2C_BCM_IPROC) += i2c-bcm-iproc.o 51 51 obj-$(CONFIG_I2C_CADENCE) += i2c-cadence.o 52 52 obj-$(CONFIG_I2C_CBUS_GPIO) += i2c-cbus-gpio.o 53 + obj-$(CONFIG_I2C_CGBC) += i2c-cgbc.o 53 54 obj-$(CONFIG_I2C_CPM) += i2c-cpm.o 54 55 obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o 55 56 obj-$(CONFIG_I2C_DESIGNWARE_CORE) += i2c-designware-core.o
+406
drivers/i2c/busses/i2c-cgbc.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Congatec Board Controller I2C busses driver 4 + * 5 + * Copyright (C) 2024 Bootlin 6 + * Author: Thomas Richard <thomas.richard@bootlin.com> 7 + */ 8 + 9 + #include <linux/i2c.h> 10 + #include <linux/iopoll.h> 11 + #include <linux/mfd/cgbc.h> 12 + #include <linux/module.h> 13 + #include <linux/platform_device.h> 14 + 15 + #define CGBC_I2C_PRIMARY_BUS_ID 0 16 + #define CGBC_I2C_PM_BUS_ID 4 17 + 18 + #define CGBC_I2C_CMD_START 0x40 19 + #define CGBC_I2C_CMD_STAT 0x48 20 + #define CGBC_I2C_CMD_DATA 0x50 21 + #define CGBC_I2C_CMD_SPEED 0x58 22 + 23 + #define CGBC_I2C_STAT_IDL 0x00 24 + #define CGBC_I2C_STAT_DAT 0x01 25 + #define CGBC_I2C_STAT_BUSY 0x02 26 + 27 + #define CGBC_I2C_START 0x80 28 + #define CGBC_I2C_STOP 0x40 29 + 30 + #define CGBC_I2C_LAST_ACK 0x80 /* send ACK on last read byte */ 31 + 32 + /* 33 + * Reference code defines 1kHz as min freq and 6.1MHz as max freq. 34 + * But in practice, the board controller limits the frequency to 1MHz, and the 35 + * 1kHz is not functional (minimal working freq is 50kHz). 36 + * So use these values as limits. 37 + */ 38 + #define CGBC_I2C_FREQ_MIN_HZ 50000 /* 50 kHz */ 39 + #define CGBC_I2C_FREQ_MAX_HZ 1000000 /* 1 MHz */ 40 + 41 + #define CGBC_I2C_FREQ_UNIT_1KHZ 0x40 42 + #define CGBC_I2C_FREQ_UNIT_10KHZ 0x80 43 + #define CGBC_I2C_FREQ_UNIT_100KHZ 0xC0 44 + 45 + #define CGBC_I2C_FREQ_UNIT_MASK 0xC0 46 + #define CGBC_I2C_FREQ_VALUE_MASK 0x3F 47 + 48 + #define CGBC_I2C_READ_MAX_LEN 31 49 + #define CGBC_I2C_WRITE_MAX_LEN 32 50 + 51 + #define CGBC_I2C_CMD_HEADER_SIZE 4 52 + #define CGBC_I2C_CMD_SIZE (CGBC_I2C_CMD_HEADER_SIZE + CGBC_I2C_WRITE_MAX_LEN) 53 + 54 + enum cgbc_i2c_state { 55 + CGBC_I2C_STATE_DONE = 0, 56 + CGBC_I2C_STATE_INIT, 57 + CGBC_I2C_STATE_START, 58 + CGBC_I2C_STATE_READ, 59 + CGBC_I2C_STATE_WRITE, 60 + CGBC_I2C_STATE_ERROR, 61 + }; 62 + 63 + struct i2c_algo_cgbc_data { 64 + u8 bus_id; 65 + unsigned long read_maxtime_us; 66 + }; 67 + 68 + struct cgbc_i2c_data { 69 + struct device *dev; 70 + struct cgbc_device_data *cgbc; 71 + struct i2c_adapter adap; 72 + struct i2c_msg *msg; 73 + int nmsgs; 74 + int pos; 75 + enum cgbc_i2c_state state; 76 + }; 77 + 78 + struct cgbc_i2c_transfer { 79 + u8 bus_id; 80 + bool start; 81 + bool stop; 82 + bool last_ack; 83 + u8 read; 84 + u8 write; 85 + u8 addr; 86 + u8 data[CGBC_I2C_WRITE_MAX_LEN]; 87 + }; 88 + 89 + static u8 cgbc_i2c_freq_to_reg(unsigned int bus_frequency) 90 + { 91 + u8 reg; 92 + 93 + if (bus_frequency <= 10000) 94 + reg = CGBC_I2C_FREQ_UNIT_1KHZ | (bus_frequency / 1000); 95 + else if (bus_frequency <= 100000) 96 + reg = CGBC_I2C_FREQ_UNIT_10KHZ | (bus_frequency / 10000); 97 + else 98 + reg = CGBC_I2C_FREQ_UNIT_100KHZ | (bus_frequency / 100000); 99 + 100 + return reg; 101 + } 102 + 103 + static unsigned int cgbc_i2c_reg_to_freq(u8 reg) 104 + { 105 + unsigned int freq = reg & CGBC_I2C_FREQ_VALUE_MASK; 106 + u8 unit = reg & CGBC_I2C_FREQ_UNIT_MASK; 107 + 108 + if (unit == CGBC_I2C_FREQ_UNIT_100KHZ) 109 + return freq * 100000; 110 + else if (unit == CGBC_I2C_FREQ_UNIT_10KHZ) 111 + return freq * 10000; 112 + else 113 + return freq * 1000; 114 + } 115 + 116 + static int cgbc_i2c_get_status(struct i2c_adapter *adap) 117 + { 118 + struct i2c_algo_cgbc_data *algo_data = adap->algo_data; 119 + struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap); 120 + struct cgbc_device_data *cgbc = i2c->cgbc; 121 + u8 cmd = CGBC_I2C_CMD_STAT | algo_data->bus_id; 122 + u8 status; 123 + int ret; 124 + 125 + ret = cgbc_command(cgbc, &cmd, sizeof(cmd), NULL, 0, &status); 126 + if (ret) 127 + return ret; 128 + 129 + return status; 130 + } 131 + 132 + static int cgbc_i2c_set_frequency(struct i2c_adapter *adap, 133 + unsigned int bus_frequency) 134 + { 135 + struct i2c_algo_cgbc_data *algo_data = adap->algo_data; 136 + struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap); 137 + struct cgbc_device_data *cgbc = i2c->cgbc; 138 + u8 cmd[2], data; 139 + int ret; 140 + 141 + if (bus_frequency > CGBC_I2C_FREQ_MAX_HZ || 142 + bus_frequency < CGBC_I2C_FREQ_MIN_HZ) { 143 + dev_info(i2c->dev, "invalid frequency %u, using default\n", bus_frequency); 144 + bus_frequency = I2C_MAX_STANDARD_MODE_FREQ; 145 + } 146 + 147 + cmd[0] = CGBC_I2C_CMD_SPEED | algo_data->bus_id; 148 + cmd[1] = cgbc_i2c_freq_to_reg(bus_frequency); 149 + 150 + ret = cgbc_command(cgbc, &cmd, sizeof(cmd), &data, 1, NULL); 151 + if (ret) 152 + return dev_err_probe(i2c->dev, ret, 153 + "Failed to initialize I2C bus %s", 154 + adap->name); 155 + 156 + cmd[1] = 0x00; 157 + 158 + ret = cgbc_command(cgbc, &cmd, sizeof(cmd), &data, 1, NULL); 159 + if (ret) 160 + return dev_err_probe(i2c->dev, ret, 161 + "Failed to get I2C bus frequency"); 162 + 163 + bus_frequency = cgbc_i2c_reg_to_freq(data); 164 + 165 + dev_dbg(i2c->dev, "%s is running at %d Hz\n", adap->name, bus_frequency); 166 + 167 + /* 168 + * The read_maxtime_us variable represents the maximum time to wait 169 + * for data during a read operation. The maximum amount of data that 170 + * can be read by a command is CGBC_I2C_READ_MAX_LEN. 171 + * Therefore, calculate the max time to properly size the timeout. 172 + */ 173 + algo_data->read_maxtime_us = (BITS_PER_BYTE + 1) * CGBC_I2C_READ_MAX_LEN 174 + * USEC_PER_SEC / bus_frequency; 175 + 176 + return 0; 177 + } 178 + 179 + static unsigned int cgbc_i2c_xfer_to_cmd(struct cgbc_i2c_transfer xfer, u8 *cmd) 180 + { 181 + int i = 0; 182 + 183 + cmd[i++] = CGBC_I2C_CMD_START | xfer.bus_id; 184 + 185 + cmd[i] = (xfer.start) ? CGBC_I2C_START : 0x00; 186 + if (xfer.stop) 187 + cmd[i] |= CGBC_I2C_STOP; 188 + cmd[i++] |= (xfer.start) ? xfer.write + 1 : xfer.write; 189 + 190 + cmd[i++] = (xfer.last_ack) ? (xfer.read | CGBC_I2C_LAST_ACK) : xfer.read; 191 + 192 + if (xfer.start) 193 + cmd[i++] = xfer.addr; 194 + 195 + if (xfer.write > 0) 196 + memcpy(&cmd[i], &xfer.data, xfer.write); 197 + 198 + return i + xfer.write; 199 + } 200 + 201 + static int cgbc_i2c_xfer_msg(struct i2c_adapter *adap) 202 + { 203 + struct i2c_algo_cgbc_data *algo_data = adap->algo_data; 204 + struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap); 205 + struct cgbc_device_data *cgbc = i2c->cgbc; 206 + struct i2c_msg *msg = i2c->msg; 207 + u8 cmd[CGBC_I2C_CMD_SIZE]; 208 + int ret, max_len, len, i; 209 + unsigned int cmd_len; 210 + u8 cmd_data; 211 + 212 + struct cgbc_i2c_transfer xfer = { 213 + .bus_id = algo_data->bus_id, 214 + .addr = i2c_8bit_addr_from_msg(msg), 215 + }; 216 + 217 + if (i2c->state == CGBC_I2C_STATE_DONE) 218 + return 0; 219 + 220 + ret = cgbc_i2c_get_status(adap); 221 + 222 + if (ret == CGBC_I2C_STAT_BUSY) 223 + return -EBUSY; 224 + else if (ret < 0) 225 + goto err; 226 + 227 + if (i2c->state == CGBC_I2C_STATE_INIT || 228 + (i2c->state == CGBC_I2C_STATE_WRITE && msg->flags & I2C_M_RD)) 229 + xfer.start = true; 230 + 231 + i2c->state = (msg->flags & I2C_M_RD) ? CGBC_I2C_STATE_READ : CGBC_I2C_STATE_WRITE; 232 + 233 + max_len = (i2c->state == CGBC_I2C_STATE_READ) ? 234 + CGBC_I2C_READ_MAX_LEN : CGBC_I2C_WRITE_MAX_LEN; 235 + 236 + if (msg->len - i2c->pos > max_len) { 237 + len = max_len; 238 + } else { 239 + len = msg->len - i2c->pos; 240 + 241 + if (i2c->nmsgs == 1) 242 + xfer.stop = true; 243 + } 244 + 245 + if (i2c->state == CGBC_I2C_STATE_WRITE) { 246 + xfer.write = len; 247 + xfer.read = 0; 248 + 249 + for (i = 0; i < len; i++) 250 + xfer.data[i] = msg->buf[i2c->pos + i]; 251 + 252 + cmd_len = cgbc_i2c_xfer_to_cmd(xfer, &cmd[0]); 253 + 254 + ret = cgbc_command(cgbc, &cmd, cmd_len, NULL, 0, NULL); 255 + if (ret) 256 + goto err; 257 + } else if (i2c->state == CGBC_I2C_STATE_READ) { 258 + xfer.write = 0; 259 + xfer.read = len; 260 + 261 + if (i2c->nmsgs > 1 || msg->len - i2c->pos > max_len) 262 + xfer.read |= CGBC_I2C_LAST_ACK; 263 + 264 + cmd_len = cgbc_i2c_xfer_to_cmd(xfer, &cmd[0]); 265 + ret = cgbc_command(cgbc, &cmd, cmd_len, NULL, 0, NULL); 266 + if (ret) 267 + goto err; 268 + 269 + ret = read_poll_timeout(cgbc_i2c_get_status, ret, 270 + ret != CGBC_I2C_STAT_BUSY, 0, 271 + 2 * algo_data->read_maxtime_us, false, adap); 272 + if (ret < 0) 273 + goto err; 274 + 275 + cmd_data = CGBC_I2C_CMD_DATA | algo_data->bus_id; 276 + ret = cgbc_command(cgbc, &cmd_data, sizeof(cmd_data), 277 + msg->buf + i2c->pos, len, NULL); 278 + if (ret) 279 + goto err; 280 + } 281 + 282 + if (len == (msg->len - i2c->pos)) { 283 + i2c->msg++; 284 + i2c->nmsgs--; 285 + i2c->pos = 0; 286 + } else { 287 + i2c->pos += len; 288 + } 289 + 290 + if (i2c->nmsgs == 0) 291 + i2c->state = CGBC_I2C_STATE_DONE; 292 + 293 + return 0; 294 + 295 + err: 296 + i2c->state = CGBC_I2C_STATE_ERROR; 297 + return ret; 298 + } 299 + 300 + static int cgbc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, 301 + int num) 302 + { 303 + struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap); 304 + unsigned long timeout = jiffies + HZ; 305 + int ret; 306 + 307 + i2c->state = CGBC_I2C_STATE_INIT; 308 + i2c->msg = msgs; 309 + i2c->nmsgs = num; 310 + i2c->pos = 0; 311 + 312 + while (time_before(jiffies, timeout)) { 313 + ret = cgbc_i2c_xfer_msg(adap); 314 + if (i2c->state == CGBC_I2C_STATE_DONE) 315 + return num; 316 + 317 + if (i2c->state == CGBC_I2C_STATE_ERROR) 318 + return ret; 319 + 320 + if (ret == 0) 321 + timeout = jiffies + HZ; 322 + } 323 + 324 + i2c->state = CGBC_I2C_STATE_ERROR; 325 + return -ETIMEDOUT; 326 + } 327 + 328 + static u32 cgbc_i2c_func(struct i2c_adapter *adap) 329 + { 330 + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~(I2C_FUNC_SMBUS_QUICK)); 331 + } 332 + 333 + static const struct i2c_algorithm cgbc_i2c_algorithm = { 334 + .master_xfer = cgbc_i2c_xfer, 335 + .functionality = cgbc_i2c_func, 336 + }; 337 + 338 + static struct i2c_algo_cgbc_data cgbc_i2c_algo_data[] = { 339 + { .bus_id = CGBC_I2C_PRIMARY_BUS_ID }, 340 + { .bus_id = CGBC_I2C_PM_BUS_ID }, 341 + }; 342 + 343 + static const struct i2c_adapter cgbc_i2c_adapter[] = { 344 + { 345 + .owner = THIS_MODULE, 346 + .name = "Congatec General Purpose I2C adapter", 347 + .class = I2C_CLASS_DEPRECATED, 348 + .algo = &cgbc_i2c_algorithm, 349 + .algo_data = &cgbc_i2c_algo_data[0], 350 + .nr = -1, 351 + }, 352 + { 353 + .owner = THIS_MODULE, 354 + .name = "Congatec Power Management I2C adapter", 355 + .class = I2C_CLASS_DEPRECATED, 356 + .algo = &cgbc_i2c_algorithm, 357 + .algo_data = &cgbc_i2c_algo_data[1], 358 + .nr = -1, 359 + }, 360 + }; 361 + 362 + static int cgbc_i2c_probe(struct platform_device *pdev) 363 + { 364 + struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent); 365 + struct cgbc_i2c_data *i2c; 366 + int ret; 367 + 368 + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); 369 + if (!i2c) 370 + return -ENOMEM; 371 + 372 + i2c->cgbc = cgbc; 373 + i2c->dev = &pdev->dev; 374 + i2c->adap = cgbc_i2c_adapter[pdev->id]; 375 + i2c->adap.dev.parent = i2c->dev; 376 + i2c_set_adapdata(&i2c->adap, i2c); 377 + platform_set_drvdata(pdev, i2c); 378 + 379 + ret = cgbc_i2c_set_frequency(&i2c->adap, I2C_MAX_STANDARD_MODE_FREQ); 380 + if (ret) 381 + return ret; 382 + 383 + return i2c_add_numbered_adapter(&i2c->adap); 384 + } 385 + 386 + static void cgbc_i2c_remove(struct platform_device *pdev) 387 + { 388 + struct cgbc_i2c_data *i2c = platform_get_drvdata(pdev); 389 + 390 + i2c_del_adapter(&i2c->adap); 391 + } 392 + 393 + static struct platform_driver cgbc_i2c_driver = { 394 + .driver = { 395 + .name = "cgbc-i2c", 396 + }, 397 + .probe = cgbc_i2c_probe, 398 + .remove_new = cgbc_i2c_remove, 399 + }; 400 + 401 + module_platform_driver(cgbc_i2c_driver); 402 + 403 + MODULE_DESCRIPTION("Congatec Board Controller I2C Driver"); 404 + MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>"); 405 + MODULE_LICENSE("GPL"); 406 + MODULE_ALIAS("platform:cgbc_i2c");
+12
drivers/mfd/Kconfig
··· 236 236 components like regulators or the PEK (Power Enable Key) under the 237 237 corresponding menus. 238 238 239 + config MFD_CGBC 240 + tristate "Congatec Board Controller" 241 + select MFD_CORE 242 + depends on X86 243 + help 244 + This is the core driver of the Board Controller found on some Congatec 245 + SMARC modules. The Board Controller provides functions like watchdog, 246 + I2C busses, and GPIO controller. 247 + 248 + To compile this driver as a module, choose M here: the module will be 249 + called cgbc-core. 250 + 239 251 config MFD_CROS_EC_DEV 240 252 tristate "ChromeOS Embedded Controller multifunction device" 241 253 select MFD_CORE
+1
drivers/mfd/Makefile
··· 13 13 obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o 14 14 obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o 15 15 obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o 16 + obj-$(CONFIG_MFD_CGBC) += cgbc-core.o 16 17 obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o 17 18 obj-$(CONFIG_MFD_CS42L43) += cs42l43.o 18 19 obj-$(CONFIG_MFD_CS42L43_I2C) += cs42l43-i2c.o
+411
drivers/mfd/cgbc-core.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Congatec Board Controller core driver. 4 + * 5 + * The x86 Congatec modules have an embedded micro controller named Board 6 + * Controller. This Board Controller has a Watchdog timer, some GPIOs, and two 7 + * I2C busses. 8 + * 9 + * Copyright (C) 2024 Bootlin 10 + * 11 + * Author: Thomas Richard <thomas.richard@bootlin.com> 12 + */ 13 + 14 + #include <linux/dmi.h> 15 + #include <linux/iopoll.h> 16 + #include <linux/mfd/cgbc.h> 17 + #include <linux/mfd/core.h> 18 + #include <linux/module.h> 19 + #include <linux/platform_device.h> 20 + #include <linux/sysfs.h> 21 + 22 + #define CGBC_IO_SESSION_BASE 0x0E20 23 + #define CGBC_IO_SESSION_END 0x0E30 24 + #define CGBC_IO_CMD_BASE 0x0E00 25 + #define CGBC_IO_CMD_END 0x0E10 26 + 27 + #define CGBC_MASK_STATUS (BIT(6) | BIT(7)) 28 + #define CGBC_MASK_DATA_COUNT 0x1F 29 + #define CGBC_MASK_ERROR_CODE 0x1F 30 + 31 + #define CGBC_STATUS_DATA_READY 0x00 32 + #define CGBC_STATUS_CMD_READY BIT(6) 33 + #define CGBC_STATUS_ERROR (BIT(6) | BIT(7)) 34 + 35 + #define CGBC_SESSION_CMD 0x00 36 + #define CGBC_SESSION_CMD_IDLE 0x00 37 + #define CGBC_SESSION_CMD_REQUEST 0x01 38 + #define CGBC_SESSION_DATA 0x01 39 + #define CGBC_SESSION_STATUS 0x02 40 + #define CGBC_SESSION_STATUS_FREE 0x03 41 + #define CGBC_SESSION_ACCESS 0x04 42 + #define CGBC_SESSION_ACCESS_GAINED 0x00 43 + 44 + #define CGBC_SESSION_VALID_MIN 0x02 45 + #define CGBC_SESSION_VALID_MAX 0xFE 46 + 47 + #define CGBC_CMD_STROBE 0x00 48 + #define CGBC_CMD_INDEX 0x02 49 + #define CGBC_CMD_INDEX_CBM_MAN8 0x00 50 + #define CGBC_CMD_INDEX_CBM_AUTO32 0x03 51 + #define CGBC_CMD_DATA 0x04 52 + #define CGBC_CMD_ACCESS 0x0C 53 + 54 + #define CGBC_CMD_GET_FW_REV 0x21 55 + 56 + static struct platform_device *cgbc_pdev; 57 + 58 + /* Wait the Board Controller is ready to receive some session commands */ 59 + static int cgbc_wait_device(struct cgbc_device_data *cgbc) 60 + { 61 + u16 status; 62 + int ret; 63 + 64 + ret = readx_poll_timeout(ioread16, cgbc->io_session + CGBC_SESSION_STATUS, status, 65 + status == CGBC_SESSION_STATUS_FREE, 0, 500000); 66 + 67 + if (ret || ioread32(cgbc->io_session + CGBC_SESSION_ACCESS)) 68 + ret = -ENODEV; 69 + 70 + return ret; 71 + } 72 + 73 + static int cgbc_session_command(struct cgbc_device_data *cgbc, u8 cmd) 74 + { 75 + int ret; 76 + u8 val; 77 + 78 + ret = readx_poll_timeout(ioread8, cgbc->io_session + CGBC_SESSION_CMD, val, 79 + val == CGBC_SESSION_CMD_IDLE, 0, 100000); 80 + if (ret) 81 + return ret; 82 + 83 + iowrite8(cmd, cgbc->io_session + CGBC_SESSION_CMD); 84 + 85 + ret = readx_poll_timeout(ioread8, cgbc->io_session + CGBC_SESSION_CMD, val, 86 + val == CGBC_SESSION_CMD_IDLE, 0, 100000); 87 + if (ret) 88 + return ret; 89 + 90 + ret = (int)ioread8(cgbc->io_session + CGBC_SESSION_DATA); 91 + 92 + iowrite8(CGBC_SESSION_STATUS_FREE, cgbc->io_session + CGBC_SESSION_STATUS); 93 + 94 + return ret; 95 + } 96 + 97 + static int cgbc_session_request(struct cgbc_device_data *cgbc) 98 + { 99 + unsigned int ret; 100 + 101 + ret = cgbc_wait_device(cgbc); 102 + 103 + if (ret) 104 + return dev_err_probe(cgbc->dev, ret, "device not found or not ready\n"); 105 + 106 + cgbc->session = cgbc_session_command(cgbc, CGBC_SESSION_CMD_REQUEST); 107 + 108 + /* The Board Controller sent us a wrong session handle, we cannot communicate with it */ 109 + if (cgbc->session < CGBC_SESSION_VALID_MIN || cgbc->session > CGBC_SESSION_VALID_MAX) 110 + return dev_err_probe(cgbc->dev, -ECONNREFUSED, 111 + "failed to get a valid session handle\n"); 112 + 113 + return 0; 114 + } 115 + 116 + static void cgbc_session_release(struct cgbc_device_data *cgbc) 117 + { 118 + if (cgbc_session_command(cgbc, cgbc->session) != cgbc->session) 119 + dev_warn(cgbc->dev, "failed to release session\n"); 120 + } 121 + 122 + static bool cgbc_command_lock(struct cgbc_device_data *cgbc) 123 + { 124 + iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_ACCESS); 125 + 126 + return ioread8(cgbc->io_cmd + CGBC_CMD_ACCESS) == cgbc->session; 127 + } 128 + 129 + static void cgbc_command_unlock(struct cgbc_device_data *cgbc) 130 + { 131 + iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_ACCESS); 132 + } 133 + 134 + int cgbc_command(struct cgbc_device_data *cgbc, void *cmd, unsigned int cmd_size, void *data, 135 + unsigned int data_size, u8 *status) 136 + { 137 + u8 checksum = 0, data_checksum = 0, istatus = 0, val; 138 + u8 *_data = (u8 *)data; 139 + u8 *_cmd = (u8 *)cmd; 140 + int mode_change = -1; 141 + bool lock; 142 + int ret, i; 143 + 144 + mutex_lock(&cgbc->lock); 145 + 146 + /* Request access */ 147 + ret = readx_poll_timeout(cgbc_command_lock, cgbc, lock, lock, 0, 100000); 148 + if (ret) 149 + goto out; 150 + 151 + /* Wait board controller is ready */ 152 + ret = readx_poll_timeout(ioread8, cgbc->io_cmd + CGBC_CMD_STROBE, val, 153 + val == CGBC_CMD_STROBE, 0, 100000); 154 + if (ret) 155 + goto release; 156 + 157 + /* Write command packet */ 158 + if (cmd_size <= 2) { 159 + iowrite8(CGBC_CMD_INDEX_CBM_MAN8, cgbc->io_cmd + CGBC_CMD_INDEX); 160 + } else { 161 + iowrite8(CGBC_CMD_INDEX_CBM_AUTO32, cgbc->io_cmd + CGBC_CMD_INDEX); 162 + if ((cmd_size % 4) != 0x03) 163 + mode_change = (cmd_size & 0xFFFC) - 1; 164 + } 165 + 166 + for (i = 0; i < cmd_size; i++) { 167 + iowrite8(_cmd[i], cgbc->io_cmd + CGBC_CMD_DATA + (i % 4)); 168 + checksum ^= _cmd[i]; 169 + if (mode_change == i) 170 + iowrite8((i + 1) | CGBC_CMD_INDEX_CBM_MAN8, cgbc->io_cmd + CGBC_CMD_INDEX); 171 + } 172 + 173 + /* Append checksum byte */ 174 + iowrite8(checksum, cgbc->io_cmd + CGBC_CMD_DATA + (i % 4)); 175 + 176 + /* Perform command strobe */ 177 + iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_STROBE); 178 + 179 + /* Rewind cmd buffer index */ 180 + iowrite8(CGBC_CMD_INDEX_CBM_AUTO32, cgbc->io_cmd + CGBC_CMD_INDEX); 181 + 182 + /* Wait command completion */ 183 + ret = read_poll_timeout(ioread8, val, val == CGBC_CMD_STROBE, 0, 100000, false, 184 + cgbc->io_cmd + CGBC_CMD_STROBE); 185 + if (ret) 186 + goto release; 187 + 188 + istatus = ioread8(cgbc->io_cmd + CGBC_CMD_DATA); 189 + checksum = istatus; 190 + 191 + /* Check command status */ 192 + switch (istatus & CGBC_MASK_STATUS) { 193 + case CGBC_STATUS_DATA_READY: 194 + if (istatus > data_size) 195 + istatus = data_size; 196 + for (i = 0; i < istatus; i++) { 197 + _data[i] = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + ((i + 1) % 4)); 198 + checksum ^= _data[i]; 199 + } 200 + data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + ((i + 1) % 4)); 201 + istatus &= CGBC_MASK_DATA_COUNT; 202 + break; 203 + case CGBC_STATUS_ERROR: 204 + case CGBC_STATUS_CMD_READY: 205 + data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + 1); 206 + if ((istatus & CGBC_MASK_STATUS) == CGBC_STATUS_ERROR) 207 + ret = -EIO; 208 + istatus = istatus & CGBC_MASK_ERROR_CODE; 209 + break; 210 + default: 211 + data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + 1); 212 + istatus &= CGBC_MASK_ERROR_CODE; 213 + ret = -EIO; 214 + break; 215 + } 216 + 217 + /* Checksum verification */ 218 + if (ret == 0 && data_checksum != checksum) 219 + ret = -EIO; 220 + 221 + release: 222 + cgbc_command_unlock(cgbc); 223 + 224 + out: 225 + mutex_unlock(&cgbc->lock); 226 + 227 + if (status) 228 + *status = istatus; 229 + 230 + return ret; 231 + } 232 + EXPORT_SYMBOL_GPL(cgbc_command); 233 + 234 + static struct mfd_cell cgbc_devs[] = { 235 + { .name = "cgbc-wdt" }, 236 + { .name = "cgbc-gpio" }, 237 + { .name = "cgbc-i2c", .id = 1 }, 238 + { .name = "cgbc-i2c", .id = 2 }, 239 + }; 240 + 241 + static int cgbc_map(struct cgbc_device_data *cgbc) 242 + { 243 + struct device *dev = cgbc->dev; 244 + struct platform_device *pdev = to_platform_device(dev); 245 + struct resource *ioport; 246 + 247 + ioport = platform_get_resource(pdev, IORESOURCE_IO, 0); 248 + if (!ioport) 249 + return -EINVAL; 250 + 251 + cgbc->io_session = devm_ioport_map(dev, ioport->start, resource_size(ioport)); 252 + if (!cgbc->io_session) 253 + return -ENOMEM; 254 + 255 + ioport = platform_get_resource(pdev, IORESOURCE_IO, 1); 256 + if (!ioport) 257 + return -EINVAL; 258 + 259 + cgbc->io_cmd = devm_ioport_map(dev, ioport->start, resource_size(ioport)); 260 + if (!cgbc->io_cmd) 261 + return -ENOMEM; 262 + 263 + return 0; 264 + } 265 + 266 + static const struct resource cgbc_resources[] = { 267 + { 268 + .start = CGBC_IO_SESSION_BASE, 269 + .end = CGBC_IO_SESSION_END, 270 + .flags = IORESOURCE_IO, 271 + }, 272 + { 273 + .start = CGBC_IO_CMD_BASE, 274 + .end = CGBC_IO_CMD_END, 275 + .flags = IORESOURCE_IO, 276 + }, 277 + }; 278 + 279 + static ssize_t cgbc_version_show(struct device *dev, 280 + struct device_attribute *attr, char *buf) 281 + { 282 + struct cgbc_device_data *cgbc = dev_get_drvdata(dev); 283 + 284 + return sysfs_emit(buf, "CGBCP%c%c%c\n", cgbc->version.feature, cgbc->version.major, 285 + cgbc->version.minor); 286 + } 287 + 288 + static DEVICE_ATTR_RO(cgbc_version); 289 + 290 + static struct attribute *cgbc_attrs[] = { 291 + &dev_attr_cgbc_version.attr, 292 + NULL 293 + }; 294 + 295 + ATTRIBUTE_GROUPS(cgbc); 296 + 297 + static int cgbc_get_version(struct cgbc_device_data *cgbc) 298 + { 299 + u8 cmd = CGBC_CMD_GET_FW_REV; 300 + u8 data[4]; 301 + int ret; 302 + 303 + ret = cgbc_command(cgbc, &cmd, 1, &data, sizeof(data), NULL); 304 + if (ret) 305 + return ret; 306 + 307 + cgbc->version.feature = data[0]; 308 + cgbc->version.major = data[1]; 309 + cgbc->version.minor = data[2]; 310 + 311 + return 0; 312 + } 313 + 314 + static int cgbc_init_device(struct cgbc_device_data *cgbc) 315 + { 316 + int ret; 317 + 318 + ret = cgbc_session_request(cgbc); 319 + if (ret) 320 + return ret; 321 + 322 + ret = cgbc_get_version(cgbc); 323 + if (ret) 324 + return ret; 325 + 326 + return mfd_add_devices(cgbc->dev, -1, cgbc_devs, ARRAY_SIZE(cgbc_devs), NULL, 0, NULL); 327 + } 328 + 329 + static int cgbc_probe(struct platform_device *pdev) 330 + { 331 + struct device *dev = &pdev->dev; 332 + struct cgbc_device_data *cgbc; 333 + int ret; 334 + 335 + cgbc = devm_kzalloc(dev, sizeof(*cgbc), GFP_KERNEL); 336 + if (!cgbc) 337 + return -ENOMEM; 338 + 339 + cgbc->dev = dev; 340 + 341 + ret = cgbc_map(cgbc); 342 + if (ret) 343 + return ret; 344 + 345 + mutex_init(&cgbc->lock); 346 + 347 + platform_set_drvdata(pdev, cgbc); 348 + 349 + return cgbc_init_device(cgbc); 350 + } 351 + 352 + static void cgbc_remove(struct platform_device *pdev) 353 + { 354 + struct cgbc_device_data *cgbc = platform_get_drvdata(pdev); 355 + 356 + cgbc_session_release(cgbc); 357 + 358 + mfd_remove_devices(&pdev->dev); 359 + } 360 + 361 + static struct platform_driver cgbc_driver = { 362 + .driver = { 363 + .name = "cgbc", 364 + .dev_groups = cgbc_groups, 365 + }, 366 + .probe = cgbc_probe, 367 + .remove_new = cgbc_remove, 368 + }; 369 + 370 + static const struct dmi_system_id cgbc_dmi_table[] __initconst = { 371 + { 372 + .ident = "SA7", 373 + .matches = { 374 + DMI_MATCH(DMI_BOARD_VENDOR, "congatec"), 375 + DMI_MATCH(DMI_BOARD_NAME, "conga-SA7"), 376 + }, 377 + }, 378 + {} 379 + }; 380 + MODULE_DEVICE_TABLE(dmi, cgbc_dmi_table); 381 + 382 + static int __init cgbc_init(void) 383 + { 384 + const struct dmi_system_id *id; 385 + int ret = -ENODEV; 386 + 387 + id = dmi_first_match(cgbc_dmi_table); 388 + if (IS_ERR_OR_NULL(id)) 389 + return ret; 390 + 391 + cgbc_pdev = platform_device_register_simple("cgbc", PLATFORM_DEVID_NONE, cgbc_resources, 392 + ARRAY_SIZE(cgbc_resources)); 393 + if (IS_ERR(cgbc_pdev)) 394 + return PTR_ERR(cgbc_pdev); 395 + 396 + return platform_driver_register(&cgbc_driver); 397 + } 398 + 399 + static void __exit cgbc_exit(void) 400 + { 401 + platform_device_unregister(cgbc_pdev); 402 + platform_driver_unregister(&cgbc_driver); 403 + } 404 + 405 + module_init(cgbc_init); 406 + module_exit(cgbc_exit); 407 + 408 + MODULE_DESCRIPTION("Congatec Board Controller Core Driver"); 409 + MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>"); 410 + MODULE_LICENSE("GPL"); 411 + MODULE_ALIAS("platform:cgbc-core");
+10
drivers/watchdog/Kconfig
··· 1151 1151 1152 1152 Most people will say N. 1153 1153 1154 + config CGBC_WDT 1155 + tristate "Congatec Board Controller Watchdog Timer" 1156 + depends on MFD_CGBC 1157 + select WATCHDOG_CORE 1158 + help 1159 + Enables watchdog timer support for the Congatec Board Controller. 1160 + 1161 + This driver can also be built as a module. If so, the module will be 1162 + called cgbc_wdt. 1163 + 1154 1164 config EBC_C384_WDT 1155 1165 tristate "WinSystems EBC-C384 Watchdog Timer" 1156 1166 depends on (X86 || COMPILE_TEST) && HAS_IOPORT
+1
drivers/watchdog/Makefile
··· 107 107 obj-$(CONFIG_ADVANTECH_EC_WDT) += advantech_ec_wdt.o 108 108 obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o 109 109 obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o 110 + obj-$(CONFIG_CGBC_WDT) += cgbc_wdt.o 110 111 obj-$(CONFIG_EBC_C384_WDT) += ebc-c384_wdt.o 111 112 obj-$(CONFIG_EXAR_WDT) += exar_wdt.o 112 113 obj-$(CONFIG_F71808E_WDT) += f71808e_wdt.o
+211
drivers/watchdog/cgbc_wdt.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Congatec Board Controller watchdog driver 4 + * 5 + * Copyright (C) 2024 Bootlin 6 + * Author: Thomas Richard <thomas.richard@bootlin.com> 7 + */ 8 + 9 + #include <linux/build_bug.h> 10 + #include <linux/device.h> 11 + #include <linux/limits.h> 12 + #include <linux/module.h> 13 + #include <linux/platform_device.h> 14 + #include <linux/watchdog.h> 15 + 16 + #include <linux/mfd/cgbc.h> 17 + 18 + #define CGBC_WDT_CMD_TRIGGER 0x27 19 + #define CGBC_WDT_CMD_INIT 0x28 20 + #define CGBC_WDT_DISABLE 0x00 21 + 22 + #define CGBC_WDT_MODE_SINGLE_EVENT 0x02 23 + 24 + #define CGBC_WDT_MIN_TIMEOUT 1 25 + #define CGBC_WDT_MAX_TIMEOUT ((U32_MAX >> 8) / 1000) 26 + 27 + #define CGBC_WDT_DEFAULT_TIMEOUT 30 28 + #define CGBC_WDT_DEFAULT_PRETIMEOUT 0 29 + 30 + enum action { 31 + ACTION_INT = 0, 32 + ACTION_SMI, 33 + ACTION_RESET, 34 + ACTION_BUTTON, 35 + }; 36 + 37 + static unsigned int timeout; 38 + module_param(timeout, uint, 0); 39 + MODULE_PARM_DESC(timeout, 40 + "Watchdog timeout in seconds. (>=0, default=" 41 + __MODULE_STRING(CGBC_WDT_DEFAULT_TIMEOUT) ")"); 42 + 43 + static unsigned int pretimeout = CGBC_WDT_DEFAULT_PRETIMEOUT; 44 + module_param(pretimeout, uint, 0); 45 + MODULE_PARM_DESC(pretimeout, 46 + "Watchdog pretimeout in seconds. (>=0, default=" 47 + __MODULE_STRING(CGBC_WDT_DEFAULT_PRETIMEOUT) ")"); 48 + 49 + static bool nowayout = WATCHDOG_NOWAYOUT; 50 + module_param(nowayout, bool, 0); 51 + MODULE_PARM_DESC(nowayout, 52 + "Watchdog cannot be stopped once started (default=" 53 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 54 + 55 + struct cgbc_wdt_data { 56 + struct cgbc_device_data *cgbc; 57 + struct watchdog_device wdd; 58 + }; 59 + 60 + struct cgbc_wdt_cmd_cfg { 61 + u8 cmd; 62 + u8 mode; 63 + u8 action; 64 + u8 timeout1[3]; 65 + u8 timeout2[3]; 66 + u8 reserved[3]; 67 + u8 delay[3]; 68 + } __packed; 69 + 70 + static_assert(sizeof(struct cgbc_wdt_cmd_cfg) == 15); 71 + 72 + static int cgbc_wdt_start(struct watchdog_device *wdd) 73 + { 74 + struct cgbc_wdt_data *wdt_data = watchdog_get_drvdata(wdd); 75 + struct cgbc_device_data *cgbc = wdt_data->cgbc; 76 + unsigned int timeout1 = (wdd->timeout - wdd->pretimeout) * 1000; 77 + unsigned int timeout2 = wdd->pretimeout * 1000; 78 + u8 action; 79 + 80 + struct cgbc_wdt_cmd_cfg cmd_start = { 81 + .cmd = CGBC_WDT_CMD_INIT, 82 + .mode = CGBC_WDT_MODE_SINGLE_EVENT, 83 + .timeout1[0] = (u8)timeout1, 84 + .timeout1[1] = (u8)(timeout1 >> 8), 85 + .timeout1[2] = (u8)(timeout1 >> 16), 86 + .timeout2[0] = (u8)timeout2, 87 + .timeout2[1] = (u8)(timeout2 >> 8), 88 + .timeout2[2] = (u8)(timeout2 >> 16), 89 + }; 90 + 91 + if (wdd->pretimeout) { 92 + action = 2; 93 + action |= ACTION_SMI << 2; 94 + action |= ACTION_RESET << 4; 95 + } else { 96 + action = 1; 97 + action |= ACTION_RESET << 2; 98 + } 99 + 100 + cmd_start.action = action; 101 + 102 + return cgbc_command(cgbc, &cmd_start, sizeof(cmd_start), NULL, 0, NULL); 103 + } 104 + 105 + static int cgbc_wdt_stop(struct watchdog_device *wdd) 106 + { 107 + struct cgbc_wdt_data *wdt_data = watchdog_get_drvdata(wdd); 108 + struct cgbc_device_data *cgbc = wdt_data->cgbc; 109 + struct cgbc_wdt_cmd_cfg cmd_stop = { 110 + .cmd = CGBC_WDT_CMD_INIT, 111 + .mode = CGBC_WDT_DISABLE, 112 + }; 113 + 114 + return cgbc_command(cgbc, &cmd_stop, sizeof(cmd_stop), NULL, 0, NULL); 115 + } 116 + 117 + static int cgbc_wdt_keepalive(struct watchdog_device *wdd) 118 + { 119 + struct cgbc_wdt_data *wdt_data = watchdog_get_drvdata(wdd); 120 + struct cgbc_device_data *cgbc = wdt_data->cgbc; 121 + u8 cmd_ping = CGBC_WDT_CMD_TRIGGER; 122 + 123 + return cgbc_command(cgbc, &cmd_ping, sizeof(cmd_ping), NULL, 0, NULL); 124 + } 125 + 126 + static int cgbc_wdt_set_pretimeout(struct watchdog_device *wdd, 127 + unsigned int pretimeout) 128 + { 129 + wdd->pretimeout = pretimeout; 130 + 131 + if (watchdog_active(wdd)) 132 + return cgbc_wdt_start(wdd); 133 + 134 + return 0; 135 + } 136 + 137 + static int cgbc_wdt_set_timeout(struct watchdog_device *wdd, 138 + unsigned int timeout) 139 + { 140 + if (timeout < wdd->pretimeout) 141 + wdd->pretimeout = 0; 142 + 143 + wdd->timeout = timeout; 144 + 145 + if (watchdog_active(wdd)) 146 + return cgbc_wdt_start(wdd); 147 + 148 + return 0; 149 + } 150 + 151 + static const struct watchdog_info cgbc_wdt_info = { 152 + .identity = "CGBC Watchdog", 153 + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | 154 + WDIOF_MAGICCLOSE | WDIOF_PRETIMEOUT 155 + }; 156 + 157 + static const struct watchdog_ops cgbc_wdt_ops = { 158 + .owner = THIS_MODULE, 159 + .start = cgbc_wdt_start, 160 + .stop = cgbc_wdt_stop, 161 + .ping = cgbc_wdt_keepalive, 162 + .set_timeout = cgbc_wdt_set_timeout, 163 + .set_pretimeout = cgbc_wdt_set_pretimeout, 164 + }; 165 + 166 + static int cgbc_wdt_probe(struct platform_device *pdev) 167 + { 168 + struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent); 169 + struct device *dev = &pdev->dev; 170 + struct cgbc_wdt_data *wdt_data; 171 + struct watchdog_device *wdd; 172 + 173 + wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL); 174 + if (!wdt_data) 175 + return -ENOMEM; 176 + 177 + wdt_data->cgbc = cgbc; 178 + wdd = &wdt_data->wdd; 179 + wdd->parent = dev; 180 + 181 + wdd->info = &cgbc_wdt_info; 182 + wdd->ops = &cgbc_wdt_ops; 183 + wdd->max_timeout = CGBC_WDT_MAX_TIMEOUT; 184 + wdd->min_timeout = CGBC_WDT_MIN_TIMEOUT; 185 + 186 + watchdog_set_drvdata(wdd, wdt_data); 187 + watchdog_set_nowayout(wdd, nowayout); 188 + 189 + wdd->timeout = CGBC_WDT_DEFAULT_TIMEOUT; 190 + watchdog_init_timeout(wdd, timeout, dev); 191 + cgbc_wdt_set_pretimeout(wdd, pretimeout); 192 + 193 + platform_set_drvdata(pdev, wdt_data); 194 + watchdog_stop_on_reboot(wdd); 195 + watchdog_stop_on_unregister(wdd); 196 + 197 + return devm_watchdog_register_device(dev, wdd); 198 + } 199 + 200 + static struct platform_driver cgbc_wdt_driver = { 201 + .driver = { 202 + .name = "cgbc-wdt", 203 + }, 204 + .probe = cgbc_wdt_probe, 205 + }; 206 + 207 + module_platform_driver(cgbc_wdt_driver); 208 + 209 + MODULE_DESCRIPTION("Congatec Board Controller Watchdog Driver"); 210 + MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>"); 211 + MODULE_LICENSE("GPL");
+44
include/linux/mfd/cgbc.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + /* 3 + * Congatec Board Controller driver definitions 4 + * 5 + * Copyright (C) 2024 Bootlin 6 + * Author: Thomas Richard <thomas.richard@bootlin.com> 7 + */ 8 + 9 + #ifndef _LINUX_MFD_CGBC_H_ 10 + 11 + /** 12 + * struct cgbc_version - Board Controller device version structure 13 + * @feature: Board Controller feature number 14 + * @major: Board Controller major revision 15 + * @minor: Board Controller minor revision 16 + */ 17 + struct cgbc_version { 18 + unsigned char feature; 19 + unsigned char major; 20 + unsigned char minor; 21 + }; 22 + 23 + /** 24 + * struct cgbc_device_data - Internal representation of the Board Controller device 25 + * @io_session: Pointer to the session IO memory 26 + * @io_cmd: Pointer to the command IO memory 27 + * @session: Session id returned by the Board Controller 28 + * @dev: Pointer to kernel device structure 29 + * @cgbc_version: Board Controller version structure 30 + * @mutex: Board Controller mutex 31 + */ 32 + struct cgbc_device_data { 33 + void __iomem *io_session; 34 + void __iomem *io_cmd; 35 + u8 session; 36 + struct device *dev; 37 + struct cgbc_version version; 38 + struct mutex lock; 39 + }; 40 + 41 + int cgbc_command(struct cgbc_device_data *cgbc, void *cmd, unsigned int cmd_size, 42 + void *data, unsigned int data_size, u8 *status); 43 + 44 + #endif /*_LINUX_MFD_CGBC_H_*/