[RFC PATCH dtc] C-based DT schema checker integrated into dtc

Thierry Reding thierry.reding at gmail.com
Wed Nov 6 07:17:54 EST 2013


On Mon, Nov 04, 2013 at 09:43:22PM +0100, Arnd Bergmann wrote:
> On Monday 04 November 2013 09:37:07 Stephen Warren wrote:
> > > The basic idea is to extend 'devres' to automatically register
> > > all the resources (registers, irq, dma, gpio, pinctrl, clk, regulator, ...)
> > > and simple properties before the ->probe() callback is even called,
> > > based on a per-driver data structure that describes them, and that
> > > can be easily parsed by an external tool.
> > 
> > I had suggested that while talking to someone at the kernel summit,
> > basically each driver supplies functions like:
> > 
> > 1) ACPI -> platform data or resources converter
> > 2) DT -> platform or resources data converter
> > 3) anything else -> platform or resources data converter
> > 4) probe()
> 
> FWIW, here is a very early draft of the interfaces I have in mind.
> At the moment the implementation is DT focused, but that should
> be extensible to ACPI if necessary.
> 
> At the end, you can see how a probe function could end up looking.
> I'm sure this is full of bugs at the moment, incomplete and needs
> to be moved into actual header and C files, but it should be enough
> to get across where I'm getting with this, and to see if anyone
> thinks it's a really bad idea or won't actually work.

I know that this is completely unconstructive, but the below gives me
the creeps.

Thierry

