[PATCH v3 7/8] mikrobus: Add mikrobus driver

Krzysztof Kozlowski krzysztof.kozlowski at linaro.org
Fri Mar 15 13:35:58 PDT 2024


On 15/03/2024 19:49, Ayush Singh wrote:
> - Setup I2C, SPI, serdev controllers associated with mikrobus connector
> - Check if a board with valid mikroBUS manifest is connected
> - Parse the manifest and register the device to kernel
> 
> Signed-off-by: Vaishnav M A <vaishnav at beagleboard.org>
> Signed-off-by: Ayush Singh <ayushdevel1325 at gmail.com>
> ---
>  MAINTAINERS                               |   1 +
>  drivers/misc/Kconfig                      |   1 +
>  drivers/misc/Makefile                     |   1 +
>  drivers/misc/mikrobus/Kconfig             |  19 +
>  drivers/misc/mikrobus/Makefile            |   6 +
>  drivers/misc/mikrobus/mikrobus_core.c     | 942 ++++++++++++++++++++++
>  drivers/misc/mikrobus/mikrobus_core.h     | 201 +++++
>  drivers/misc/mikrobus/mikrobus_id.c       | 229 ++++++
>  drivers/misc/mikrobus/mikrobus_manifest.c | 502 ++++++++++++
>  drivers/misc/mikrobus/mikrobus_manifest.h |  20 +
>  10 files changed, 1922 insertions(+)
>  create mode 100644 drivers/misc/mikrobus/Kconfig
>  create mode 100644 drivers/misc/mikrobus/Makefile
>  create mode 100644 drivers/misc/mikrobus/mikrobus_core.c
>  create mode 100644 drivers/misc/mikrobus/mikrobus_core.h
>  create mode 100644 drivers/misc/mikrobus/mikrobus_id.c
>  create mode 100644 drivers/misc/mikrobus/mikrobus_manifest.c
>  create mode 100644 drivers/misc/mikrobus/mikrobus_manifest.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 69418a058c6b..83bc5e48bef9 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14772,6 +14772,7 @@ M:	Ayush Singh <ayushdevel1325 at gmail.com>
>  M:	Vaishnav M A <vaishnav at beagleboard.org>
>  S:	Maintained
>  F:	Documentation/devicetree/bindings/misc/mikrobus-connector.yaml
> +F:	drivers/misc/mikrobus/*
>  
>  MIKROTIK CRS3XX 98DX3236 BOARD SUPPORT
>  M:	Luka Kovacic <luka.kovacic at sartura.hr>
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index 4fb291f0bf7c..3d5c36205c6c 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -591,4 +591,5 @@ source "drivers/misc/cardreader/Kconfig"
>  source "drivers/misc/uacce/Kconfig"
>  source "drivers/misc/pvpanic/Kconfig"
>  source "drivers/misc/mchp_pci1xxxx/Kconfig"
> +source "drivers/misc/mikrobus/Kconfig"
>  endmenu
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index ea6ea5bbbc9c..b9ac88055b87 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -68,3 +68,4 @@ obj-$(CONFIG_TMR_INJECT)	+= xilinx_tmr_inject.o
>  obj-$(CONFIG_TPS6594_ESM)	+= tps6594-esm.o
>  obj-$(CONFIG_TPS6594_PFSM)	+= tps6594-pfsm.o
>  obj-$(CONFIG_NSM)		+= nsm.o
> +obj-y				+= mikrobus/

> diff --git a/drivers/misc/mikrobus/Kconfig b/drivers/misc/mikrobus/Kconfig
> new file mode 100644
> index 000000000000..f0770006b4fe
> --- /dev/null
> +++ b/drivers/misc/mikrobus/Kconfig
> @@ -0,0 +1,19 @@
> +menuconfig MIKROBUS
> +	tristate "Module for instantiating devices on mikroBUS ports"
> +	depends on GPIOLIB
> +	depends on W1
> +	depends on W1_MASTER_GPIO
> +	help
> +	  This option enables the mikroBUS driver. mikroBUS is an add-on
> +	  board socket standard that offers maximum expandability with
> +	  the smallest number of pins. The mikroBUS driver instantiates
> +	  devices on a mikroBUS port described by identifying data present
> +	  in an add-on board resident EEPROM, more details on the mikroBUS
> +	  driver support and discussion can be found in this eLinux wiki :
> +	  elinux.org/Mikrobus
> +
> +
> +	  Say Y here to enable support for this driver.
> +
> +	  To compile this code as a module, chose M here: the module
> +	  will be called mikrobus.ko
> diff --git a/drivers/misc/mikrobus/Makefile b/drivers/misc/mikrobus/Makefile
> new file mode 100644
> index 000000000000..c89ff2abb80e
> --- /dev/null
> +++ b/drivers/misc/mikrobus/Makefile
> @@ -0,0 +1,6 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# mikroBUS Core
> +
> +obj-$(CONFIG_MIKROBUS) += mikrobus.o
> +mikrobus-y :=	mikrobus_core.o	mikrobus_manifest.o
> +obj-$(CONFIG_MIKROBUS) += mikrobus_id.o
> diff --git a/drivers/misc/mikrobus/mikrobus_core.c b/drivers/misc/mikrobus/mikrobus_core.c
> new file mode 100644
> index 000000000000..17718ed315b9
> --- /dev/null
> +++ b/drivers/misc/mikrobus/mikrobus_core.c
> @@ -0,0 +1,942 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * mikroBUS driver for instantiating add-on
> + * board devices with an identifier EEPROM
> + *
> + * Copyright 2020 Vaishnav M A, BeagleBoard.org Foundation.
> + * Copyright 2024 Ayush Singh <ayushdevel1325 at gmail.com>
> + */
> +
> +#define pr_fmt(fmt) "mikrobus:%s: " fmt, __func__
> +
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/idr.h>
> +#include <linux/init.h>
> +#include <linux/jump_label.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/mutex.h>
> +#include <linux/w1.h>
> +#include <linux/device.h>
> +#include <linux/i2c.h>
> +#include <linux/gpio.h>
> +#include <linux/gpio/machine.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/nvmem-provider.h>
> +#include <linux/interrupt.h>
> +#include <linux/spi/spi.h>
> +#include <linux/serdev.h>
> +#include <linux/property.h>
> +#include <linux/platform_device.h>
> +#include <linux/debugfs.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/regulator/fixed.h>
> +#include <linux/regulator/machine.h>
> +#include <linux/clk-provider.h>
> +#include <linux/greybus/greybus_manifest.h>
> +#include <linux/of_platform.h>
> +#include <linux/pwm.h>

