···12871287 This driver can also be built as a module. If so, the module12881288 will be called gpio-bd9571mwv.1289128912901290+config GPIO_CGBC12911291+ tristate "Congatec Board Controller GPIO support"12921292+ depends on MFD_CGBC12931293+ help12941294+ Select this option to enable GPIO support for the Congatec Board12951295+ Controller.12961296+12971297+ This driver can also be built as a module. If so, the module will be12981298+ called gpio-cgbc.12991299+12901300config GPIO_CROS_EC12911301 tristate "ChromeOS EC GPIO support"12921302 depends on CROS_EC
···11+// SPDX-License-Identifier: GPL-2.0-or-later22+/*33+ * Congatec Board Controller GPIO driver44+ *55+ * Copyright (C) 2024 Bootlin66+ * Author: Thomas Richard <thomas.richard@bootlin.com>77+ */88+99+#include <linux/gpio/driver.h>1010+#include <linux/mfd/cgbc.h>1111+#include <linux/module.h>1212+#include <linux/mutex.h>1313+#include <linux/platform_device.h>1414+1515+#define CGBC_GPIO_NGPIO 141616+1717+#define CGBC_GPIO_CMD_GET 0x641818+#define CGBC_GPIO_CMD_SET 0x651919+#define CGBC_GPIO_CMD_DIR_GET 0x662020+#define CGBC_GPIO_CMD_DIR_SET 0x672121+2222+struct cgbc_gpio_data {2323+ struct gpio_chip chip;2424+ struct cgbc_device_data *cgbc;2525+ struct mutex lock;2626+};2727+2828+static int cgbc_gpio_cmd(struct cgbc_device_data *cgbc,2929+ u8 cmd0, u8 cmd1, u8 cmd2, u8 *value)3030+{3131+ u8 cmd[3] = {cmd0, cmd1, cmd2};3232+3333+ return cgbc_command(cgbc, cmd, sizeof(cmd), value, 1, NULL);3434+}3535+3636+static int cgbc_gpio_get(struct gpio_chip *chip, unsigned int offset)3737+{3838+ struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);3939+ struct cgbc_device_data *cgbc = gpio->cgbc;4040+ int ret;4141+ u8 val;4242+4343+ scoped_guard(mutex, &gpio->lock)4444+ ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val);4545+4646+ offset %= 8;4747+4848+ if (ret)4949+ return ret;5050+ else5151+ return (int)(val & (u8)BIT(offset));5252+}5353+5454+static void __cgbc_gpio_set(struct gpio_chip *chip,5555+ unsigned int offset, int value)5656+{5757+ struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);5858+ struct cgbc_device_data *cgbc = gpio->cgbc;5959+ u8 val;6060+ int ret;6161+6262+ ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_GET, (offset > 7) ? 1 : 0, 0, &val);6363+ if (ret)6464+ return;6565+6666+ if (value)6767+ val |= BIT(offset % 8);6868+ else6969+ val &= ~(BIT(offset % 8));7070+7171+ cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_SET, (offset > 7) ? 1 : 0, val, &val);7272+}7373+7474+static void cgbc_gpio_set(struct gpio_chip *chip,7575+ unsigned int offset, int value)7676+{7777+ struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);7878+7979+ scoped_guard(mutex, &gpio->lock)8080+ __cgbc_gpio_set(chip, offset, value);8181+}8282+8383+static int cgbc_gpio_direction_set(struct gpio_chip *chip,8484+ unsigned int offset, int direction)8585+{8686+ struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);8787+ struct cgbc_device_data *cgbc = gpio->cgbc;8888+ int ret;8989+ u8 val;9090+9191+ ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val);9292+ if (ret)9393+ goto end;9494+9595+ if (direction == GPIO_LINE_DIRECTION_IN)9696+ val &= ~(BIT(offset % 8));9797+ else9898+ val |= BIT(offset % 8);9999+100100+ ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_SET, (offset > 7) ? 1 : 0, val, &val);101101+102102+end:103103+ return ret;104104+}105105+106106+static int cgbc_gpio_direction_input(struct gpio_chip *chip,107107+ unsigned int offset)108108+{109109+ struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);110110+111111+ guard(mutex)(&gpio->lock);112112+ return cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_IN);113113+}114114+115115+static int cgbc_gpio_direction_output(struct gpio_chip *chip,116116+ unsigned int offset, int value)117117+{118118+ struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);119119+120120+ guard(mutex)(&gpio->lock);121121+122122+ __cgbc_gpio_set(chip, offset, value);123123+ return cgbc_gpio_direction_set(chip, offset, GPIO_LINE_DIRECTION_OUT);124124+}125125+126126+static int cgbc_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)127127+{128128+ struct cgbc_gpio_data *gpio = gpiochip_get_data(chip);129129+ struct cgbc_device_data *cgbc = gpio->cgbc;130130+ int ret;131131+ u8 val;132132+133133+ scoped_guard(mutex, &gpio->lock)134134+ ret = cgbc_gpio_cmd(cgbc, CGBC_GPIO_CMD_DIR_GET, (offset > 7) ? 1 : 0, 0, &val);135135+136136+ if (ret)137137+ return ret;138138+139139+ if (val & BIT(offset % 8))140140+ return GPIO_LINE_DIRECTION_OUT;141141+ else142142+ return GPIO_LINE_DIRECTION_IN;143143+}144144+145145+static int cgbc_gpio_probe(struct platform_device *pdev)146146+{147147+ struct device *dev = &pdev->dev;148148+ struct cgbc_device_data *cgbc = dev_get_drvdata(dev->parent);149149+ struct cgbc_gpio_data *gpio;150150+ struct gpio_chip *chip;151151+ int ret;152152+153153+ gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);154154+ if (!gpio)155155+ return -ENOMEM;156156+157157+ gpio->cgbc = cgbc;158158+159159+ platform_set_drvdata(pdev, gpio);160160+161161+ chip = &gpio->chip;162162+ chip->label = dev_name(&pdev->dev);163163+ chip->owner = THIS_MODULE;164164+ chip->parent = dev;165165+ chip->base = -1;166166+ chip->direction_input = cgbc_gpio_direction_input;167167+ chip->direction_output = cgbc_gpio_direction_output;168168+ chip->get_direction = cgbc_gpio_get_direction;169169+ chip->get = cgbc_gpio_get;170170+ chip->set = cgbc_gpio_set;171171+ chip->ngpio = CGBC_GPIO_NGPIO;172172+173173+ ret = devm_mutex_init(dev, &gpio->lock);174174+ if (ret)175175+ return ret;176176+177177+ ret = devm_gpiochip_add_data(dev, chip, gpio);178178+ if (ret)179179+ return dev_err_probe(dev, ret, "Could not register GPIO chip\n");180180+181181+ return 0;182182+}183183+184184+static struct platform_driver cgbc_gpio_driver = {185185+ .driver = {186186+ .name = "cgbc-gpio",187187+ },188188+ .probe = cgbc_gpio_probe,189189+};190190+191191+module_platform_driver(cgbc_gpio_driver);192192+193193+MODULE_DESCRIPTION("Congatec Board Controller GPIO Driver");194194+MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");195195+MODULE_LICENSE("GPL");196196+MODULE_ALIAS("platform:cgbc-gpio");
+10
drivers/i2c/busses/Kconfig
···535535 This driver can also be built as a module. If so, the module536536 will be called i2c-cbus-gpio.537537538538+config I2C_CGBC539539+ tristate "Congatec I2C Controller"540540+ depends on MFD_CGBC541541+ help542542+ This driver supports the 2 I2C interfaces on the Congatec Board543543+ Controller.544544+545545+ This driver can also be built as a module. If so, the module will546546+ be called i2c-cgbc.ko.547547+538548config I2C_CPM539549 tristate "Freescale CPM1 or CPM2 (MPC8xx/826x)"540550 depends on CPM1 || CPM2
···11+// SPDX-License-Identifier: GPL-2.0-or-later22+/*33+ * Congatec Board Controller I2C busses driver44+ *55+ * Copyright (C) 2024 Bootlin66+ * Author: Thomas Richard <thomas.richard@bootlin.com>77+ */88+99+#include <linux/i2c.h>1010+#include <linux/iopoll.h>1111+#include <linux/mfd/cgbc.h>1212+#include <linux/module.h>1313+#include <linux/platform_device.h>1414+1515+#define CGBC_I2C_PRIMARY_BUS_ID 01616+#define CGBC_I2C_PM_BUS_ID 41717+1818+#define CGBC_I2C_CMD_START 0x401919+#define CGBC_I2C_CMD_STAT 0x482020+#define CGBC_I2C_CMD_DATA 0x502121+#define CGBC_I2C_CMD_SPEED 0x582222+2323+#define CGBC_I2C_STAT_IDL 0x002424+#define CGBC_I2C_STAT_DAT 0x012525+#define CGBC_I2C_STAT_BUSY 0x022626+2727+#define CGBC_I2C_START 0x802828+#define CGBC_I2C_STOP 0x402929+3030+#define CGBC_I2C_LAST_ACK 0x80 /* send ACK on last read byte */3131+3232+/*3333+ * Reference code defines 1kHz as min freq and 6.1MHz as max freq.3434+ * But in practice, the board controller limits the frequency to 1MHz, and the3535+ * 1kHz is not functional (minimal working freq is 50kHz).3636+ * So use these values as limits.3737+ */3838+#define CGBC_I2C_FREQ_MIN_HZ 50000 /* 50 kHz */3939+#define CGBC_I2C_FREQ_MAX_HZ 1000000 /* 1 MHz */4040+4141+#define CGBC_I2C_FREQ_UNIT_1KHZ 0x404242+#define CGBC_I2C_FREQ_UNIT_10KHZ 0x804343+#define CGBC_I2C_FREQ_UNIT_100KHZ 0xC04444+4545+#define CGBC_I2C_FREQ_UNIT_MASK 0xC04646+#define CGBC_I2C_FREQ_VALUE_MASK 0x3F4747+4848+#define CGBC_I2C_READ_MAX_LEN 314949+#define CGBC_I2C_WRITE_MAX_LEN 325050+5151+#define CGBC_I2C_CMD_HEADER_SIZE 45252+#define CGBC_I2C_CMD_SIZE (CGBC_I2C_CMD_HEADER_SIZE + CGBC_I2C_WRITE_MAX_LEN)5353+5454+enum cgbc_i2c_state {5555+ CGBC_I2C_STATE_DONE = 0,5656+ CGBC_I2C_STATE_INIT,5757+ CGBC_I2C_STATE_START,5858+ CGBC_I2C_STATE_READ,5959+ CGBC_I2C_STATE_WRITE,6060+ CGBC_I2C_STATE_ERROR,6161+};6262+6363+struct i2c_algo_cgbc_data {6464+ u8 bus_id;6565+ unsigned long read_maxtime_us;6666+};6767+6868+struct cgbc_i2c_data {6969+ struct device *dev;7070+ struct cgbc_device_data *cgbc;7171+ struct i2c_adapter adap;7272+ struct i2c_msg *msg;7373+ int nmsgs;7474+ int pos;7575+ enum cgbc_i2c_state state;7676+};7777+7878+struct cgbc_i2c_transfer {7979+ u8 bus_id;8080+ bool start;8181+ bool stop;8282+ bool last_ack;8383+ u8 read;8484+ u8 write;8585+ u8 addr;8686+ u8 data[CGBC_I2C_WRITE_MAX_LEN];8787+};8888+8989+static u8 cgbc_i2c_freq_to_reg(unsigned int bus_frequency)9090+{9191+ u8 reg;9292+9393+ if (bus_frequency <= 10000)9494+ reg = CGBC_I2C_FREQ_UNIT_1KHZ | (bus_frequency / 1000);9595+ else if (bus_frequency <= 100000)9696+ reg = CGBC_I2C_FREQ_UNIT_10KHZ | (bus_frequency / 10000);9797+ else9898+ reg = CGBC_I2C_FREQ_UNIT_100KHZ | (bus_frequency / 100000);9999+100100+ return reg;101101+}102102+103103+static unsigned int cgbc_i2c_reg_to_freq(u8 reg)104104+{105105+ unsigned int freq = reg & CGBC_I2C_FREQ_VALUE_MASK;106106+ u8 unit = reg & CGBC_I2C_FREQ_UNIT_MASK;107107+108108+ if (unit == CGBC_I2C_FREQ_UNIT_100KHZ)109109+ return freq * 100000;110110+ else if (unit == CGBC_I2C_FREQ_UNIT_10KHZ)111111+ return freq * 10000;112112+ else113113+ return freq * 1000;114114+}115115+116116+static int cgbc_i2c_get_status(struct i2c_adapter *adap)117117+{118118+ struct i2c_algo_cgbc_data *algo_data = adap->algo_data;119119+ struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap);120120+ struct cgbc_device_data *cgbc = i2c->cgbc;121121+ u8 cmd = CGBC_I2C_CMD_STAT | algo_data->bus_id;122122+ u8 status;123123+ int ret;124124+125125+ ret = cgbc_command(cgbc, &cmd, sizeof(cmd), NULL, 0, &status);126126+ if (ret)127127+ return ret;128128+129129+ return status;130130+}131131+132132+static int cgbc_i2c_set_frequency(struct i2c_adapter *adap,133133+ unsigned int bus_frequency)134134+{135135+ struct i2c_algo_cgbc_data *algo_data = adap->algo_data;136136+ struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap);137137+ struct cgbc_device_data *cgbc = i2c->cgbc;138138+ u8 cmd[2], data;139139+ int ret;140140+141141+ if (bus_frequency > CGBC_I2C_FREQ_MAX_HZ ||142142+ bus_frequency < CGBC_I2C_FREQ_MIN_HZ) {143143+ dev_info(i2c->dev, "invalid frequency %u, using default\n", bus_frequency);144144+ bus_frequency = I2C_MAX_STANDARD_MODE_FREQ;145145+ }146146+147147+ cmd[0] = CGBC_I2C_CMD_SPEED | algo_data->bus_id;148148+ cmd[1] = cgbc_i2c_freq_to_reg(bus_frequency);149149+150150+ ret = cgbc_command(cgbc, &cmd, sizeof(cmd), &data, 1, NULL);151151+ if (ret)152152+ return dev_err_probe(i2c->dev, ret,153153+ "Failed to initialize I2C bus %s",154154+ adap->name);155155+156156+ cmd[1] = 0x00;157157+158158+ ret = cgbc_command(cgbc, &cmd, sizeof(cmd), &data, 1, NULL);159159+ if (ret)160160+ return dev_err_probe(i2c->dev, ret,161161+ "Failed to get I2C bus frequency");162162+163163+ bus_frequency = cgbc_i2c_reg_to_freq(data);164164+165165+ dev_dbg(i2c->dev, "%s is running at %d Hz\n", adap->name, bus_frequency);166166+167167+ /*168168+ * The read_maxtime_us variable represents the maximum time to wait169169+ * for data during a read operation. The maximum amount of data that170170+ * can be read by a command is CGBC_I2C_READ_MAX_LEN.171171+ * Therefore, calculate the max time to properly size the timeout.172172+ */173173+ algo_data->read_maxtime_us = (BITS_PER_BYTE + 1) * CGBC_I2C_READ_MAX_LEN174174+ * USEC_PER_SEC / bus_frequency;175175+176176+ return 0;177177+}178178+179179+static unsigned int cgbc_i2c_xfer_to_cmd(struct cgbc_i2c_transfer xfer, u8 *cmd)180180+{181181+ int i = 0;182182+183183+ cmd[i++] = CGBC_I2C_CMD_START | xfer.bus_id;184184+185185+ cmd[i] = (xfer.start) ? CGBC_I2C_START : 0x00;186186+ if (xfer.stop)187187+ cmd[i] |= CGBC_I2C_STOP;188188+ cmd[i++] |= (xfer.start) ? xfer.write + 1 : xfer.write;189189+190190+ cmd[i++] = (xfer.last_ack) ? (xfer.read | CGBC_I2C_LAST_ACK) : xfer.read;191191+192192+ if (xfer.start)193193+ cmd[i++] = xfer.addr;194194+195195+ if (xfer.write > 0)196196+ memcpy(&cmd[i], &xfer.data, xfer.write);197197+198198+ return i + xfer.write;199199+}200200+201201+static int cgbc_i2c_xfer_msg(struct i2c_adapter *adap)202202+{203203+ struct i2c_algo_cgbc_data *algo_data = adap->algo_data;204204+ struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap);205205+ struct cgbc_device_data *cgbc = i2c->cgbc;206206+ struct i2c_msg *msg = i2c->msg;207207+ u8 cmd[CGBC_I2C_CMD_SIZE];208208+ int ret, max_len, len, i;209209+ unsigned int cmd_len;210210+ u8 cmd_data;211211+212212+ struct cgbc_i2c_transfer xfer = {213213+ .bus_id = algo_data->bus_id,214214+ .addr = i2c_8bit_addr_from_msg(msg),215215+ };216216+217217+ if (i2c->state == CGBC_I2C_STATE_DONE)218218+ return 0;219219+220220+ ret = cgbc_i2c_get_status(adap);221221+222222+ if (ret == CGBC_I2C_STAT_BUSY)223223+ return -EBUSY;224224+ else if (ret < 0)225225+ goto err;226226+227227+ if (i2c->state == CGBC_I2C_STATE_INIT ||228228+ (i2c->state == CGBC_I2C_STATE_WRITE && msg->flags & I2C_M_RD))229229+ xfer.start = true;230230+231231+ i2c->state = (msg->flags & I2C_M_RD) ? CGBC_I2C_STATE_READ : CGBC_I2C_STATE_WRITE;232232+233233+ max_len = (i2c->state == CGBC_I2C_STATE_READ) ?234234+ CGBC_I2C_READ_MAX_LEN : CGBC_I2C_WRITE_MAX_LEN;235235+236236+ if (msg->len - i2c->pos > max_len) {237237+ len = max_len;238238+ } else {239239+ len = msg->len - i2c->pos;240240+241241+ if (i2c->nmsgs == 1)242242+ xfer.stop = true;243243+ }244244+245245+ if (i2c->state == CGBC_I2C_STATE_WRITE) {246246+ xfer.write = len;247247+ xfer.read = 0;248248+249249+ for (i = 0; i < len; i++)250250+ xfer.data[i] = msg->buf[i2c->pos + i];251251+252252+ cmd_len = cgbc_i2c_xfer_to_cmd(xfer, &cmd[0]);253253+254254+ ret = cgbc_command(cgbc, &cmd, cmd_len, NULL, 0, NULL);255255+ if (ret)256256+ goto err;257257+ } else if (i2c->state == CGBC_I2C_STATE_READ) {258258+ xfer.write = 0;259259+ xfer.read = len;260260+261261+ if (i2c->nmsgs > 1 || msg->len - i2c->pos > max_len)262262+ xfer.read |= CGBC_I2C_LAST_ACK;263263+264264+ cmd_len = cgbc_i2c_xfer_to_cmd(xfer, &cmd[0]);265265+ ret = cgbc_command(cgbc, &cmd, cmd_len, NULL, 0, NULL);266266+ if (ret)267267+ goto err;268268+269269+ ret = read_poll_timeout(cgbc_i2c_get_status, ret,270270+ ret != CGBC_I2C_STAT_BUSY, 0,271271+ 2 * algo_data->read_maxtime_us, false, adap);272272+ if (ret < 0)273273+ goto err;274274+275275+ cmd_data = CGBC_I2C_CMD_DATA | algo_data->bus_id;276276+ ret = cgbc_command(cgbc, &cmd_data, sizeof(cmd_data),277277+ msg->buf + i2c->pos, len, NULL);278278+ if (ret)279279+ goto err;280280+ }281281+282282+ if (len == (msg->len - i2c->pos)) {283283+ i2c->msg++;284284+ i2c->nmsgs--;285285+ i2c->pos = 0;286286+ } else {287287+ i2c->pos += len;288288+ }289289+290290+ if (i2c->nmsgs == 0)291291+ i2c->state = CGBC_I2C_STATE_DONE;292292+293293+ return 0;294294+295295+err:296296+ i2c->state = CGBC_I2C_STATE_ERROR;297297+ return ret;298298+}299299+300300+static int cgbc_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs,301301+ int num)302302+{303303+ struct cgbc_i2c_data *i2c = i2c_get_adapdata(adap);304304+ unsigned long timeout = jiffies + HZ;305305+ int ret;306306+307307+ i2c->state = CGBC_I2C_STATE_INIT;308308+ i2c->msg = msgs;309309+ i2c->nmsgs = num;310310+ i2c->pos = 0;311311+312312+ while (time_before(jiffies, timeout)) {313313+ ret = cgbc_i2c_xfer_msg(adap);314314+ if (i2c->state == CGBC_I2C_STATE_DONE)315315+ return num;316316+317317+ if (i2c->state == CGBC_I2C_STATE_ERROR)318318+ return ret;319319+320320+ if (ret == 0)321321+ timeout = jiffies + HZ;322322+ }323323+324324+ i2c->state = CGBC_I2C_STATE_ERROR;325325+ return -ETIMEDOUT;326326+}327327+328328+static u32 cgbc_i2c_func(struct i2c_adapter *adap)329329+{330330+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~(I2C_FUNC_SMBUS_QUICK));331331+}332332+333333+static const struct i2c_algorithm cgbc_i2c_algorithm = {334334+ .master_xfer = cgbc_i2c_xfer,335335+ .functionality = cgbc_i2c_func,336336+};337337+338338+static struct i2c_algo_cgbc_data cgbc_i2c_algo_data[] = {339339+ { .bus_id = CGBC_I2C_PRIMARY_BUS_ID },340340+ { .bus_id = CGBC_I2C_PM_BUS_ID },341341+};342342+343343+static const struct i2c_adapter cgbc_i2c_adapter[] = {344344+ {345345+ .owner = THIS_MODULE,346346+ .name = "Congatec General Purpose I2C adapter",347347+ .class = I2C_CLASS_DEPRECATED,348348+ .algo = &cgbc_i2c_algorithm,349349+ .algo_data = &cgbc_i2c_algo_data[0],350350+ .nr = -1,351351+ },352352+ {353353+ .owner = THIS_MODULE,354354+ .name = "Congatec Power Management I2C adapter",355355+ .class = I2C_CLASS_DEPRECATED,356356+ .algo = &cgbc_i2c_algorithm,357357+ .algo_data = &cgbc_i2c_algo_data[1],358358+ .nr = -1,359359+ },360360+};361361+362362+static int cgbc_i2c_probe(struct platform_device *pdev)363363+{364364+ struct cgbc_device_data *cgbc = dev_get_drvdata(pdev->dev.parent);365365+ struct cgbc_i2c_data *i2c;366366+ int ret;367367+368368+ i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL);369369+ if (!i2c)370370+ return -ENOMEM;371371+372372+ i2c->cgbc = cgbc;373373+ i2c->dev = &pdev->dev;374374+ i2c->adap = cgbc_i2c_adapter[pdev->id];375375+ i2c->adap.dev.parent = i2c->dev;376376+ i2c_set_adapdata(&i2c->adap, i2c);377377+ platform_set_drvdata(pdev, i2c);378378+379379+ ret = cgbc_i2c_set_frequency(&i2c->adap, I2C_MAX_STANDARD_MODE_FREQ);380380+ if (ret)381381+ return ret;382382+383383+ return i2c_add_numbered_adapter(&i2c->adap);384384+}385385+386386+static void cgbc_i2c_remove(struct platform_device *pdev)387387+{388388+ struct cgbc_i2c_data *i2c = platform_get_drvdata(pdev);389389+390390+ i2c_del_adapter(&i2c->adap);391391+}392392+393393+static struct platform_driver cgbc_i2c_driver = {394394+ .driver = {395395+ .name = "cgbc-i2c",396396+ },397397+ .probe = cgbc_i2c_probe,398398+ .remove_new = cgbc_i2c_remove,399399+};400400+401401+module_platform_driver(cgbc_i2c_driver);402402+403403+MODULE_DESCRIPTION("Congatec Board Controller I2C Driver");404404+MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");405405+MODULE_LICENSE("GPL");406406+MODULE_ALIAS("platform:cgbc_i2c");
+12
drivers/mfd/Kconfig
···236236 components like regulators or the PEK (Power Enable Key) under the237237 corresponding menus.238238239239+config MFD_CGBC240240+ tristate "Congatec Board Controller"241241+ select MFD_CORE242242+ depends on X86243243+ help244244+ This is the core driver of the Board Controller found on some Congatec245245+ SMARC modules. The Board Controller provides functions like watchdog,246246+ I2C busses, and GPIO controller.247247+248248+ To compile this driver as a module, choose M here: the module will be249249+ called cgbc-core.250250+239251config MFD_CROS_EC_DEV240252 tristate "ChromeOS Embedded Controller multifunction device"241253 select MFD_CORE
···11+// SPDX-License-Identifier: GPL-2.0-or-later22+/*33+ * Congatec Board Controller core driver.44+ *55+ * The x86 Congatec modules have an embedded micro controller named Board66+ * Controller. This Board Controller has a Watchdog timer, some GPIOs, and two77+ * I2C busses.88+ *99+ * Copyright (C) 2024 Bootlin1010+ *1111+ * Author: Thomas Richard <thomas.richard@bootlin.com>1212+ */1313+1414+#include <linux/dmi.h>1515+#include <linux/iopoll.h>1616+#include <linux/mfd/cgbc.h>1717+#include <linux/mfd/core.h>1818+#include <linux/module.h>1919+#include <linux/platform_device.h>2020+#include <linux/sysfs.h>2121+2222+#define CGBC_IO_SESSION_BASE 0x0E202323+#define CGBC_IO_SESSION_END 0x0E302424+#define CGBC_IO_CMD_BASE 0x0E002525+#define CGBC_IO_CMD_END 0x0E102626+2727+#define CGBC_MASK_STATUS (BIT(6) | BIT(7))2828+#define CGBC_MASK_DATA_COUNT 0x1F2929+#define CGBC_MASK_ERROR_CODE 0x1F3030+3131+#define CGBC_STATUS_DATA_READY 0x003232+#define CGBC_STATUS_CMD_READY BIT(6)3333+#define CGBC_STATUS_ERROR (BIT(6) | BIT(7))3434+3535+#define CGBC_SESSION_CMD 0x003636+#define CGBC_SESSION_CMD_IDLE 0x003737+#define CGBC_SESSION_CMD_REQUEST 0x013838+#define CGBC_SESSION_DATA 0x013939+#define CGBC_SESSION_STATUS 0x024040+#define CGBC_SESSION_STATUS_FREE 0x034141+#define CGBC_SESSION_ACCESS 0x044242+#define CGBC_SESSION_ACCESS_GAINED 0x004343+4444+#define CGBC_SESSION_VALID_MIN 0x024545+#define CGBC_SESSION_VALID_MAX 0xFE4646+4747+#define CGBC_CMD_STROBE 0x004848+#define CGBC_CMD_INDEX 0x024949+#define CGBC_CMD_INDEX_CBM_MAN8 0x005050+#define CGBC_CMD_INDEX_CBM_AUTO32 0x035151+#define CGBC_CMD_DATA 0x045252+#define CGBC_CMD_ACCESS 0x0C5353+5454+#define CGBC_CMD_GET_FW_REV 0x215555+5656+static struct platform_device *cgbc_pdev;5757+5858+/* Wait the Board Controller is ready to receive some session commands */5959+static int cgbc_wait_device(struct cgbc_device_data *cgbc)6060+{6161+ u16 status;6262+ int ret;6363+6464+ ret = readx_poll_timeout(ioread16, cgbc->io_session + CGBC_SESSION_STATUS, status,6565+ status == CGBC_SESSION_STATUS_FREE, 0, 500000);6666+6767+ if (ret || ioread32(cgbc->io_session + CGBC_SESSION_ACCESS))6868+ ret = -ENODEV;6969+7070+ return ret;7171+}7272+7373+static int cgbc_session_command(struct cgbc_device_data *cgbc, u8 cmd)7474+{7575+ int ret;7676+ u8 val;7777+7878+ ret = readx_poll_timeout(ioread8, cgbc->io_session + CGBC_SESSION_CMD, val,7979+ val == CGBC_SESSION_CMD_IDLE, 0, 100000);8080+ if (ret)8181+ return ret;8282+8383+ iowrite8(cmd, cgbc->io_session + CGBC_SESSION_CMD);8484+8585+ ret = readx_poll_timeout(ioread8, cgbc->io_session + CGBC_SESSION_CMD, val,8686+ val == CGBC_SESSION_CMD_IDLE, 0, 100000);8787+ if (ret)8888+ return ret;8989+9090+ ret = (int)ioread8(cgbc->io_session + CGBC_SESSION_DATA);9191+9292+ iowrite8(CGBC_SESSION_STATUS_FREE, cgbc->io_session + CGBC_SESSION_STATUS);9393+9494+ return ret;9595+}9696+9797+static int cgbc_session_request(struct cgbc_device_data *cgbc)9898+{9999+ unsigned int ret;100100+101101+ ret = cgbc_wait_device(cgbc);102102+103103+ if (ret)104104+ return dev_err_probe(cgbc->dev, ret, "device not found or not ready\n");105105+106106+ cgbc->session = cgbc_session_command(cgbc, CGBC_SESSION_CMD_REQUEST);107107+108108+ /* The Board Controller sent us a wrong session handle, we cannot communicate with it */109109+ if (cgbc->session < CGBC_SESSION_VALID_MIN || cgbc->session > CGBC_SESSION_VALID_MAX)110110+ return dev_err_probe(cgbc->dev, -ECONNREFUSED,111111+ "failed to get a valid session handle\n");112112+113113+ return 0;114114+}115115+116116+static void cgbc_session_release(struct cgbc_device_data *cgbc)117117+{118118+ if (cgbc_session_command(cgbc, cgbc->session) != cgbc->session)119119+ dev_warn(cgbc->dev, "failed to release session\n");120120+}121121+122122+static bool cgbc_command_lock(struct cgbc_device_data *cgbc)123123+{124124+ iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_ACCESS);125125+126126+ return ioread8(cgbc->io_cmd + CGBC_CMD_ACCESS) == cgbc->session;127127+}128128+129129+static void cgbc_command_unlock(struct cgbc_device_data *cgbc)130130+{131131+ iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_ACCESS);132132+}133133+134134+int cgbc_command(struct cgbc_device_data *cgbc, void *cmd, unsigned int cmd_size, void *data,135135+ unsigned int data_size, u8 *status)136136+{137137+ u8 checksum = 0, data_checksum = 0, istatus = 0, val;138138+ u8 *_data = (u8 *)data;139139+ u8 *_cmd = (u8 *)cmd;140140+ int mode_change = -1;141141+ bool lock;142142+ int ret, i;143143+144144+ mutex_lock(&cgbc->lock);145145+146146+ /* Request access */147147+ ret = readx_poll_timeout(cgbc_command_lock, cgbc, lock, lock, 0, 100000);148148+ if (ret)149149+ goto out;150150+151151+ /* Wait board controller is ready */152152+ ret = readx_poll_timeout(ioread8, cgbc->io_cmd + CGBC_CMD_STROBE, val,153153+ val == CGBC_CMD_STROBE, 0, 100000);154154+ if (ret)155155+ goto release;156156+157157+ /* Write command packet */158158+ if (cmd_size <= 2) {159159+ iowrite8(CGBC_CMD_INDEX_CBM_MAN8, cgbc->io_cmd + CGBC_CMD_INDEX);160160+ } else {161161+ iowrite8(CGBC_CMD_INDEX_CBM_AUTO32, cgbc->io_cmd + CGBC_CMD_INDEX);162162+ if ((cmd_size % 4) != 0x03)163163+ mode_change = (cmd_size & 0xFFFC) - 1;164164+ }165165+166166+ for (i = 0; i < cmd_size; i++) {167167+ iowrite8(_cmd[i], cgbc->io_cmd + CGBC_CMD_DATA + (i % 4));168168+ checksum ^= _cmd[i];169169+ if (mode_change == i)170170+ iowrite8((i + 1) | CGBC_CMD_INDEX_CBM_MAN8, cgbc->io_cmd + CGBC_CMD_INDEX);171171+ }172172+173173+ /* Append checksum byte */174174+ iowrite8(checksum, cgbc->io_cmd + CGBC_CMD_DATA + (i % 4));175175+176176+ /* Perform command strobe */177177+ iowrite8(cgbc->session, cgbc->io_cmd + CGBC_CMD_STROBE);178178+179179+ /* Rewind cmd buffer index */180180+ iowrite8(CGBC_CMD_INDEX_CBM_AUTO32, cgbc->io_cmd + CGBC_CMD_INDEX);181181+182182+ /* Wait command completion */183183+ ret = read_poll_timeout(ioread8, val, val == CGBC_CMD_STROBE, 0, 100000, false,184184+ cgbc->io_cmd + CGBC_CMD_STROBE);185185+ if (ret)186186+ goto release;187187+188188+ istatus = ioread8(cgbc->io_cmd + CGBC_CMD_DATA);189189+ checksum = istatus;190190+191191+ /* Check command status */192192+ switch (istatus & CGBC_MASK_STATUS) {193193+ case CGBC_STATUS_DATA_READY:194194+ if (istatus > data_size)195195+ istatus = data_size;196196+ for (i = 0; i < istatus; i++) {197197+ _data[i] = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + ((i + 1) % 4));198198+ checksum ^= _data[i];199199+ }200200+ data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + ((i + 1) % 4));201201+ istatus &= CGBC_MASK_DATA_COUNT;202202+ break;203203+ case CGBC_STATUS_ERROR:204204+ case CGBC_STATUS_CMD_READY:205205+ data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + 1);206206+ if ((istatus & CGBC_MASK_STATUS) == CGBC_STATUS_ERROR)207207+ ret = -EIO;208208+ istatus = istatus & CGBC_MASK_ERROR_CODE;209209+ break;210210+ default:211211+ data_checksum = ioread8(cgbc->io_cmd + CGBC_CMD_DATA + 1);212212+ istatus &= CGBC_MASK_ERROR_CODE;213213+ ret = -EIO;214214+ break;215215+ }216216+217217+ /* Checksum verification */218218+ if (ret == 0 && data_checksum != checksum)219219+ ret = -EIO;220220+221221+release:222222+ cgbc_command_unlock(cgbc);223223+224224+out:225225+ mutex_unlock(&cgbc->lock);226226+227227+ if (status)228228+ *status = istatus;229229+230230+ return ret;231231+}232232+EXPORT_SYMBOL_GPL(cgbc_command);233233+234234+static struct mfd_cell cgbc_devs[] = {235235+ { .name = "cgbc-wdt" },236236+ { .name = "cgbc-gpio" },237237+ { .name = "cgbc-i2c", .id = 1 },238238+ { .name = "cgbc-i2c", .id = 2 },239239+};240240+241241+static int cgbc_map(struct cgbc_device_data *cgbc)242242+{243243+ struct device *dev = cgbc->dev;244244+ struct platform_device *pdev = to_platform_device(dev);245245+ struct resource *ioport;246246+247247+ ioport = platform_get_resource(pdev, IORESOURCE_IO, 0);248248+ if (!ioport)249249+ return -EINVAL;250250+251251+ cgbc->io_session = devm_ioport_map(dev, ioport->start, resource_size(ioport));252252+ if (!cgbc->io_session)253253+ return -ENOMEM;254254+255255+ ioport = platform_get_resource(pdev, IORESOURCE_IO, 1);256256+ if (!ioport)257257+ return -EINVAL;258258+259259+ cgbc->io_cmd = devm_ioport_map(dev, ioport->start, resource_size(ioport));260260+ if (!cgbc->io_cmd)261261+ return -ENOMEM;262262+263263+ return 0;264264+}265265+266266+static const struct resource cgbc_resources[] = {267267+ {268268+ .start = CGBC_IO_SESSION_BASE,269269+ .end = CGBC_IO_SESSION_END,270270+ .flags = IORESOURCE_IO,271271+ },272272+ {273273+ .start = CGBC_IO_CMD_BASE,274274+ .end = CGBC_IO_CMD_END,275275+ .flags = IORESOURCE_IO,276276+ },277277+};278278+279279+static ssize_t cgbc_version_show(struct device *dev,280280+ struct device_attribute *attr, char *buf)281281+{282282+ struct cgbc_device_data *cgbc = dev_get_drvdata(dev);283283+284284+ return sysfs_emit(buf, "CGBCP%c%c%c\n", cgbc->version.feature, cgbc->version.major,285285+ cgbc->version.minor);286286+}287287+288288+static DEVICE_ATTR_RO(cgbc_version);289289+290290+static struct attribute *cgbc_attrs[] = {291291+ &dev_attr_cgbc_version.attr,292292+ NULL293293+};294294+295295+ATTRIBUTE_GROUPS(cgbc);296296+297297+static int cgbc_get_version(struct cgbc_device_data *cgbc)298298+{299299+ u8 cmd = CGBC_CMD_GET_FW_REV;300300+ u8 data[4];301301+ int ret;302302+303303+ ret = cgbc_command(cgbc, &cmd, 1, &data, sizeof(data), NULL);304304+ if (ret)305305+ return ret;306306+307307+ cgbc->version.feature = data[0];308308+ cgbc->version.major = data[1];309309+ cgbc->version.minor = data[2];310310+311311+ return 0;312312+}313313+314314+static int cgbc_init_device(struct cgbc_device_data *cgbc)315315+{316316+ int ret;317317+318318+ ret = cgbc_session_request(cgbc);319319+ if (ret)320320+ return ret;321321+322322+ ret = cgbc_get_version(cgbc);323323+ if (ret)324324+ return ret;325325+326326+ return mfd_add_devices(cgbc->dev, -1, cgbc_devs, ARRAY_SIZE(cgbc_devs), NULL, 0, NULL);327327+}328328+329329+static int cgbc_probe(struct platform_device *pdev)330330+{331331+ struct device *dev = &pdev->dev;332332+ struct cgbc_device_data *cgbc;333333+ int ret;334334+335335+ cgbc = devm_kzalloc(dev, sizeof(*cgbc), GFP_KERNEL);336336+ if (!cgbc)337337+ return -ENOMEM;338338+339339+ cgbc->dev = dev;340340+341341+ ret = cgbc_map(cgbc);342342+ if (ret)343343+ return ret;344344+345345+ mutex_init(&cgbc->lock);346346+347347+ platform_set_drvdata(pdev, cgbc);348348+349349+ return cgbc_init_device(cgbc);350350+}351351+352352+static void cgbc_remove(struct platform_device *pdev)353353+{354354+ struct cgbc_device_data *cgbc = platform_get_drvdata(pdev);355355+356356+ cgbc_session_release(cgbc);357357+358358+ mfd_remove_devices(&pdev->dev);359359+}360360+361361+static struct platform_driver cgbc_driver = {362362+ .driver = {363363+ .name = "cgbc",364364+ .dev_groups = cgbc_groups,365365+ },366366+ .probe = cgbc_probe,367367+ .remove_new = cgbc_remove,368368+};369369+370370+static const struct dmi_system_id cgbc_dmi_table[] __initconst = {371371+ {372372+ .ident = "SA7",373373+ .matches = {374374+ DMI_MATCH(DMI_BOARD_VENDOR, "congatec"),375375+ DMI_MATCH(DMI_BOARD_NAME, "conga-SA7"),376376+ },377377+ },378378+ {}379379+};380380+MODULE_DEVICE_TABLE(dmi, cgbc_dmi_table);381381+382382+static int __init cgbc_init(void)383383+{384384+ const struct dmi_system_id *id;385385+ int ret = -ENODEV;386386+387387+ id = dmi_first_match(cgbc_dmi_table);388388+ if (IS_ERR_OR_NULL(id))389389+ return ret;390390+391391+ cgbc_pdev = platform_device_register_simple("cgbc", PLATFORM_DEVID_NONE, cgbc_resources,392392+ ARRAY_SIZE(cgbc_resources));393393+ if (IS_ERR(cgbc_pdev))394394+ return PTR_ERR(cgbc_pdev);395395+396396+ return platform_driver_register(&cgbc_driver);397397+}398398+399399+static void __exit cgbc_exit(void)400400+{401401+ platform_device_unregister(cgbc_pdev);402402+ platform_driver_unregister(&cgbc_driver);403403+}404404+405405+module_init(cgbc_init);406406+module_exit(cgbc_exit);407407+408408+MODULE_DESCRIPTION("Congatec Board Controller Core Driver");409409+MODULE_AUTHOR("Thomas Richard <thomas.richard@bootlin.com>");410410+MODULE_LICENSE("GPL");411411+MODULE_ALIAS("platform:cgbc-core");
+10
drivers/watchdog/Kconfig
···1151115111521152 Most people will say N.1153115311541154+config CGBC_WDT11551155+ tristate "Congatec Board Controller Watchdog Timer"11561156+ depends on MFD_CGBC11571157+ select WATCHDOG_CORE11581158+ help11591159+ Enables watchdog timer support for the Congatec Board Controller.11601160+11611161+ This driver can also be built as a module. If so, the module will be11621162+ called cgbc_wdt.11631163+11541164config EBC_C384_WDT11551165 tristate "WinSystems EBC-C384 Watchdog Timer"11561166 depends on (X86 || COMPILE_TEST) && HAS_IOPORT