diff mbox

[v2,4/8] gpio: pcie-idio-24: Implement get_multiple/set_multiple callbacks

Message ID 41c682a781e20b2db55491d36d600d56bb6b5c66.1521128287.git.vilhelm.gray@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

William Breathitt Gray March 15, 2018, 3:52 p.m. UTC
The ACCES I/O PCIe-IDIO-24 series of devices provides 24
optically-isolated digital I/O accessed via six 8-bit ports. Since eight
input lines are acquired on a single port input read -- and similarly
eight output lines are set on a single port output write -- the
PCIe-IDIO-24 GPIO driver may improve multiple I/O reads/writes by
utilizing a get_multiple/set_multiple callbacks. This patch implements
the idio_24_gpio_get_multiple function which serves as the respective
get_multiple callback, and implements the idio_24_gpio_set_multiple
function which serves as the respective set_multiple callback.

Signed-off-by: William Breathitt Gray <vilhelm.gray@gmail.com>
---
 drivers/gpio/gpio-pcie-idio-24.c | 118 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)

Comments

kernel test robot March 17, 2018, 9:01 p.m. UTC | #1
Hi William,

I love your patch! Perhaps something to improve:

[auto build test WARNING on v4.16-rc4]
[also build test WARNING on next-20180316]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/William-Breathitt-Gray/Implement-get_multiple-for-ACCES-and-PC-104-drivers/20180317-224135
reproduce:
        # apt-get install sparse
        make ARCH=x86_64 allmodconfig
        make C=1 CF=-D__CHECK_ENDIAN__


sparse warnings: (new ones prefixed by >>)

   drivers/gpio/gpio-pcie-idio-24.c:231:17: sparse: undefined identifier 'word_mask'
   drivers/gpio/gpio-pcie-idio-24.c:232:22: sparse: undefined identifier 'word_mask'
>> drivers/gpio/gpio-pcie-idio-24.c:331:43: sparse: incorrect type in argument 1 (different modifiers) @@    expected void [noderef] <asn:2>*<noident> @@    got unsigned char cvoid [noderef] <asn:2>*<noident> @@
   drivers/gpio/gpio-pcie-idio-24.c:333:43: sparse: incorrect type in argument 2 (different modifiers) @@    expected void [noderef] <asn:2>*<noident> @@    got unsigned char cvoid [noderef] <asn:2>*<noident> @@
>> drivers/gpio/gpio-pcie-idio-24.c:518:30: sparse: incorrect type in assignment (incompatible argument 2 (different base types)) @@    expected void ( *set )( ... ) @@    got void ( *set )( ... ) @@
>> drivers/gpio/gpio-pcie-idio-24.c:210:27: sparse: dereference of noderef expression
   drivers/gpio/gpio-pcie-idio-24.c:210:52: sparse: dereference of noderef expression
   drivers/gpio/gpio-pcie-idio-24.c:211:27: sparse: dereference of noderef expression
   drivers/gpio/gpio-pcie-idio-24.c:211:54: sparse: dereference of noderef expression
   drivers/gpio/gpio-pcie-idio-24.c:212:27: sparse: dereference of noderef expression
   drivers/gpio/gpio-pcie-idio-24.c:212:52: sparse: dereference of noderef expression