> 
> 	Arnd
> 
> #if 0
> /* allocate drvdata */
> DEVM_ALLOC,
> 
> /* request hardware properties */
> DEVM_IRQ,
> DEVM_IOMAP,
> DEVM_GPIO,
> DEVM_DMA_SLAVE,
> DEVM_PINCTRL,
> DEVM_REGULATOR,
> DEVM_CLK,
> DEVM_PWM,
> DEVM_USBPHY,
> 
> /* auxiliary information */
> DEVM_PROP_BOOL
> DEVM_PROP_U32
> DEVM_PROP_STRING
> #endif
> 
> #error don't bother compiling this file, it's only proof-of-concept
> 
> struct device;
> 
> struct devm_probe {
> 	int (*initfunc)(struct device *dev, const struct devm_probe *probe);
> 	ptrdiff_t offset;
> 	unsigned int index;
> 	const char *name;
> 	void *arg;
> 	unsigned int flags;
> };
> 
> /* like offsetof(), but ensures that MEMBER is of type MEMBERTYPE */
> #define offsetof_t(TYPE, MEMBER, MEMBERTYPE) \
> 	((typeof(MEMBER)*)0 - typeof(MEMBERTYPE*)0 + offsetof((TYPE), (MEMBER)))
> 
> /* cast 'ptr' to void* and cause a build warning if it wasn't of 'type' before */
> #define typecheck_ptr(ptr, type) \
> 	(void *)(ptr - (type)NULL + (type)NULL)
> 
> /*
>  * This is the main entry point for drivers: 
>  *
>  * Given an zero-terminated array of 'struct devm_probe' entries,
>  * this calls all initfunc pointers in the given order. Each initfunc
>  * may manipulate a field in the driver_data, as pointed to by
>  * the 'offset' member.
>  */
> int devm_probe(struct device *dev, const struct devm_probe *probe)
> {
> 	int ret;
> 
> 	for (ret = 0; !ret && probe->initfunc, probe++)
> 		ret = probe->initfunc(dev, probe);
> 
> 	return ret;
> }
> EXPORT_SYMBOL_GPL(devm_probe);
> 
> /*
>  * this must be the called before any of the others, or not at
>  * all, if dev_set_drvdata() has already been called.
>  */
> static void devm_probe_alloc_release(struct device *dev, void *res)
> {
> 	dev_set_drvdata(dev, NULL);
> }
> int devm_probe_alloc(struct device *dev, const struct devm_probe *probe)
> {
> 	void *dr;
> 
> 	if (dev_get_drvdata)
> 		return -EBUSY;
> 
> 	dr = alloc_dr(devm_probe_alloc_release, probe->offset, GFP_KERNEL);
> 	if (unlikely(!dr))
> 		return -ENOMEM;
> 
> 	dev_set_drvdata(dev, dr);
> 	set_node_dbginfo(&dr->node, "devm_probe_alloc", size);
> 	devres_add(dev, dr->data);
> }
> EXPORT_SYMBOL_GPL(devm_probe_alloc);
> 
> 
> #define DEVM_ALLOC(_struct) \
> 	{ .initfunc = devm_probe_alloc, .offset = sizeof(struct _struct), }
> 
> int devm_probe_irq(struct device *dev, const struct devm_probe *probe)
> {
> 	int ret;
> 	int irq;
> 	int *irqp;
> 	int index;
> 	const char *name;
> 
> 	/* come up with a reasonable string for the irq name,
> 	   maybe create devm_kasprintf() to allow arbitrary length? */
> 	name = devm_kmalloc(dev, 32, GFP_KERNEL);
> 	if (!name)
> 		return -ENOMEM;
> 	if (probe->name)
> 		snprintf(name, 32 - 1, "%s:%s", dev_name(dev), probe->name);
> 	else
> 		snprintf(name, 32 - 1, "%s:%n", dev_name(dev), probe->index);
> 
> 	/* find IRQ number from resource if platform device */
> 	irq = 0;
> 	if (dev->bus_type == &platform_bus) {
> 		struct platform_device *pdev = to_platform_device(dev);
> 
> 		if (probe->name)
> 			irq = platform_get_irq_byname(pdev, probe->name);
> 		else
> 			irq = platform_get_irq(pdev, probe->index);
> 	}
> 
> 	/* try getting irq number from device tree if that failed */
> 	if (!irq && dev->of_node) {
> 		if (probe->name)
> 			index = of_property_match_string(dev->of_node,
> 							 "interrupt-names",
> 							 probe->name);
> 		else
> 			index = probe->index;
> 
> 		irq = irq_of_parse_and_map(dev->of_node, index);
> 	}
> 
> 	/* copy irq number to driver data */
> 	irqp = dev_get_drvdata(dev) + probe->offset;
> 	*irqp = irq;
> 	
> 	return devm_request_irq(dev, irq, probe->arg, probe->flags, name, probe->dev);
> }
> EXPORT_SYMBOL_GPL(devm_probe_irq);
> 
> #define DEVM_IRQ(_struct, _member, _index, _handler, _flags) {	\
> 	.initfunc = devm_probe_irq,				\
> 	.offset = offsetof_t(struct _struct, _member, int),	\
> 	.index = _index,					\
> 	.arg = typecheck_ptr((_handler), irq_handler_t),	\
> 	.flags = _flags,					\
> }	
> 
> #define DEVM_IRQ_NAMED(_struct, _member, _name, _handler, _flags) { \
> 	.initfunc = devm_probe_irq,				\
> 	.offset = offsetof_t(struct _struct, _member, int),	\
> 	.name = _name,						\
> 	.arg = typecheck_ptr((_handler), irq_handler_t),	\
> 	.flags = _flags,					\
> }	
> 
> 
> #define DEVM_IOMAP_NOREQUEST 1
> 
> int devm_probe_iomap(struct device *dev, const struct devm_probe *probe)
> {
> 	struct resource res, *resp;
> 	void __iomem *vaddr;
> 	void * __iomem *vaddrp;
> 	int ret;
> 
> 	/* find mem resource from platform device */
> 	if (dev->bus_type == &platform_bus) {
> 		struct platform_device *pdev = to_platform_device(dev);
> 
> 		if (probe->name)
> 			resp = platform_get_resource_byname(pdev,
> 					IORESOURCE_MEM, probe->name);
> 		else
> 			resp = platform_get_resource(pdev,
> 					IORESOURCE_MEM, probe->index);
> 	}
> 
> 	/* try getting resource from device tree if that failed */
> 	if (!resp && dev->of_node) {
> 		if (probe->name)
> 			index = of_property_match_string(dev->of_node,
> 							 "reg-names",
> 							 probe->name);
> 		else
> 			index = probe->index;
> 
> 		ret = of_address_to_resource(dev->of_node, index, &res);
> 		if (ret)
> 			return ret;
> 		resp = &res;
> 	}
> 
> 
> 	if (probe->flags & DEVM_IOMAP_NOREQUEST) {
> 		vaddr = devm_ioremap(dev, resp->start, resource_size(resp));
> 		if (!vaddr)
> 			return -EINVAL;
> 	} else {
> 		vaddr = devm_ioremap_resource(dev, resp);
> 		if (IS_ERR(vaddr))
> 			return PTR_ERR(vaddr);
> 	}
> 
> 	vaddrp = dev_get_drvdata(dev) + probe->offset;
> 	*vaddrp = vaddr;
> 
> 	return 0;
> }
> EXPORT_SYMBOL_GPL(devm_probe_iomap);
> 
> #define DEVM_IOMAP(_struct, _member, _index, _flags) {			\
> 	.initfunc = devm_probe_iomap,					\
> 	.offset = offsetof_t(struct _struct, _member, void __iomem *),	\
> 	.index = _index,						\
> 	.flags = _flags,						\
> }	
> 
> #define DEVM_IOMAP_NAMED(_struct, _member, _name, _flags) { 		\
> 	.initfunc = devm_probe_iomap,					\
> 	.offset = offsetof_t(struct _struct, _member, void __iomem *),	\
> 	.name = _name,							\
> 	.flags = _flags,						\
> }	
> 
> int devm_probe_gpio(struct device *dev, const struct devm_probe *probe)
> {
> 	struct gpio_desc *desc, **descp;
> 
> 	desc = devm_gpiod_get_index(dev, probe->name, probe->index);
> 	if (IS_ERR(desc)) {
> 		/* FIXME: this looks wrong */
> 		desc = of_get_named_gpiod_flags(dev->of_node, probe->name,
> 						probe->index, NULL);
> 		if (IS_ERR(desc))
> 			return PTR_ERR(desc);
> 		devm_gpio_request(dev, desc_to_gpio(desc), probe->name);
> 	}
> 
> 	descp = dev_get_drvdata(dev) + probe->offset;
> 	*descp = desc;
> 
> 	return 0;
> }
> 
> #define DEVM_GPIO(_struct, _member, _index) {				 \
> 	.initfunc = devm_probe_iomap,					 \
> 	.offset = offsetof_t(struct _struct, _member, struct gpio_desc*),\
> 	.name = "gpios",						 \
> 	.index = _index,						 \
> }	
> 
> #define DEVM_GPIO_NAMED(_struct, _member, _name) { 			 \
> 	.initfunc = devm_probe_iomap,					 \
> 	.offset = offsetof_t(struct _struct, _member, struct gpio_desc*),\
> 	.name = _name,							 \
> }	
> 
> static void devm_probe_dma_release(struct device *dev, void *chanp)
> {
> 	dma_release_channel(*(struct dma_chan**)chanp);
> }
> 
> int devm_probe_dma(struct device *dev, const struct devm_probe *probe)
> {
> 	struct dma_chan *chan, **chanp;
> 
> 	/* there is no devm_dma_request_channel yet, so build our own */
> 	chanp = devres_alloc(devm_probe_dma_release, sizeof(chanp), GFP_KERNEL);
> 	if (!chanp)
> 		return -ENOMEM;
> 
> 	chan = dma_request_slave_channel(dev, probe->name);
> 	if (!chan) {
> 		devres_free(chanp);
> 		return -EINVAL;
> 	}
> 
> 	*chanp = chan;
> 	devres_add(dev, chanp);
> 
> 	/* add pointer to the private data */
> 	chanp = dev_get_drvdata(dev) + probe->offset;
> 	*chanp = chan;
> 
> 	return 0;
> }
> 
> #define DEVM_DMA_SLAVE(_struct, _member, _name)				\
> 	.offset = offsetof_t(struct _struct, _member, struct dma_chan*),\
> 	.name = _name,							\
> }
> 
> /*
>  * simple properties: bool, u32, string
>  * no actual need for managed interfaces, just a way to abstract the
>  * access to DT or other information source
>  */
> int devm_probe_prop_bool(struct device *dev, struct devm_probe *probe)
> {
> 	bool *val;
> 
> 	val = dev_get_drvdata(dev) + probe->offset;
> 	*val = of_property_read_bool(dev->of_node, probe->name);
> 
> 	return 0;
> }
> EXPORT_SYMBOL_GPL(devm_probe_prop_bool);
> 
> #define DEVM_PROP_BOOL(_struct, _member, _name)			\
> 	.offset = offsetof_t(struct _struct, _member, bool),	\
> 	.name = _name,						\
> }
> 
> int devm_probe_prop_u32(struct device *dev, struct devm_probe *probe)
> {
> 	u32 *val;
> 	int ret;
> 
> 	val = dev_get_drvdata(dev) + probe->offset;
> 	ret = of_property_read_u32(dev->of_node, probe->name, val);
> 
> 	return ret;
> }
> EXPORT_SYMBOL_GPL(devm_probe_prop_bool);
> 
> #define DEVM_PROP_U32(_struct, _member, _name)			\
> 	.offset = offsetof_t(struct _struct, _member, u32),	\
> 	.name = _name,						\
> }
> 
> int devm_probe_prop_string(struct device *dev, struct devm_probe *probe)
> {
> 	const char **val;
> 	int ret;
> 
> 	val = dev_get_drvdata(dev) + probe->offset;
> 	ret = of_property_read_string(dev->of_node, probe->name, val);
> 
> 	return ret;
> }
> EXPORT_SYMBOL_GPL(devm_probe_prop_bool);
> 
> #define DEVM_PROP_STRING(_struct, _member, _name)			\
> 	.offset = offsetof_t(struct _struct, _member, const char *),	\
> 	.name = _name,							\
> }
> 
> /* example driver */
> struct foo_priv {
> 	spinlock_t lock;
> 	void __iomem *regs;
> 	int irq;
> 	struct gpio_desc *gpio;
> 	struct dma_chan *rxdma;
> 	struct dma_chan *txdma;
> 	bool oldstyle_dma;
> };
> 
> static irqreturn_t foo_irq_handler(int irq, void *dev);
> 
> /*
>  * this lists all properties we access from the driver. The list
>  * is interpreted by devm_probe() and can be programmatically
>  * verified to match the binding.
>  */
> static const struct devm_probe foo_probe_list[] = {
> 	DEVM_ALLOC(foo_priv),
> 	DEVM_IOMAP(foo_priv, regs, 0, 0),
> 	DEVM_PROP_BOOL(foo_priv, oldstyle_dma, "foo,oldstyle-dma"),
> 	DEVM_DMA_SLAVE(foo_priv, rxdma, "rx");
> 	DEVM_DMA_SLAVE(foo_priv, txdma, "tx");
> 	DEVM_GPIO(foo_priv, gpio, 0);
> 	DEVM_IRQ_NAMED(foo_priv, irq, foo_irq_handler, "fifo", IRQF_SHARED),
> 	{},
> };
> 
> static int foo_probe(struct platform_device *dev)
> {
> 	int ret;
> 
> 	ret = devm_probe(dev->dev, foo_probe_list);
> 	if (ret)
> 		return ret;
> 
> 	return bar_subsystem_register(&foo_bar_ops, dev);
> }
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20131106/4a78b7af/attachment-0001.sig>


More information about the linux-arm-kernel mailing list