Order all includes by name and double check if you really need that
bunch. I have doubts. Why do you implement nvmem provider here? Why do
you include GPIO machine driver here? I could go on...

> +
> +#include "mikrobus_core.h"
> +#include "mikrobus_manifest.h"
> +
> +#define MIKROBUS_ID_EEPROM_MANIFEST_ADDR 0x20
> +
> +static DEFINE_MUTEX(core_lock);
> +static DEFINE_IDR(mikrobus_port_idr);
> +static struct class_compat *mikrobus_port_compat_class;
> +int __mikrobus_first_dynamic_bus_num;

First, don't mix local and global scope variables.
Second, no don't define global scope variables.

> +static bool is_registered;
> +static int mikrobus_port_id_eeprom_probe(struct mikrobus_port *port);
> +
> +const char *MIKROBUS_PINCTRL_STR[] = { "pwm", "uart", "i2c", "spi" };

No global scope variables.

> +
> +const struct bus_type mikrobus_bus_type = {
> +	.name = "mikrobus",
> +};
> +EXPORT_SYMBOL_GPL(mikrobus_bus_type);

Why it has to be global and has to be exported?

> +
> +int mikrobus_port_scan_eeprom(struct mikrobus_port *port)
> +{
> +	const u16 manifest_start_addr = MIKROBUS_ID_EEPROM_MANIFEST_ADDR;
> +	struct addon_board_info *board;
> +	int manifest_size, retval;
> +	char header[12], *buf;
> +
> +	if (port->skip_scan)
> +		return -EINVAL;
> +
> +	retval = nvmem_device_read(port->eeprom, manifest_start_addr, 12, header);
> +	if (retval != 12) {
> +		return dev_err_probe(&port->dev, -EINVAL, "failed to fetch manifest header %d\n",
> +				     retval);

Is it probe context? Same comment everywhere else.

> +	}
> +
> +	manifest_size = mikrobus_manifest_header_validate(header, 12);
> +	if (manifest_size < 0) {
> +		return dev_err_probe(&port->dev, -EINVAL, "invalid manifest size %d\n",

Are you sure this fits in Linux coding style limit (not checkpatch
limit, but the limit expressed by Linux coding style)?
> +				     manifest_size);
> +	}
> +
> +	buf = kzalloc(manifest_size, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	retval = nvmem_device_read(port->eeprom, manifest_start_addr, manifest_size, buf);
> +	if (retval != manifest_size) {
> +		retval =
> +			dev_err_probe(&port->dev, -EINVAL, "failed to fetch manifest %d\n", retval);

No, this for sure does not fit.

> +		goto err_free_buf;
> +	}
> +
> +	board = kzalloc(sizeof(*board), GFP_KERNEL);
> +	if (!board) {
> +		retval = -ENOMEM;
> +		goto err_free_buf;
> +	}
> +
> +	w1_reset_bus(port->w1_master);
> +	/* set RST HIGH */
> +	gpiod_direction_output(port->gpios->desc[MIKROBUS_PIN_RST], 1);
> +	set_bit(W1_ABORT_SEARCH, &port->w1_master->flags);
> +
> +	INIT_LIST_HEAD(&board->manifest_descs);
> +	INIT_LIST_HEAD(&board->devices);
> +	retval = mikrobus_manifest_parse(board, buf, manifest_size);
> +	if (!retval) {
> +		retval = dev_err_probe(&port->dev, -EINVAL, "failed to parse manifest, size %d\n",
> +				       manifest_size);
> +		goto err_free_board;
> +	}
> +
> +	retval = mikrobus_board_register(port, board);
> +	if (retval) {
> +		dev_err(&port->dev, "failed to register board %s\n", board->name);
> +		goto err_free_board;
> +	}
> +
> +	kfree(buf);
> +	return 0;
> +
> +err_free_board:
> +	kfree(board);
> +err_free_buf:
> +	kfree(buf);
> +	return retval;
> +}
> +EXPORT_SYMBOL_GPL(mikrobus_port_scan_eeprom);
> +
> +static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	return sprintf(buf, "%s\n", to_mikrobus_port(dev)->name);
> +}
> +static DEVICE_ATTR_RO(name);
> +
> +static ssize_t new_device_store(struct device *dev, struct device_attribute *attr, const char *buf,
> +				size_t count)