>> drivers/gpio/gpio-pcie-idio-24.c:231:17: sparse: generating address of non-lvalue (3)
   drivers/gpio/gpio-pcie-idio-24.c:306:27: sparse: dereference of noderef expression
   drivers/gpio/gpio-pcie-idio-24.c:306:52: sparse: dereference of noderef expression
   drivers/gpio/gpio-pcie-idio-24.c:307:27: sparse: dereference of noderef expression
   drivers/gpio/gpio-pcie-idio-24.c: In function 'idio_24_gpio_get_multiple':
   drivers/gpio/gpio-pcie-idio-24.c:231:3: error: 'word_mask' undeclared (first use in this function); did you mean 'port_mask'?
      word_mask = mask[word_index] & (port_mask << word_offset);
      ^~~~~~~~~
      port_mask
   drivers/gpio/gpio-pcie-idio-24.c:231:3: note: each undeclared identifier is reported only once for each function it appears in
   drivers/gpio/gpio-pcie-idio-24.c:239:25: warning: passing argument 1 of 'ioread8' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
       port_state = ioread8(ports + i);
                            ^~~~~
   In file included from arch/x86/include/asm/io.h:223:0,
                    from arch/x86/include/asm/realmode.h:15,
                    from arch/x86/include/asm/acpi.h:33,
                    from arch/x86/include/asm/fixmap.h:19,
                    from arch/x86/include/asm/apic.h:10,
                    from arch/x86/include/asm/smp.h:13,
                    from arch/x86/include/asm/mmzone_64.h:11,
                    from arch/x86/include/asm/mmzone.h:5,
                    from include/linux/mmzone.h:912,
                    from include/linux/gfp.h:6,
                    from include/linux/idr.h:16,
                    from include/linux/kernfs.h:14,
                    from include/linux/sysfs.h:16,
                    from include/linux/kobject.h:20,
                    from include/linux/device.h:16,
                    from drivers/gpio/gpio-pcie-idio-24.c:20:
   include/asm-generic/iomap.h:29:21: note: expected 'void *' but argument is of type 'const u8 * {aka const unsigned char *}'
    extern unsigned int ioread8(void __iomem *);
                        ^~~~~~~
   drivers/gpio/gpio-pcie-idio-24.c:206:16: warning: unused variable 'mask_word' [-Wunused-variable]
     unsigned long mask_word;
                   ^~~~~~~~~
   drivers/gpio/gpio-pcie-idio-24.c: In function 'idio_24_gpio_set_multiple':
   drivers/gpio/gpio-pcie-idio-24.c:302:16: error: redeclaration of 'gpio_mask' with no linkage
     unsigned long gpio_mask;
                   ^~~~~~~~~
   drivers/gpio/gpio-pcie-idio-24.c:299:16: note: previous declaration of 'gpio_mask' was here
     unsigned long gpio_mask;
                   ^~~~~~~~~
   drivers/gpio/gpio-pcie-idio-24.c:331:23: warning: passing argument 1 of 'ioread8' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
      out_state = ioread8(ports + i) & ~gpio_mask;
                          ^~~~~
   In file included from arch/x86/include/asm/io.h:223:0,
                    from arch/x86/include/asm/realmode.h:15,
                    from arch/x86/include/asm/acpi.h:33,
                    from arch/x86/include/asm/fixmap.h:19,
                    from arch/x86/include/asm/apic.h:10,
                    from arch/x86/include/asm/smp.h:13,
                    from arch/x86/include/asm/mmzone_64.h:11,
                    from arch/x86/include/asm/mmzone.h:5,
                    from include/linux/mmzone.h:912,
                    from include/linux/gfp.h:6,
                    from include/linux/idr.h:16,
                    from include/linux/kernfs.h:14,
                    from include/linux/sysfs.h:16,
                    from include/linux/kobject.h:20,
                    from include/linux/device.h:16,
                    from drivers/gpio/gpio-pcie-idio-24.c:20:
   include/asm-generic/iomap.h:29:21: note: expected 'void *' but argument is of type 'const u8 * {aka const unsigned char *}'
    extern unsigned int ioread8(void __iomem *);
                        ^~~~~~~
   drivers/gpio/gpio-pcie-idio-24.c:333:23: warning: passing argument 2 of 'iowrite8' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
      iowrite8(out_state, ports + i);
                          ^~~~~
   In file included from arch/x86/include/asm/io.h:223:0,
                    from arch/x86/include/asm/realmode.h:15,
                    from arch/x86/include/asm/acpi.h:33,
                    from arch/x86/include/asm/fixmap.h:19,
                    from arch/x86/include/asm/apic.h:10,
                    from arch/x86/include/asm/smp.h:13,
                    from arch/x86/include/asm/mmzone_64.h:11,
                    from arch/x86/include/asm/mmzone.h:5,
                    from include/linux/mmzone.h:912,
                    from include/linux/gfp.h:6,
                    from include/linux/idr.h:16,
                    from include/linux/kernfs.h:14,
                    from include/linux/sysfs.h:16,
                    from include/linux/kobject.h:20,
                    from include/linux/device.h:16,
                    from drivers/gpio/gpio-pcie-idio-24.c:20:
   include/asm-generic/iomap.h:39:13: note: expected 'void *' but argument is of type 'const u8 * {aka const unsigned char *}'
    extern void iowrite8(u8, void __iomem *);
                ^~~~~~~~
   drivers/gpio/gpio-pcie-idio-24.c: In function 'idio_24_probe':
   drivers/gpio/gpio-pcie-idio-24.c:518:23: error: assignment from incompatible pointer type [-Werror=incompatible-pointer-types]
     idio24gpio->chip.set = idio_24_gpio_set_multiple;
                          ^
   cc1: some warnings being treated as errors

vim +331 drivers/gpio/gpio-pcie-idio-24.c

   196	
   197	static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
   198		unsigned long *mask, unsigned long *bits)
   199	{
   200		struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
   201		size_t i;
   202		const unsigned int gpio_reg_size = 8;
   203		unsigned int bits_offset;
   204		size_t word_index;
   205		unsigned int word_offset;
   206		unsigned long mask_word;
   207		const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
   208		unsigned long port_state;
   209		const u8 __iomem ports[] = {
 > 210			idio24gpio->reg->out0_7, idio24gpio->reg->out8_15,
   211			idio24gpio->reg->out16_23, idio24gpio->reg->in0_7,
   212			idio24gpio->reg->in8_15, idio24gpio->reg->in16_23
   213		};
   214		const unsigned long out_mode_mask = BIT(1);
   215	
   216		/* clear bits array to a clean slate */
   217		bitmap_zero(bits, chip->ngpio);
   218	
   219		/* get bits are evaluated a gpio port register at a time */
   220		for (i = 0; i < ARRAY_SIZE(ports); i++) {
   221			/* gpio offset in bits array */
   222			bits_offset = i * gpio_reg_size;
   223	
   224			/* word index for bits array */
   225			word_index = BIT_WORD(bits_offset);
   226	
   227			/* gpio offset within current word of bits array */
   228			word_offset = bits_offset % BITS_PER_LONG;
   229	
   230			/* mask of get bits for current gpio within current word */
 > 231			word_mask = mask[word_index] & (port_mask << word_offset);
   232			if (!word_mask) {
   233				/* no get bits in this port so skip to next one */
   234				continue;
   235			}
   236	
   237			/* read bits from current gpio port (port 6 is TTL GPIO) */
   238			if (i < 6)
   239				port_state = ioread8(ports + i);
   240			else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
   241				port_state = ioread8(&idio24gpio->reg->ttl_out0_7);
   242			else
   243				port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
   244	
   245			/* store acquired bits at respective bits array offset */
   246			bits[word_index] |= port_state << word_offset;
   247		}
   248	
   249		return 0;
   250	}
   251	
   252	static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
   253		int value)
   254	{
   255		struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
   256		const unsigned long out_mode_mask = BIT(1);
   257		void __iomem *base;
   258		const unsigned int mask = BIT(offset % 8);
   259		unsigned long flags;
   260		unsigned int out_state;
   261	
   262		/* Isolated Inputs */
   263		if (offset > 23 && offset < 48)
   264			return;
   265	
   266		/* TTL/CMOS Inputs */
   267		if (offset > 47 && !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
   268			return;
   269	
   270		/* TTL/CMOS Outputs */
   271		if (offset > 47)
   272			base = &idio24gpio->reg->ttl_out0_7;
   273		/* FET Outputs */
   274		else if (offset > 15)
   275			base = &idio24gpio->reg->out16_23;
   276		else if (offset > 7)
   277			base = &idio24gpio->reg->out8_15;
   278		else
   279			base = &idio24gpio->reg->out0_7;
   280	
   281		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
   282	
   283		if (value)
   284			out_state = ioread8(base) | mask;
   285		else
   286			out_state = ioread8(base) & ~mask;
   287	
   288		iowrite8(out_state, base);
   289	
   290		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
   291	}
   292	
   293	static void idio_24_gpio_set_multiple(struct gpio_chip *chip,
   294		unsigned long *mask, unsigned long *bits)
   295	{
   296		struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
   297		size_t i;
   298		unsigned long bits_offset;
   299		unsigned long gpio_mask;
   300		const unsigned int gpio_reg_size = 8;
   301		const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
   302		unsigned long gpio_mask;
   303		unsigned long flags;
   304		unsigned int out_state;
   305		const u8 __iomem ports[] = {
   306			idio24gpio->reg->out0_7, idio24gpio->reg->out8_15,
   307			idio24gpio->reg->out16_23
   308		};
   309		const unsigned long out_mode_mask = BIT(1);
   310		const unsigned int ttl_offset = 48;
   311		const size_t ttl_i = BIT_WORD(ttl_offset);
   312		const unsigned int word_offset = ttl_offset % BITS_PER_LONG;
   313		const unsigned long ttl_mask = (mask[ttl_i] >> word_offset) & port_mask;
   314		const unsigned long ttl_bits = (bits[ttl_i] >> word_offset) & ttl_mask;
   315	
   316		/* set bits are processed a gpio port register at a time */
   317		for (i = 0; i < ARRAY_SIZE(ports); i++) {
   318			/* gpio offset in bits array */
   319			bits_offset = i * gpio_reg_size;
   320	
   321			/* check if any set bits for current port */
   322			gpio_mask = (*mask >> bits_offset) & port_mask;
   323			if (!gpio_mask) {
   324				/* no set bits for this port so move on to next port */
   325				continue;
   326			}
   327	
   328			raw_spin_lock_irqsave(&idio24gpio->lock, flags);
   329	
   330			/* process output lines */
 > 331			out_state = ioread8(ports + i) & ~gpio_mask;
   332			out_state |= (*bits >> bits_offset) & gpio_mask;
   333			iowrite8(out_state, ports + i);
   334	
   335			raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
   336		}
   337	
   338		/* check if setting TTL lines and if they are in output mode */
   339		if (!ttl_mask || !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
   340			return;
   341	
   342		/* handle TTL output */
   343		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
   344	
   345		/* process output lines */
   346		out_state = ioread8(&idio24gpio->reg->ttl_out0_7) & ~ttl_mask;
   347		out_state |= ttl_bits;
   348		iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
   349	
   350		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
   351	}
   352	
   353	static void idio_24_irq_ack(struct irq_data *data)
   354	{
   355	}
   356	
   357	static void idio_24_irq_mask(struct irq_data *data)
   358	{
   359		struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
   360		struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
   361		unsigned long flags;
   362		const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
   363		unsigned char new_irq_mask;
   364		const unsigned long bank_offset = bit_offset/8 * 8;
   365		unsigned char cos_enable_state;
   366	
   367		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
   368	
   369		idio24gpio->irq_mask &= BIT(bit_offset);
   370		new_irq_mask = idio24gpio->irq_mask >> bank_offset;
   371	
   372		if (!new_irq_mask) {
   373			cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
   374	
   375			/* Disable Rising Edge detection */
   376			cos_enable_state &= ~BIT(bank_offset);
   377			/* Disable Falling Edge detection */
   378			cos_enable_state &= ~BIT(bank_offset + 4);
   379	
   380			iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
   381		}
   382	
   383		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
   384	}
   385	
   386	static void idio_24_irq_unmask(struct irq_data *data)
   387	{
   388		struct gpio_chip *const chip = irq_data_get_irq_chip_data(data);
   389		struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
   390		unsigned long flags;
   391		unsigned char prev_irq_mask;
   392		const unsigned long bit_offset = irqd_to_hwirq(data) - 24;
   393		const unsigned long bank_offset = bit_offset/8 * 8;
   394		unsigned char cos_enable_state;
   395	
   396		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
   397	
   398		prev_irq_mask = idio24gpio->irq_mask >> bank_offset;
   399		idio24gpio->irq_mask |= BIT(bit_offset);
   400	
   401		if (!prev_irq_mask) {
   402			cos_enable_state = ioread8(&idio24gpio->reg->cos_enable);
   403	
   404			/* Enable Rising Edge detection */
   405			cos_enable_state |= BIT(bank_offset);
   406			/* Enable Falling Edge detection */
   407			cos_enable_state |= BIT(bank_offset + 4);
   408	
   409			iowrite8(cos_enable_state, &idio24gpio->reg->cos_enable);
   410		}
   411	
   412		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
   413	}
   414	
   415	static int idio_24_irq_set_type(struct irq_data *data, unsigned int flow_type)
   416	{
   417		/* The only valid irq types are none and both-edges */
   418		if (flow_type != IRQ_TYPE_NONE &&
   419			(flow_type & IRQ_TYPE_EDGE_BOTH) != IRQ_TYPE_EDGE_BOTH)
   420			return -EINVAL;
   421	
   422		return 0;
   423	}
   424	
   425	static struct irq_chip idio_24_irqchip = {
   426		.name = "pcie-idio-24",
   427		.irq_ack = idio_24_irq_ack,
   428		.irq_mask = idio_24_irq_mask,
   429		.irq_unmask = idio_24_irq_unmask,
   430		.irq_set_type = idio_24_irq_set_type
   431	};
   432	
   433	static irqreturn_t idio_24_irq_handler(int irq, void *dev_id)
   434	{
   435		struct idio_24_gpio *const idio24gpio = dev_id;
   436		unsigned long irq_status;
   437		struct gpio_chip *const chip = &idio24gpio->chip;
   438		unsigned long irq_mask;
   439		int gpio;
   440	
   441		raw_spin_lock(&idio24gpio->lock);
   442	
   443		/* Read Change-Of-State status */
   444		irq_status = ioread32(&idio24gpio->reg->cos0_7);
   445	
   446		raw_spin_unlock(&idio24gpio->lock);
   447	
   448		/* Make sure our device generated IRQ */
   449		if (!irq_status)
   450			return IRQ_NONE;
   451	
   452		/* Handle only unmasked IRQ */
   453		irq_mask = idio24gpio->irq_mask & irq_status;
   454	
   455		for_each_set_bit(gpio, &irq_mask, chip->ngpio - 24)
   456			generic_handle_irq(irq_find_mapping(chip->irq.domain,
   457				gpio + 24));
   458	
   459		raw_spin_lock(&idio24gpio->lock);
   460	
   461		/* Clear Change-Of-State status */
   462		iowrite32(irq_status, &idio24gpio->reg->cos0_7);
   463	
   464		raw_spin_unlock(&idio24gpio->lock);
   465	
   466		return IRQ_HANDLED;
   467	}
   468	
   469	#define IDIO_24_NGPIO 56
   470	static const char *idio_24_names[IDIO_24_NGPIO] = {
   471		"OUT0", "OUT1", "OUT2", "OUT3", "OUT4", "OUT5", "OUT6", "OUT7",
   472		"OUT8", "OUT9", "OUT10", "OUT11", "OUT12", "OUT13", "OUT14", "OUT15",
   473		"OUT16", "OUT17", "OUT18", "OUT19", "OUT20", "OUT21", "OUT22", "OUT23",
   474		"IIN0", "IIN1", "IIN2", "IIN3", "IIN4", "IIN5", "IIN6", "IIN7",
   475		"IIN8", "IIN9", "IIN10", "IIN11", "IIN12", "IIN13", "IIN14", "IIN15",
   476		"IIN16", "IIN17", "IIN18", "IIN19", "IIN20", "IIN21", "IIN22", "IIN23",
   477		"TTL0", "TTL1", "TTL2", "TTL3", "TTL4", "TTL5", "TTL6", "TTL7"
   478	};
   479	
   480	static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
   481	{
   482		struct device *const dev = &pdev->dev;
   483		struct idio_24_gpio *idio24gpio;
   484		int err;
   485		const size_t pci_bar_index = 2;
   486		const char *const name = pci_name(pdev);
   487	
   488		idio24gpio = devm_kzalloc(dev, sizeof(*idio24gpio), GFP_KERNEL);
   489		if (!idio24gpio)
   490			return -ENOMEM;
   491	
   492		err = pcim_enable_device(pdev);
   493		if (err) {
   494			dev_err(dev, "Failed to enable PCI device (%d)\n", err);
   495			return err;
   496		}
   497	
   498		err = pcim_iomap_regions(pdev, BIT(pci_bar_index), name);
   499		if (err) {
   500			dev_err(dev, "Unable to map PCI I/O addresses (%d)\n", err);
   501			return err;
   502		}
   503	
   504		idio24gpio->reg = pcim_iomap_table(pdev)[pci_bar_index];
   505	
   506		idio24gpio->chip.label = name;
   507		idio24gpio->chip.parent = dev;
   508		idio24gpio->chip.owner = THIS_MODULE;
   509		idio24gpio->chip.base = -1;
   510		idio24gpio->chip.ngpio = IDIO_24_NGPIO;
   511		idio24gpio->chip.names = idio_24_names;
   512		idio24gpio->chip.get_direction = idio_24_gpio_get_direction;
   513		idio24gpio->chip.direction_input = idio_24_gpio_direction_input;
   514		idio24gpio->chip.direction_output = idio_24_gpio_direction_output;
   515		idio24gpio->chip.get = idio_24_gpio_get;
   516		idio24gpio->chip.get_multiple = idio_24_gpio_get_multiple;
   517		idio24gpio->chip.set = idio_24_gpio_set;
 > 518		idio24gpio->chip.set = idio_24_gpio_set_multiple;
   519	
   520		raw_spin_lock_init(&idio24gpio->lock);
   521	
   522		/* Software board reset */
   523		iowrite8(0, &idio24gpio->reg->soft_reset);
   524	
   525		err = devm_gpiochip_add_data(dev, &idio24gpio->chip, idio24gpio);
   526		if (err) {
   527			dev_err(dev, "GPIO registering failed (%d)\n", err);
   528			return err;
   529		}
   530	
   531		err = gpiochip_irqchip_add(&idio24gpio->chip, &idio_24_irqchip, 0,
   532			handle_edge_irq, IRQ_TYPE_NONE);
   533		if (err) {
   534			dev_err(dev, "Could not add irqchip (%d)\n", err);
   535			return err;
   536		}
   537	
   538		err = devm_request_irq(dev, pdev->irq, idio_24_irq_handler, IRQF_SHARED,
   539			name, idio24gpio);
   540		if (err) {
   541			dev_err(dev, "IRQ handler registering failed (%d)\n", err);
   542			return err;
   543		}
   544	
   545		return 0;
   546	}
   547	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/gpio/gpio-pcie-idio-24.c b/drivers/gpio/gpio-pcie-idio-24.c
index f666e2e69074..8d2f4745a13c 100644
--- a/drivers/gpio/gpio-pcie-idio-24.c
+++ b/drivers/gpio/gpio-pcie-idio-24.c
@@ -15,6 +15,7 @@ 
  * This driver supports the following ACCES devices: PCIe-IDIO-24,
  * PCIe-IDI-24, PCIe-IDO-24, and PCIe-IDIO-12.
  */
+#include <linux/bitmap.h>
 #include <linux/bitops.h>
 #include <linux/device.h>
 #include <linux/errno.h>
@@ -193,6 +194,61 @@  static int idio_24_gpio_get(struct gpio_chip *chip, unsigned int offset)
 	return !!(ioread8(&idio24gpio->reg->ttl_in0_7) & offset_mask);
 }
 
+static int idio_24_gpio_get_multiple(struct gpio_chip *chip,
+	unsigned long *mask, unsigned long *bits)
+{
+	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
+	size_t i;
+	const unsigned int gpio_reg_size = 8;
+	unsigned int bits_offset;
+	size_t word_index;
+	unsigned int word_offset;
+	unsigned long mask_word;
+	const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
+	unsigned long port_state;
+	const u8 __iomem ports[] = {
+		idio24gpio->reg->out0_7, idio24gpio->reg->out8_15,
+		idio24gpio->reg->out16_23, idio24gpio->reg->in0_7,
+		idio24gpio->reg->in8_15, idio24gpio->reg->in16_23
+	};
+	const unsigned long out_mode_mask = BIT(1);
+
+	/* clear bits array to a clean slate */
+	bitmap_zero(bits, chip->ngpio);
+
+	/* get bits are evaluated a gpio port register at a time */
+	for (i = 0; i < ARRAY_SIZE(ports); i++) {
+		/* gpio offset in bits array */
+		bits_offset = i * gpio_reg_size;
+
+		/* word index for bits array */
+		word_index = BIT_WORD(bits_offset);
+
+		/* gpio offset within current word of bits array */
+		word_offset = bits_offset % BITS_PER_LONG;
+
+		/* mask of get bits for current gpio within current word */
+		word_mask = mask[word_index] & (port_mask << word_offset);
+		if (!word_mask) {
+			/* no get bits in this port so skip to next one */
+			continue;
+		}
+
+		/* read bits from current gpio port (port 6 is TTL GPIO) */
+		if (i < 6)
+			port_state = ioread8(ports + i);
+		else if (ioread8(&idio24gpio->reg->ctl) & out_mode_mask)
+			port_state = ioread8(&idio24gpio->reg->ttl_out0_7);
+		else
+			port_state = ioread8(&idio24gpio->reg->ttl_in0_7);
+
+		/* store acquired bits at respective bits array offset */
+		bits[word_index] |= port_state << word_offset;
+	}
+
+	return 0;
+}
+
 static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
 	int value)
 {
@@ -234,6 +290,66 @@  static void idio_24_gpio_set(struct gpio_chip *chip, unsigned int offset,
 	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
 }
 
+static void idio_24_gpio_set_multiple(struct gpio_chip *chip,
+	unsigned long *mask, unsigned long *bits)
+{
+	struct idio_24_gpio *const idio24gpio = gpiochip_get_data(chip);
+	size_t i;
+	unsigned long bits_offset;
+	unsigned long gpio_mask;
+	const unsigned int gpio_reg_size = 8;
+	const unsigned long port_mask = GENMASK(gpio_reg_size, 0);
+	unsigned long gpio_mask;
+	unsigned long flags;
+	unsigned int out_state;
+	const u8 __iomem ports[] = {
+		idio24gpio->reg->out0_7, idio24gpio->reg->out8_15,
+		idio24gpio->reg->out16_23
+	};
+	const unsigned long out_mode_mask = BIT(1);
+	const unsigned int ttl_offset = 48;
+	const size_t ttl_i = BIT_WORD(ttl_offset);
+	const unsigned int word_offset = ttl_offset % BITS_PER_LONG;
+	const unsigned long ttl_mask = (mask[ttl_i] >> word_offset) & port_mask;
+	const unsigned long ttl_bits = (bits[ttl_i] >> word_offset) & ttl_mask;
+
+	/* set bits are processed a gpio port register at a time */
+	for (i = 0; i < ARRAY_SIZE(ports); i++) {
+		/* gpio offset in bits array */
+		bits_offset = i * gpio_reg_size;
+
+		/* check if any set bits for current port */
+		gpio_mask = (*mask >> bits_offset) & port_mask;
+		if (!gpio_mask) {
+			/* no set bits for this port so move on to next port */
+			continue;
+		}
+
+		raw_spin_lock_irqsave(&idio24gpio->lock, flags);
+
+		/* process output lines */
+		out_state = ioread8(ports + i) & ~gpio_mask;
+		out_state |= (*bits >> bits_offset) & gpio_mask;
+		iowrite8(out_state, ports + i);
+
+		raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
+	}
+
+	/* check if setting TTL lines and if they are in output mode */
+	if (!ttl_mask || !(ioread8(&idio24gpio->reg->ctl) & out_mode_mask))
+		return;
+
+	/* handle TTL output */
+	raw_spin_lock_irqsave(&idio24gpio->lock, flags);
+
+	/* process output lines */
+	out_state = ioread8(&idio24gpio->reg->ttl_out0_7) & ~ttl_mask;
+	out_state |= ttl_bits;
+	iowrite8(out_state, &idio24gpio->reg->ttl_out0_7);
+
+	raw_spin_unlock_irqrestore(&idio24gpio->lock, flags);
+}
+
 static void idio_24_irq_ack(struct irq_data *data)
 {
 }
@@ -397,7 +513,9 @@  static int idio_24_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	idio24gpio->chip.direction_input = idio_24_gpio_direction_input;
 	idio24gpio->chip.direction_output = idio_24_gpio_direction_output;
 	idio24gpio->chip.get = idio_24_gpio_get;
+	idio24gpio->chip.get_multiple = idio_24_gpio_get_multiple;
 	idio24gpio->chip.set = idio_24_gpio_set;
+	idio24gpio->chip.set = idio_24_gpio_set_multiple;
 
 	raw_spin_lock_init(&idio24gpio->lock);