Again, does not fit to coding style wrap limit.

But what's more important: where is ABI for all your interfaces? It
seems you created user-space interface for registering devices. This
needs to be discussed first.


> +{
> +	struct mikrobus_port *port = to_mikrobus_port(dev);
> +	struct addon_board_info *board;
> +	int retval;
> +
> +	if (port->board)
> +		return dev_err_probe(dev, -EBUSY, "already has board registered\n");
> +
> +	board = kzalloc(sizeof(*board), GFP_KERNEL);
> +	if (!board)
> +		return -ENOMEM;


...


> +
> +static int mikrobus_port_probe_pinctrl_setup(struct mikrobus_port *port)
> +{
> +	struct device *dev = port->dev.parent;
> +	struct pinctrl_state *state;
> +	int retval, i;
> +
> +	state = pinctrl_lookup_state(port->pinctrl, PINCTRL_STATE_DEFAULT);
> +	if (!IS_ERR(state)) {
> +		retval = pinctrl_select_state(port->pinctrl, state);
> +		if (retval) {
> +			return dev_err_probe(dev, retval, "Failed to select state %s\n",
> +					     PINCTRL_STATE_DEFAULT);
> +		}
> +	} else {
> +		return dev_err_probe(dev, PTR_ERR(state), "failed to find state %s\n",
> +				     PINCTRL_STATE_DEFAULT);
> +	}
> +
> +	for (i = 0; i < MIKROBUS_NUM_PINCTRL_STATE; i++) {
> +		port->pinctrl_selected[i] = kmalloc(MIKROBUS_PINCTRL_NAME_SIZE, GFP_KERNEL);
> +		sprintf(port->pinctrl_selected[i], "%s_%s", MIKROBUS_PINCTRL_STR[i],
> +			PINCTRL_STATE_DEFAULT);
> +	}
> +
> +	retval = mikrobus_port_pinctrl_select(port);
> +	if (retval)
> +		dev_err(dev, "failed to select pinctrl states [%d]", retval);
> +
> +	return retval;
> +}
> +
> +static int mikrobus_port_probe(struct platform_device *pdev)
> +{
> +	struct device_node *i2c_adap_np, *uart_np, *spi_np;
> +	struct device *dev = &pdev->dev;
> +	struct mikrobus_port *port;
> +	int retval;

int ret

> +
> +	port = kzalloc(sizeof(*port), GFP_KERNEL);

Why not devm?

> +	if (!port)
> +		return -ENOMEM;
> +
> +	/* I2C setup */
> +	i2c_adap_np = of_parse_phandle(dev->of_node, "i2c-adapter", 0);
> +	if (!i2c_adap_np) {
> +		retval = dev_err_probe(dev, -ENODEV, "cannot parse i2c-adapter");
> +		goto err_port;
> +	}
> +	port->i2c_adap = of_find_i2c_adapter_by_node(i2c_adap_np);
> +	of_node_put(i2c_adap_np);
> +
> +	/* GPIO setup */
> +	port->gpios = gpiod_get_array(dev, "mikrobus", GPIOD_OUT_LOW);
> +	if (IS_ERR(port->gpios)) {
> +		retval = PTR_ERR(port->gpios);
> +		dev_err(dev, "failed to get gpio array [%d]", retval);

ret = dev_err_probe

Where is put_device? You leak I2C adapter. All code leaks probably much
more.

> +		goto err_port;
> +	}
> +
> +	/* SPI setup */
> +	spi_np = of_parse_phandle(dev->of_node, "spi-controller", 0);
> +	if (!spi_np) {
> +		retval = dev_err_probe(dev, -ENODEV, "cannot parse spi-controller");
> +		goto err_port;
> +	}
> +	port->spi_ctrl = of_find_spi_controller_by_node(spi_np);
> +	of_node_put(spi_np);
> +	if (!port->spi_ctrl) {
> +		retval = dev_err_probe(dev, -ENODEV, "cannot find spi controller");
> +		goto err_port;
> +	}
> +	retval = device_property_read_u32_array(dev, "spi-cs", port->chip_select, MIKROBUS_NUM_CS);
> +	if (retval) {
> +		dev_err(dev, "failed to get spi-cs [%d]", retval);
> +		goto err_port;
> +	}
> +
> +	/* UART setup */
> +	uart_np = of_parse_phandle(dev->of_node, "uart", 0);
> +	if (!uart_np) {
> +		retval = dev_err_probe(dev, -ENODEV, "cannot parse uart");
> +		goto err_port;
> +	}
> +	port->ser_ctrl = of_find_serdev_controller_by_node(uart_np);
> +	of_node_put(uart_np);
> +	if (!port->ser_ctrl) {
> +		retval = dev_err_probe(dev, -ENODEV, "cannot find uart controller");
> +		goto err_port;
> +	}
> +
> +	/* PWM setup */
> +	port->pwm = devm_pwm_get(dev, NULL);
> +	if (!port->pwm) {
> +		retval = dev_err_probe(dev, -ENODEV, "cannot find pwm controller");
> +		goto err_port;
> +	}
> +
> +	/* pinctrl setup */
> +	port->pinctrl = devm_pinctrl_get(dev);
> +	if (IS_ERR(port->pinctrl)) {
> +		retval = PTR_ERR(port->pinctrl);
> +		dev_err(dev, "failed to get pinctrl [%d]", retval);

ret = dev_err_probe()

> +		goto err_port;
> +	}
> +	port->dev.parent = dev;
> +	port->dev.of_node = pdev->dev.of_node;
> +	retval = mikrobus_port_probe_pinctrl_setup(port);
> +	if (retval) {
> +		dev_err(dev, "failed to setup pinctrl [%d]", retval);
> +		goto err_port;
> +	}
> +
> +	retval = mikrobus_port_register(port);
> +	if (retval) {
> +		dev_err(dev, "port : can't register port [%d]", retval);
> +		goto err_port;
> +	}
> +
> +	platform_set_drvdata(pdev, port);
> +
> +	return 0;
> +
> +err_port:
> +	kfree(port);
> +	return retval;
> +}
> +
> +static int mikrobus_port_remove(struct platform_device *pdev)
> +{
> +	struct mikrobus_port *port = platform_get_drvdata(pdev);
> +
> +	mikrobus_port_delete(port);
> +	return 0;
> +}
> +
> +static const struct of_device_id mikrobus_port_of_match[] = {
> +	{ .compatible = "mikrobus-connector" },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, mikrobus_port_of_match);
> +
> +static struct platform_driver mikrobus_port_driver = {
> +	.probe = mikrobus_port_probe,
> +	.remove = mikrobus_port_remove,
> +	.driver = {
> +		.name = "mikrobus",
> +		.of_match_table = of_match_ptr(mikrobus_port_of_match),

Drop of_match_ptr

> +	},
> +};
> +
> +static int mikrobus_init(void)
> +{
> +	int retval;
> +
> +	retval = bus_register(&mikrobus_bus_type);
> +	if (retval) {
> +		pr_err("bus_register failed (%d)\n", retval);
> +		return retval;
> +	}
> +
> +	mikrobus_port_compat_class = class_compat_register("mikrobus-port");
> +	if (!mikrobus_port_compat_class) {
> +		pr_err("class_compat register failed (%d)\n", retval);
> +		retval = -ENOMEM;
> +		goto class_err;
> +	}
> +
> +	retval = of_alias_get_highest_id("mikrobus");
> +	if (retval >= __mikrobus_first_dynamic_bus_num)
> +		__mikrobus_first_dynamic_bus_num = retval + 1;
> +
> +	is_registered = true;
> +	retval = platform_driver_register(&mikrobus_port_driver);
> +	if (retval)
> +		pr_err("driver register failed [%d]\n", retval);
> +
> +	return retval;
> +
> +class_err:
> +	bus_unregister(&mikrobus_bus_type);
> +	idr_destroy(&mikrobus_port_idr);
> +	is_registered = false;
> +	return retval;
> +}
> +subsys_initcall(mikrobus_init);
> +
> +static void mikrobus_exit(void)
> +{
> +	platform_driver_unregister(&mikrobus_port_driver);
> +	bus_unregister(&mikrobus_bus_type);
> +	class_compat_unregister(mikrobus_port_compat_class);
> +	idr_destroy(&mikrobus_port_idr);
> +}
> +module_exit(mikrobus_exit);
> +
> +MODULE_AUTHOR("Vaishnav M A <vaishnav at beagleboard.org>");
> +MODULE_AUTHOR("Ayush Singh <ayushdevel1325 at beagleboard.org>");
> +MODULE_DESCRIPTION("mikroBUS main module");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/misc/mikrobus/mikrobus_core.h b/drivers/misc/mikrobus/mikrobus_core.h
> new file mode 100644
> index 000000000000..8bd101828964
> --- /dev/null
> +++ b/drivers/misc/mikrobus/mikrobus_core.h
> @@ -0,0 +1,201 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * mikroBUS Driver for instantiating add-on
> + * board devices with an identifier EEPROM
> + *
> + * Copyright 2020 Vaishnav M A, BeagleBoard.org Foundation.
> + */
> +
> +#ifndef __MIKROBUS_H
> +#define __MIKROBUS_H
> +
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/i2c.h>
> +#include <linux/gpio.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/gpio/machine.h>
> +#include <linux/spi/spi.h>
> +#include <linux/serdev.h>
> +#include <linux/property.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/nvmem-provider.h>

Why did you stuff here so many includes? Are you sure you need all three
pinctrls? All three gpio? The header must include *only* stuff it
directly uses.

> +
> +#define MIKROBUS_VERSION_MAJOR 0x00
> +#define MIKROBUS_VERSION_MINOR 0x03
> +
> +#define MIKROBUS_NAME_SIZE 40
> +#define MIKROBUS_PINCTRL_NAME_SIZE 20
> +
> +#define MIKROBUS_NUM_PINCTRL_STATE 4
> +#define MIKROBUS_NUM_CS 2
> +
> +#define MIKROBUS_PINCTRL_PWM 0
> +#define MIKROBUS_PINCTRL_UART 1
> +#define MIKROBUS_PINCTRL_I2C 2
> +#define MIKROBUS_PINCTRL_SPI 3
> +
> +#define MIKROBUS_PINCTRL_STATE_GPIO "gpio"
> +
> +#define MIKROBUS_EEPROM_EXIT_ID_CMD 0xD2
> +
> +extern const struct bus_type mikrobus_bus_type;
> +extern const struct device_type mikrobus_port_type;
> +extern const char *MIKROBUS_PINCTRL_STR[];

All three look suspicious. Why do you create global variables? Looks
like some pattern from very old vendor code...

> +
> +enum mikrobus_property_type {
> +	MIKROBUS_PROPERTY_TYPE_MIKROBUS = 0x00,
> +	MIKROBUS_PROPERTY_TYPE_PROPERTY = 0x01,
> +	MIKROBUS_PROPERTY_TYPE_GPIO = 0x02,
> +	MIKROBUS_PROPERTY_TYPE_U8 = 0x03,
> +	MIKROBUS_PROPERTY_TYPE_U16 = 0x04,
> +	MIKROBUS_PROPERTY_TYPE_U32 = 0x05,
> +	MIKROBUS_PROPERTY_TYPE_U64 = 0x06,
> +	MIKROBUS_PROPERTY_TYPE_REGULATOR = 0x07,
> +	MIKROBUS_PROPERTY_TYPE_CLOCK = 0x08,
> +};
> +
> +enum mikrobus_pin {
> +	MIKROBUS_PIN_PWM = 0x00,
> +	MIKROBUS_PIN_INT = 0x01,
> +	MIKROBUS_PIN_RX = 0x02,
> +	MIKROBUS_PIN_TX = 0x03,
> +	MIKROBUS_PIN_SCL = 0x04,
> +	MIKROBUS_PIN_SDA = 0x05,
> +	MIKROBUS_PIN_MOSI = 0x06,
> +	MIKROBUS_PIN_MISO = 0x07,
> +	MIKROBUS_PIN_SCK = 0x08,
> +	MIKROBUS_PIN_CS = 0x09,
> +	MIKROBUS_PIN_RST = 0x0A,
> +	MIKROBUS_PIN_AN = 0x0B,
> +	MIKROBUS_PORT_PIN_COUNT = 0x0C,

Why do you need to assign values to the enumerated entries?

> +};
> +
> +enum mikrobus_pin_state {
> +	MIKROBUS_STATE_INPUT = 0x01,
> +	MIKROBUS_STATE_OUTPUT_HIGH = 0x02,
> +	MIKROBUS_STATE_OUTPUT_LOW = 0x03,
> +	MIKROBUS_STATE_PWM = 0x04,
> +	MIKROBUS_STATE_SPI = 0x05,
> +	MIKROBUS_STATE_I2C = 0x06,
> +	MIKROBUS_STATE_UART = 0x07,
> +};
> +
> +/*
> + * board_device_info describes a single device on a mikrobus add-on
> + * board, an add-on board can present one or more device to the host
> + *
> + * @gpio_lookup: used to provide the GPIO lookup table for
> + * passing the named GPIOs to device drivers.
> + * @properties: used to provide the property_entry to pass named
> + * properties to device drivers, applicable only when driver uses
> + * device_property_read_* calls to fetch the properties.
> + * @num_gpio_resources: number of named gpio resources for the device,
> + * used mainly for gpiod_lookup_table memory allocation.
> + * @num_properties: number of custom properties for the device,
> + * used mainly for property_entry memory allocation.
> + * @protocol: used to know the type of the device and it should
> + * contain one of the values defined under 'enum greybus_class_type'
> + * under linux/greybus/greybus_manifest.h
> + * @reg: I2C address for the device, for devices on the SPI bus
> + * this field is the chip select address relative to the mikrobus
> + * port:0->device chip select connected to CS pin on mikroBUS port
> + *	1->device chip select connected to RST Pin on mikroBUS port
> + * @mode: SPI mode
> + * @max_speed_hz: SPI max speed(Hz)
> + * @drv_name: device_id to match with the driver
> + * @irq_type: type of IRQ trigger , match with defines in linux/interrupt.h
> + * @irq: irq number relative to the mikrobus port should contain one of the
> + * values defined under 'enum mikrobus_pin'
> + * @id: device id starting from 1
> + */
> +struct board_device_info {
> +	struct gpiod_lookup_table *gpio_lookup;
> +	struct property_entry *properties;
> +	struct property_entry *regulators;
> +	struct property_entry *clocks;
> +	struct list_head links;
> +	unsigned short num_gpio_resources;
> +	unsigned short num_properties;
> +	unsigned short num_regulators;
> +	unsigned short num_clocks;
> +	unsigned short protocol;
> +	unsigned short reg;
> +	unsigned int mode;
> +	void *dev_client;
> +	u32 max_speed_hz;
> +	char *drv_name;
> +	int irq_type;
> +	int irq;
> +	int id;
> +};
> +
> +/*
> + * addon_board_info describes a mikrobus add-on device the add-on
> + * board, an add-on board can present one or more device to the host
> + *
> + * @manifest_descs: list of manifest descriptors
> + * @devices: list of devices on the board
> + * @pin_state: the state of each pin on the mikrobus port required
> + * for the add-on board should contain one of the values defined under
> + * 'enum mikrobus_pin_state' restrictions are as per mikrobus standard
> + * specifications.
> + * @name: add-on board name
> + */
> +struct addon_board_info {
> +	struct list_head manifest_descs;
> +	struct list_head devices;
> +	u8 pin_state[MIKROBUS_PORT_PIN_COUNT];
> +	char *name;
> +};
> +
> +/*
> + * mikrobus_port describes the peripherals mapped to a
> + * mikrobus port.
> + *
> + * @eeprom_client: i2c_client corresponding to the eeprom
> + * on the add-on board.
> + * @board: pointer to the attached add-on board.
> + * @i2c_adap: I2C adapter attached to the mikrobus port.
> + * @spi_mstr: SPI master attached to the mikrobus port.
> + * @eeprom: nvmem_device for the eeprom on the add-on board.
> + * @pwm: pwm_device attached to the mikrobus port PWM pin.
> + * @pinctrl_selected: current pinctrl_selected state.
> + * @chip_select: chip select number mapped to the SPI
> + * CS pin on the mikrobus port and the RST pin on the mikrobus
> + * port
> + * @id: port id starting from 1
> + */
> +struct mikrobus_port {
> +	struct addon_board_info *board;
> +	struct nvmem_device *eeprom;
> +	struct i2c_adapter *i2c_adap;
> +	struct spi_controller *spi_ctrl;
> +	struct w1_master *w1_master;
> +	struct platform_device *w1_gpio;
> +	struct serdev_controller *ser_ctrl;
> +	struct gpio_descs *gpios;
> +	struct pwm_device *pwm;
> +	struct pinctrl *pinctrl;
> +	struct module *owner;
> +	struct device dev;
> +	char name[MIKROBUS_NAME_SIZE];

Why do you have fixed size names?

> +	char *pinctrl_selected[MIKROBUS_NUM_PINCTRL_STATE];
> +	unsigned int chip_select[MIKROBUS_NUM_CS];
> +	int skip_scan;
> +	int id;
> +};
> +
> +#define to_mikrobus_port(d) container_of(d, struct mikrobus_port, dev)
> +
> +void mikrobus_board_unregister(struct mikrobus_port *port, struct addon_board_info *board);
> +int mikrobus_board_register(struct mikrobus_port *port, struct addon_board_info *board);
> +int mikrobus_port_register(struct mikrobus_port *port);
> +int mikrobus_port_pinctrl_select(struct mikrobus_port *port);
> +void mikrobus_port_delete(struct mikrobus_port *port);
> +int mikrobus_port_scan_eeprom(struct mikrobus_port *port);
> +struct mikrobus_port *mikrobus_find_port_by_w1_master(struct w1_master *master);
> +#endif /* __MIKROBUS_H */
> diff --git a/drivers/misc/mikrobus/mikrobus_id.c b/drivers/misc/mikrobus/mikrobus_id.c
> new file mode 100644
> index 000000000000..42a0a558785d
> --- /dev/null
> +++ b/drivers/misc/mikrobus/mikrobus_id.c
> @@ -0,0 +1,229 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * mikrobus_id.c - w1 mikroBUS ID family EEPROM driver
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/types.h>
> +#include <linux/delay.h>
> +
> +#include <linux/crc16.h>
> +#include <linux/w1.h>
> +#include <linux/nvmem-provider.h>
> +
> +#include "mikrobus_core.h"
> +
> +#define W1_EEPROM_MIKROBUS_ID 0xCC
> +#define W1_MIKROBUS_ID_EEPROM_SIZE 0x0200
> +#define W1_MIKROBUS_ID_EEPROM_PAGE_SIZE 32
> +#define W1_MIKROBUS_ID_READ_EEPROM 0x69
> +#define W1_MIKROBUS_ID_WRITE_EEPROM 0x96
> +#define W1_MIKROBUS_ID_RELEASE_EEPROM 0xAA
> +#define W1_MIKROBUS_ID_EEPROM_READ_RETRIES 10
> +
> +#define W1_MIKROBUS_EEPROM_MANIFEST_START_PAGE 1
> +
> +static ssize_t mikrobus_manifest_store(struct device *device, struct device_attribute *attr,
> +				       const char *buf, size_t count)
> +{
> +	u8 write_request[] = { W1_MIKROBUS_ID_WRITE_EEPROM,
> +			       W1_MIKROBUS_EEPROM_MANIFEST_START_PAGE };
> +	u8 release_command = W1_MIKROBUS_ID_RELEASE_EEPROM;
> +	struct w1_slave *sl = dev_to_w1_slave(device);

This looks like w1 driver. Why is it not suitable for w1 directory?



Best regards,
Krzysztof




More information about the linux-arm-kernel mailing list