mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-31 04:01:48 -04:00
This is the bulk of GPIO changes for the v4.17 kernel cycle:
New drivers:
- Nintendo Wii GameCube GPIO, known as "Hollywood"
- Raspberry Pi mailbox service GPIO expander
- Spreadtrum main SC9860 SoC and IEC GPIO controllers.
Improvements:
- Implemented .get_multiple() callback for most of the
high-performance industrial GPIO cards for the ISA bus.
- ISA GPIO drivers now select the ISA_BUS_API instead of
depending on it. This is merged with the same pattern
for all the ISA drivers and some other Kconfig cleanups
related to this.
Cleanup:
- Delete the TZ1090 GPIO drivers following the deletion of
this SoC from the ARM tree.
- Move the documentation over to driver-api to conform with
the rest of the kernel documentation build.
- Continue to make the GPIO drivers include only
<linux/gpio/driver.h> and not the too broad <linux/gpio.h>
that we want to get rid of.
- Managed to remove VLA allocation from two drivers pending
more fixes in this area for the next merge window.
- Misc janitorial fixes.
-----BEGIN PGP SIGNATURE-----
iQIcBAABAgAGBQJaxIehAAoJEEEQszewGV1zlEAP/3p3E6J8vPqJNV/C39c40krC
ajo0ndiTC7cotmCXNQOl9xfMCTgkjBtx3WEKwTDfCsuWW+2YB0DRmMd0Bkf2RWjQ
nM4rB64FzAu+rdD9jdGtfn24ofylSRFaHNQ/V8Prc2JVAXJt4DS97h+6kwzIAqCm
A/xXQAx67k5qoTXLvR2n/8LX8TphSe2kwH/f/3/lJpNLfLCRRJ3GqJfpa72jw2eL
4VIPc6KmttkqzJ1GFtzLPfhkhRr0p4sSzUNydlj5BKhmOSVu6Afv5ylgpK/p38dQ
mGvNqFnU0lpwelsoZK75YikDFbqQjn4XkXJGvmIRMw4qM7crcw5oSkeMwCrcGqJW
7Uo7NoQU94wcQSZTppFQdaJs7NHdcnpW7jcfRYYetZL/6eDGBtfxoym90Lyjvaqs
y+ykofbadI0X/9omO5j+qozvIneLam/CF7iDRUb/5t1LJbNwtXUsVYhz3FuwPDt1
ZHb6w+np9ZHN6H9jz3b/F9B/uQt54pshm7NorSXrJvZfKrv8kV14MoHgYsuQDDjV
khbveygB8DwaPeV4XjpLeYhJB1L/Wjf46CVD6tyaCRDByGQmdoJEQF9QB2CxrF2J
ouaaaS8tSC0IK/mKMMgJxC1Vr2gh0NMlQ3AL9EJDJvX+9RoIA2gwtBAiGnlEcdq3
GyFAZ0szb5P4BaNnX9qc
=C5t5
-----END PGP SIGNATURE-----
Merge tag 'gpio-v4.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio
Pull GPIO updates from Linus Walleij:
"This is the bulk of GPIO changes for the v4.17 kernel cycle:
New drivers:
- Nintendo Wii GameCube GPIO, known as "Hollywood"
- Raspberry Pi mailbox service GPIO expander
- Spreadtrum main SC9860 SoC and IEC GPIO controllers.
Improvements:
- Implemented .get_multiple() callback for most of the
high-performance industrial GPIO cards for the ISA bus.
- ISA GPIO drivers now select the ISA_BUS_API instead of depending on
it. This is merged with the same pattern for all the ISA drivers
and some other Kconfig cleanups related to this.
Cleanup:
- Delete the TZ1090 GPIO drivers following the deletion of this SoC
from the ARM tree.
- Move the documentation over to driver-api to conform with the rest
of the kernel documentation build.
- Continue to make the GPIO drivers include only
<linux/gpio/driver.h> and not the too broad <linux/gpio.h> that we
want to get rid of.
- Managed to remove VLA allocation from two drivers pending more
fixes in this area for the next merge window.
- Misc janitorial fixes"
* tag 'gpio-v4.17-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-gpio: (77 commits)
gpio: Add Spreadtrum PMIC EIC driver support
gpio: Add Spreadtrum EIC driver support
dt-bindings: gpio: Add Spreadtrum EIC controller documentation
gpio: ath79: Fix potential NULL dereference in ath79_gpio_probe()
pinctrl: qcom: Don't allow protected pins to be requested
gpiolib: Support 'gpio-reserved-ranges' property
gpiolib: Change bitmap allocation to kmalloc_array
gpiolib: Extract mask allocation into subroutine
dt-bindings: gpio: Add a gpio-reserved-ranges property
gpio: mockup: fix a potential crash when creating debugfs entries
gpio: pca953x: add compatibility for pcal6524 and pcal9555a
gpio: dwapb: Add support for a bus clock
gpio: Remove VLA from xra1403 driver
gpio: Remove VLA from MAX3191X driver
gpio: ws16c48: Implement get_multiple callback
gpio: gpio-mm: Implement get_multiple callback
gpio: 104-idi-48: Implement get_multiple callback
gpio: 104-dio-48e: Implement get_multiple callback
gpio: pcie-idio-24: Implement get_multiple/set_multiple callbacks
gpio: pci-idio-16: Implement get_multiple callback
...
This commit is contained in:
commit
1b2951dd99
77 changed files with 2771 additions and 1274 deletions
|
|
@ -122,6 +122,17 @@ config GPIO_ATH79
|
|||
Select this option to enable GPIO driver for
|
||||
Atheros AR71XX/AR724X/AR913X SoC devices.
|
||||
|
||||
config GPIO_RASPBERRYPI_EXP
|
||||
tristate "Raspberry Pi 3 GPIO Expander"
|
||||
default RASPBERRYPI_FIRMWARE
|
||||
depends on OF_GPIO
|
||||
# Make sure not 'y' when RASPBERRYPI_FIRMWARE is 'm'. This can only
|
||||
# happen when COMPILE_TEST=y, hence the added !RASPBERRYPI_FIRMWARE.
|
||||
depends on (ARCH_BCM2835 && RASPBERRYPI_FIRMWARE) || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
|
||||
help
|
||||
Turn on GPIO support for the expander on Raspberry Pi 3 boards, using
|
||||
the firmware mailbox to communicate with VideoCore on BCM283x chips.
|
||||
|
||||
config GPIO_BCM_KONA
|
||||
bool "Broadcom Kona GPIO"
|
||||
depends on OF_GPIO && (ARCH_BCM_MOBILE || COMPILE_TEST)
|
||||
|
|
@ -159,6 +170,14 @@ config GPIO_DWAPB
|
|||
Say Y or M here to build support for the Synopsys DesignWare APB
|
||||
GPIO block.
|
||||
|
||||
config GPIO_EIC_SPRD
|
||||
tristate "Spreadtrum EIC support"
|
||||
depends on ARCH_SPRD || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say yes here to support Spreadtrum EIC device.
|
||||
|
||||
config GPIO_EM
|
||||
tristate "Emma Mobile GPIO"
|
||||
depends on (ARCH_EMEV2 || COMPILE_TEST) && OF_GPIO
|
||||
|
|
@ -214,6 +233,15 @@ config GPIO_GRGPIO
|
|||
Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB
|
||||
VHDL IP core library.
|
||||
|
||||
config GPIO_HLWD
|
||||
tristate "Nintendo Wii (Hollywood) GPIO"
|
||||
depends on OF_GPIO
|
||||
select GPIO_GENERIC
|
||||
help
|
||||
Select this to support the GPIO controller of the Nintendo Wii.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config GPIO_ICH
|
||||
tristate "Intel ICH GPIO"
|
||||
depends on PCI && X86
|
||||
|
|
@ -363,6 +391,14 @@ config GPIO_PL061
|
|||
help
|
||||
Say yes here to support the PrimeCell PL061 GPIO device
|
||||
|
||||
config GPIO_PMIC_EIC_SPRD
|
||||
tristate "Spreadtrum PMIC EIC support"
|
||||
depends on MFD_SC27XX_PMIC || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say yes here to support Spreadtrum PMIC EIC device.
|
||||
|
||||
config GPIO_PXA
|
||||
bool "PXA GPIO support"
|
||||
depends on ARCH_PXA || ARCH_MMP
|
||||
|
|
@ -389,6 +425,14 @@ config GPIO_SPEAR_SPICS
|
|||
help
|
||||
Say yes here to support ST SPEAr SPI Chip Select as GPIO device
|
||||
|
||||
config GPIO_SPRD
|
||||
tristate "Spreadtrum GPIO support"
|
||||
depends on ARCH_SPRD || COMPILE_TEST
|
||||
depends on OF_GPIO
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Say yes here to support Spreadtrum GPIO device.
|
||||
|
||||
config GPIO_STA2X11
|
||||
bool "STA2x11/ConneXt GPIO support"
|
||||
depends on MFD_STA2X11
|
||||
|
|
@ -453,21 +497,6 @@ config GPIO_THUNDERX
|
|||
Say yes here to support the on-chip GPIO lines on the ThunderX
|
||||
and OCTEON-TX families of SoCs.
|
||||
|
||||
config GPIO_TZ1090
|
||||
bool "Toumaz Xenif TZ1090 GPIO support"
|
||||
depends on SOC_TZ1090
|
||||
select GENERIC_IRQ_CHIP
|
||||
default y
|
||||
help
|
||||
Say yes here to support Toumaz Xenif TZ1090 GPIOs.
|
||||
|
||||
config GPIO_TZ1090_PDC
|
||||
bool "Toumaz Xenif TZ1090 PDC GPIO support"
|
||||
depends on SOC_TZ1090
|
||||
default y
|
||||
help
|
||||
Say yes here to support Toumaz Xenif TZ1090 PDC GPIOs.
|
||||
|
||||
config GPIO_UNIPHIER
|
||||
tristate "UniPhier GPIO support"
|
||||
depends on ARCH_UNIPHIER || COMPILE_TEST
|
||||
|
|
@ -581,7 +610,8 @@ menu "Port-mapped I/O GPIO drivers"
|
|||
|
||||
config GPIO_104_DIO_48E
|
||||
tristate "ACCES 104-DIO-48E GPIO support"
|
||||
depends on PC104 && ISA_BUS_API
|
||||
depends on PC104
|
||||
select ISA_BUS_API
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Enables GPIO support for the ACCES 104-DIO-48E series (104-DIO-48E,
|
||||
|
|
@ -591,7 +621,8 @@ config GPIO_104_DIO_48E
|
|||
|
||||
config GPIO_104_IDIO_16
|
||||
tristate "ACCES 104-IDIO-16 GPIO support"
|
||||
depends on PC104 && ISA_BUS_API
|
||||
depends on PC104
|
||||
select ISA_BUS_API
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Enables GPIO support for the ACCES 104-IDIO-16 family (104-IDIO-16,
|
||||
|
|
@ -602,7 +633,8 @@ config GPIO_104_IDIO_16
|
|||
|
||||
config GPIO_104_IDI_48
|
||||
tristate "ACCES 104-IDI-48 GPIO support"
|
||||
depends on PC104 && ISA_BUS_API
|
||||
depends on PC104
|
||||
select ISA_BUS_API
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Enables GPIO support for the ACCES 104-IDI-48 family (104-IDI-48A,
|
||||
|
|
@ -622,7 +654,8 @@ config GPIO_F7188X
|
|||
|
||||
config GPIO_GPIO_MM
|
||||
tristate "Diamond Systems GPIO-MM GPIO support"
|
||||
depends on PC104 && ISA_BUS_API
|
||||
depends on PC104
|
||||
select ISA_BUS_API
|
||||
help
|
||||
Enables GPIO support for the Diamond Systems GPIO-MM and GPIO-MM-12.
|
||||
|
||||
|
|
@ -691,7 +724,7 @@ config GPIO_TS5500
|
|||
|
||||
config GPIO_WINBOND
|
||||
tristate "Winbond Super I/O GPIO support"
|
||||
depends on ISA_BUS_API
|
||||
select ISA_BUS_API
|
||||
help
|
||||
This option enables support for GPIOs found on Winbond Super I/O
|
||||
chips.
|
||||
|
|
@ -707,7 +740,7 @@ config GPIO_WINBOND
|
|||
|
||||
config GPIO_WS16C48
|
||||
tristate "WinSystems WS16C48 GPIO support"
|
||||
depends on ISA_BUS_API
|
||||
select ISA_BUS_API
|
||||
select GPIOLIB_IRQCHIP
|
||||
help
|
||||
Enables GPIO support for the WinSystems WS16C48. The base port
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o
|
|||
obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
|
||||
obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o
|
||||
obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o
|
||||
obj-$(CONFIG_GPIO_RASPBERRYPI_EXP) += gpio-raspberrypi-exp.o
|
||||
obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
|
||||
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
|
||||
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
|
||||
|
|
@ -44,6 +45,7 @@ obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o
|
|||
obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o
|
||||
obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o
|
||||
obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o
|
||||
obj-$(CONFIG_GPIO_EIC_SPRD) += gpio-eic-sprd.o
|
||||
obj-$(CONFIG_GPIO_EM) += gpio-em.o
|
||||
obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o
|
||||
obj-$(CONFIG_GPIO_EXAR) += gpio-exar.o
|
||||
|
|
@ -52,6 +54,7 @@ obj-$(CONFIG_GPIO_FTGPIO010) += gpio-ftgpio010.o
|
|||
obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o
|
||||
obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o
|
||||
obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o
|
||||
obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o
|
||||
obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o
|
||||
obj-$(CONFIG_GPIO_ICH) += gpio-ich.o
|
||||
obj-$(CONFIG_GPIO_INGENIC) += gpio-ingenic.o
|
||||
|
|
@ -97,6 +100,7 @@ obj-$(CONFIG_GPIO_PCI_IDIO_16) += gpio-pci-idio-16.o
|
|||
obj-$(CONFIG_GPIO_PCIE_IDIO_24) += gpio-pcie-idio-24.o
|
||||
obj-$(CONFIG_GPIO_PISOSR) += gpio-pisosr.o
|
||||
obj-$(CONFIG_GPIO_PL061) += gpio-pl061.o
|
||||
obj-$(CONFIG_GPIO_PMIC_EIC_SPRD) += gpio-pmic-eic-sprd.o
|
||||
obj-$(CONFIG_GPIO_PXA) += gpio-pxa.o
|
||||
obj-$(CONFIG_GPIO_RC5T583) += gpio-rc5t583.o
|
||||
obj-$(CONFIG_GPIO_RDC321X) += gpio-rdc321x.o
|
||||
|
|
@ -107,6 +111,7 @@ obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
|
|||
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
|
||||
obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o
|
||||
obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o
|
||||
obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o
|
||||
obj-$(CONFIG_GPIO_STA2X11) += gpio-sta2x11.o
|
||||
obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o
|
||||
obj-$(CONFIG_GPIO_STP_XWAY) += gpio-stp-xway.o
|
||||
|
|
@ -130,8 +135,6 @@ obj-$(CONFIG_GPIO_TS4900) += gpio-ts4900.o
|
|||
obj-$(CONFIG_GPIO_TS5500) += gpio-ts5500.o
|
||||
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
|
||||
obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
|
||||
obj-$(CONFIG_GPIO_TZ1090) += gpio-tz1090.o
|
||||
obj-$(CONFIG_GPIO_TZ1090_PDC) += gpio-tz1090-pdc.o
|
||||
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
|
||||
obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.o
|
||||
obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
* This driver supports the following ACCES devices: 104-DIO-48E and
|
||||
* 104-DIO-24E.
|
||||
*/
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
|
|
@ -182,6 +183,51 @@ static int dio48e_gpio_get(struct gpio_chip *chip, unsigned offset)
|
|||
return !!(port_state & mask);
|
||||
}
|
||||
|
||||
static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
|
||||
unsigned long *bits)
|
||||
{
|
||||
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
|
||||
size_t i;
|
||||
const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
|
||||
const unsigned int gpio_reg_size = 8;
|
||||
unsigned int bits_offset;
|
||||
size_t word_index;
|
||||
unsigned int word_offset;
|
||||
unsigned long word_mask;
|
||||
const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
|
||||
unsigned long port_state;
|
||||
|
||||
/* 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_state = inb(dio48egpio->base + ports[i]);
|
||||
|
||||
/* store acquired bits at respective bits array offset */
|
||||
bits[word_index] |= port_state << word_offset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dio48e_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip);
|
||||
|
|
@ -384,6 +430,7 @@ static int dio48e_probe(struct device *dev, unsigned int id)
|
|||
dio48egpio->chip.direction_input = dio48e_gpio_direction_input;
|
||||
dio48egpio->chip.direction_output = dio48e_gpio_direction_output;
|
||||
dio48egpio->chip.get = dio48e_gpio_get;
|
||||
dio48egpio->chip.get_multiple = dio48e_gpio_get_multiple;
|
||||
dio48egpio->chip.set = dio48e_gpio_set;
|
||||
dio48egpio->chip.set_multiple = dio48e_gpio_set_multiple;
|
||||
dio48egpio->base = base[id];
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
* This driver supports the following ACCES devices: 104-IDI-48A,
|
||||
* 104-IDI-48AC, 104-IDI-48B, and 104-IDI-48BC.
|
||||
*/
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
|
|
@ -88,6 +89,51 @@ static int idi_48_gpio_get(struct gpio_chip *chip, unsigned offset)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
|
||||
unsigned long *bits)
|
||||
{
|
||||
struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip);
|
||||
size_t i;
|
||||
const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
|
||||
const unsigned int gpio_reg_size = 8;
|
||||
unsigned int bits_offset;
|
||||
size_t word_index;
|
||||
unsigned int word_offset;
|
||||
unsigned long word_mask;
|
||||
const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
|
||||
unsigned long port_state;
|
||||
|
||||
/* 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_state = inb(idi48gpio->base + ports[i]);
|
||||
|
||||
/* store acquired bits at respective bits array offset */
|
||||
bits[word_index] |= port_state << word_offset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void idi_48_irq_ack(struct irq_data *data)
|
||||
{
|
||||
}
|
||||
|
|
@ -256,6 +302,7 @@ static int idi_48_probe(struct device *dev, unsigned int id)
|
|||
idi48gpio->chip.get_direction = idi_48_gpio_get_direction;
|
||||
idi48gpio->chip.direction_input = idi_48_gpio_direction_input;
|
||||
idi48gpio->chip.get = idi_48_gpio_get;
|
||||
idi48gpio->chip.get_multiple = idi_48_gpio_get_multiple;
|
||||
idi48gpio->base = base[id];
|
||||
|
||||
raw_spin_lock_init(&idi48gpio->lock);
|
||||
|
|
|
|||
|
|
@ -90,6 +90,20 @@ static int idio_16_gpio_get(struct gpio_chip *chip, unsigned offset)
|
|||
return !!(inb(idio16gpio->base + 5) & (mask>>8));
|
||||
}
|
||||
|
||||
static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
|
||||
unsigned long *mask, unsigned long *bits)
|
||||
{
|
||||
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
|
||||
|
||||
*bits = 0;
|
||||
if (*mask & GENMASK(23, 16))
|
||||
*bits |= (unsigned long)inb(idio16gpio->base + 1) << 16;
|
||||
if (*mask & GENMASK(31, 24))
|
||||
*bits |= (unsigned long)inb(idio16gpio->base + 5) << 24;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void idio_16_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
struct idio_16_gpio *const idio16gpio = gpiochip_get_data(chip);
|
||||
|
|
@ -244,6 +258,7 @@ static int idio_16_probe(struct device *dev, unsigned int id)
|
|||
idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
|
||||
idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
|
||||
idio16gpio->chip.get = idio_16_gpio_get;
|
||||
idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
|
||||
idio16gpio->chip.set = idio_16_gpio_set;
|
||||
idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
|
||||
idio16gpio->base = base[id];
|
||||
|
|
|
|||
|
|
@ -258,6 +258,8 @@ static int ath79_gpio_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -EINVAL;
|
||||
ctrl->base = devm_ioremap_nocache(
|
||||
&pdev->dev, res->start, resource_size(res));
|
||||
if (!ctrl->base)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/gpio-davinci.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
|
|
@ -225,6 +226,11 @@ static int davinci_gpio_probe(struct platform_device *pdev)
|
|||
chips->chip.of_gpio_n_cells = 2;
|
||||
chips->chip.parent = dev;
|
||||
chips->chip.of_node = dev->of_node;
|
||||
|
||||
if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
|
||||
chips->chip.request = gpiochip_generic_request;
|
||||
chips->chip.free = gpiochip_generic_free;
|
||||
}
|
||||
#endif
|
||||
spin_lock_init(&chips->lock);
|
||||
bank_base += ngpio;
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
#include <linux/irqdomain.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqchip/chained_irq.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/dln2.h>
|
||||
|
|
@ -204,9 +203,9 @@ static int dln2_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
|
|||
struct dln2_gpio *dln2 = gpiochip_get_data(chip);
|
||||
|
||||
if (test_bit(offset, dln2->output_enabled))
|
||||
return GPIOF_DIR_OUT;
|
||||
return 0;
|
||||
|
||||
return GPIOF_DIR_IN;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
|
|
@ -218,7 +217,7 @@ static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
|||
if (dir < 0)
|
||||
return dir;
|
||||
|
||||
if (dir == GPIOF_DIR_IN)
|
||||
if (dir == 1)
|
||||
return dln2_gpio_pin_get_in_val(dln2, offset);
|
||||
|
||||
return dln2_gpio_pin_get_out_val(dln2, offset);
|
||||
|
|
|
|||
|
|
@ -8,10 +8,9 @@
|
|||
* All enquiries to support@picochip.com
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
/* FIXME: for gpio_get_value(), replace this with direct register read */
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
|
|
@ -53,9 +52,9 @@
|
|||
#define GPIO_EXT_PORTD 0x5c
|
||||
|
||||
#define DWAPB_MAX_PORTS 4
|
||||
#define GPIO_EXT_PORT_SIZE (GPIO_EXT_PORTB - GPIO_EXT_PORTA)
|
||||
#define GPIO_SWPORT_DR_SIZE (GPIO_SWPORTB_DR - GPIO_SWPORTA_DR)
|
||||
#define GPIO_SWPORT_DDR_SIZE (GPIO_SWPORTB_DDR - GPIO_SWPORTA_DDR)
|
||||
#define GPIO_EXT_PORT_STRIDE 0x04 /* register stride 32 bits */
|
||||
#define GPIO_SWPORT_DR_STRIDE 0x0c /* register stride 3*32 bits */
|
||||
#define GPIO_SWPORT_DDR_STRIDE 0x0c /* register stride 3*32 bits */
|
||||
|
||||
#define GPIO_REG_OFFSET_V2 1
|
||||
|
||||
|
|
@ -100,6 +99,7 @@ struct dwapb_gpio {
|
|||
struct irq_domain *domain;
|
||||
unsigned int flags;
|
||||
struct reset_control *rst;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static inline u32 gpio_reg_v2_convert(unsigned int offset)
|
||||
|
|
@ -153,16 +153,40 @@ static int dwapb_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
|
|||
return irq_find_mapping(gpio->domain, offset);
|
||||
}
|
||||
|
||||
static struct dwapb_gpio_port *dwapb_offs_to_port(struct dwapb_gpio *gpio, unsigned int offs)
|
||||
{
|
||||
struct dwapb_gpio_port *port;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < gpio->nr_ports; i++) {
|
||||
port = &gpio->ports[i];
|
||||
if (port->idx == offs / 32)
|
||||
return port;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void dwapb_toggle_trigger(struct dwapb_gpio *gpio, unsigned int offs)
|
||||
{
|
||||
u32 v = dwapb_read(gpio, GPIO_INT_POLARITY);
|
||||
struct dwapb_gpio_port *port = dwapb_offs_to_port(gpio, offs);
|
||||
struct gpio_chip *gc;
|
||||
u32 pol;
|
||||
int val;
|
||||
|
||||
if (gpio_get_value(gpio->ports[0].gc.base + offs))
|
||||
v &= ~BIT(offs);
|
||||
if (!port)
|
||||
return;
|
||||
gc = &port->gc;
|
||||
|
||||
pol = dwapb_read(gpio, GPIO_INT_POLARITY);
|
||||
/* Just read the current value right out of the data register */
|
||||
val = gc->get(gc, offs % 32);
|
||||
if (val)
|
||||
pol &= ~BIT(offs);
|
||||
else
|
||||
v |= BIT(offs);
|
||||
pol |= BIT(offs);
|
||||
|
||||
dwapb_write(gpio, GPIO_INT_POLARITY, v);
|
||||
dwapb_write(gpio, GPIO_INT_POLARITY, pol);
|
||||
}
|
||||
|
||||
static u32 dwapb_do_irq(struct dwapb_gpio *gpio)
|
||||
|
|
@ -476,11 +500,12 @@ static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
|
|||
return -ENOMEM;
|
||||
#endif
|
||||
|
||||
dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_SIZE);
|
||||
set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_SIZE);
|
||||
dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_STRIDE);
|
||||
set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_STRIDE);
|
||||
dirout = gpio->regs + GPIO_SWPORTA_DDR +
|
||||
(pp->idx * GPIO_SWPORT_DDR_SIZE);
|
||||
(pp->idx * GPIO_SWPORT_DDR_STRIDE);
|
||||
|
||||
/* This registers 32 GPIO lines per port */
|
||||
err = bgpio_init(&port->gc, gpio->dev, 4, dat, set, NULL, dirout,
|
||||
NULL, 0);
|
||||
if (err) {
|
||||
|
|
@ -647,6 +672,16 @@ static int dwapb_gpio_probe(struct platform_device *pdev)
|
|||
if (IS_ERR(gpio->regs))
|
||||
return PTR_ERR(gpio->regs);
|
||||
|
||||
/* Optional bus clock */
|
||||
gpio->clk = devm_clk_get(&pdev->dev, "bus");
|
||||
if (!IS_ERR(gpio->clk)) {
|
||||
err = clk_prepare_enable(gpio->clk);
|
||||
if (err) {
|
||||
dev_info(&pdev->dev, "Cannot enable clock\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
gpio->flags = 0;
|
||||
if (dev->of_node) {
|
||||
const struct of_device_id *of_devid;
|
||||
|
|
@ -689,6 +724,7 @@ static int dwapb_gpio_remove(struct platform_device *pdev)
|
|||
dwapb_gpio_unregister(gpio);
|
||||
dwapb_irq_teardown(gpio);
|
||||
reset_control_assert(gpio->rst);
|
||||
clk_disable_unprepare(gpio->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -710,13 +746,13 @@ static int dwapb_gpio_suspend(struct device *dev)
|
|||
|
||||
BUG_ON(!ctx);
|
||||
|
||||
offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE;
|
||||
offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_STRIDE;
|
||||
ctx->dir = dwapb_read(gpio, offset);
|
||||
|
||||
offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE;
|
||||
offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_STRIDE;
|
||||
ctx->data = dwapb_read(gpio, offset);
|
||||
|
||||
offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE;
|
||||
offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_STRIDE;
|
||||
ctx->ext = dwapb_read(gpio, offset);
|
||||
|
||||
/* Only port A can provide interrupts */
|
||||
|
|
@ -734,6 +770,8 @@ static int dwapb_gpio_suspend(struct device *dev)
|
|||
}
|
||||
spin_unlock_irqrestore(&gc->bgpio_lock, flags);
|
||||
|
||||
clk_disable_unprepare(gpio->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -745,6 +783,9 @@ static int dwapb_gpio_resume(struct device *dev)
|
|||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
if (!IS_ERR(gpio->clk))
|
||||
clk_prepare_enable(gpio->clk);
|
||||
|
||||
spin_lock_irqsave(&gc->bgpio_lock, flags);
|
||||
for (i = 0; i < gpio->nr_ports; i++) {
|
||||
unsigned int offset;
|
||||
|
|
@ -753,13 +794,13 @@ static int dwapb_gpio_resume(struct device *dev)
|
|||
|
||||
BUG_ON(!ctx);
|
||||
|
||||
offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_SIZE;
|
||||
offset = GPIO_SWPORTA_DR + idx * GPIO_SWPORT_DR_STRIDE;
|
||||
dwapb_write(gpio, offset, ctx->data);
|
||||
|
||||
offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_SIZE;
|
||||
offset = GPIO_SWPORTA_DDR + idx * GPIO_SWPORT_DDR_STRIDE;
|
||||
dwapb_write(gpio, offset, ctx->dir);
|
||||
|
||||
offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_SIZE;
|
||||
offset = GPIO_EXT_PORTA + idx * GPIO_EXT_PORT_STRIDE;
|
||||
dwapb_write(gpio, offset, ctx->ext);
|
||||
|
||||
/* Only port A can provide interrupts */
|
||||
|
|
|
|||
606
drivers/gpio/gpio-eic-sprd.c
Normal file
606
drivers/gpio/gpio-eic-sprd.c
Normal file
|
|
@ -0,0 +1,606 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018 Spreadtrum Communications Inc.
|
||||
* Copyright (C) 2018 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/* EIC registers definition */
|
||||
#define SPRD_EIC_DBNC_DATA 0x0
|
||||
#define SPRD_EIC_DBNC_DMSK 0x4
|
||||
#define SPRD_EIC_DBNC_IEV 0x14
|
||||
#define SPRD_EIC_DBNC_IE 0x18
|
||||
#define SPRD_EIC_DBNC_RIS 0x1c
|
||||
#define SPRD_EIC_DBNC_MIS 0x20
|
||||
#define SPRD_EIC_DBNC_IC 0x24
|
||||
#define SPRD_EIC_DBNC_TRIG 0x28
|
||||
#define SPRD_EIC_DBNC_CTRL0 0x40
|
||||
|
||||
#define SPRD_EIC_LATCH_INTEN 0x0
|
||||
#define SPRD_EIC_LATCH_INTRAW 0x4
|
||||
#define SPRD_EIC_LATCH_INTMSK 0x8
|
||||
#define SPRD_EIC_LATCH_INTCLR 0xc
|
||||
#define SPRD_EIC_LATCH_INTPOL 0x10
|
||||
#define SPRD_EIC_LATCH_INTMODE 0x14
|
||||
|
||||
#define SPRD_EIC_ASYNC_INTIE 0x0
|
||||
#define SPRD_EIC_ASYNC_INTRAW 0x4
|
||||
#define SPRD_EIC_ASYNC_INTMSK 0x8
|
||||
#define SPRD_EIC_ASYNC_INTCLR 0xc
|
||||
#define SPRD_EIC_ASYNC_INTMODE 0x10
|
||||
#define SPRD_EIC_ASYNC_INTBOTH 0x14
|
||||
#define SPRD_EIC_ASYNC_INTPOL 0x18
|
||||
#define SPRD_EIC_ASYNC_DATA 0x1c
|
||||
|
||||
#define SPRD_EIC_SYNC_INTIE 0x0
|
||||
#define SPRD_EIC_SYNC_INTRAW 0x4
|
||||
#define SPRD_EIC_SYNC_INTMSK 0x8
|
||||
#define SPRD_EIC_SYNC_INTCLR 0xc
|
||||
#define SPRD_EIC_SYNC_INTMODE 0x10
|
||||
#define SPRD_EIC_SYNC_INTBOTH 0x14
|
||||
#define SPRD_EIC_SYNC_INTPOL 0x18
|
||||
#define SPRD_EIC_SYNC_DATA 0x1c
|
||||
|
||||
/*
|
||||
* The digital-chip EIC controller can support maximum 3 banks, and each bank
|
||||
* contains 8 EICs.
|
||||
*/
|
||||
#define SPRD_EIC_MAX_BANK 3
|
||||
#define SPRD_EIC_PER_BANK_NR 8
|
||||
#define SPRD_EIC_DATA_MASK GENMASK(7, 0)
|
||||
#define SPRD_EIC_BIT(x) ((x) & (SPRD_EIC_PER_BANK_NR - 1))
|
||||
#define SPRD_EIC_DBNC_MASK GENMASK(11, 0)
|
||||
|
||||
/*
|
||||
* The Spreadtrum EIC (external interrupt controller) can be used only in
|
||||
* input mode to generate interrupts if detecting input signals.
|
||||
*
|
||||
* The Spreadtrum digital-chip EIC controller contains 4 sub-modules:
|
||||
* debounce EIC, latch EIC, async EIC and sync EIC,
|
||||
*
|
||||
* The debounce EIC is used to capture the input signals' stable status
|
||||
* (millisecond resolution) and a single-trigger mechanism is introduced
|
||||
* into this sub-module to enhance the input event detection reliability.
|
||||
* The debounce range is from 1ms to 4s with a step size of 1ms.
|
||||
*
|
||||
* The latch EIC is used to latch some special power down signals and
|
||||
* generate interrupts, since the latch EIC does not depend on the APB clock
|
||||
* to capture signals.
|
||||
*
|
||||
* The async EIC uses a 32k clock to capture the short signals (microsecond
|
||||
* resolution) to generate interrupts by level or edge trigger.
|
||||
*
|
||||
* The EIC-sync is similar with GPIO's input function, which is a synchronized
|
||||
* signal input register.
|
||||
*/
|
||||
enum sprd_eic_type {
|
||||
SPRD_EIC_DEBOUNCE,
|
||||
SPRD_EIC_LATCH,
|
||||
SPRD_EIC_ASYNC,
|
||||
SPRD_EIC_SYNC,
|
||||
SPRD_EIC_MAX,
|
||||
};
|
||||
|
||||
struct sprd_eic {
|
||||
struct gpio_chip chip;
|
||||
struct irq_chip intc;
|
||||
void __iomem *base[SPRD_EIC_MAX_BANK];
|
||||
enum sprd_eic_type type;
|
||||
spinlock_t lock;
|
||||
int irq;
|
||||
};
|
||||
|
||||
struct sprd_eic_variant_data {
|
||||
enum sprd_eic_type type;
|
||||
u32 num_eics;
|
||||
};
|
||||
|
||||
static const char *sprd_eic_label_name[SPRD_EIC_MAX] = {
|
||||
"eic-debounce", "eic-latch", "eic-async",
|
||||
"eic-sync",
|
||||
};
|
||||
|
||||
static const struct sprd_eic_variant_data sc9860_eic_dbnc_data = {
|
||||
.type = SPRD_EIC_DEBOUNCE,
|
||||
.num_eics = 8,
|
||||
};
|
||||
|
||||
static const struct sprd_eic_variant_data sc9860_eic_latch_data = {
|
||||
.type = SPRD_EIC_LATCH,
|
||||
.num_eics = 8,
|
||||
};
|
||||
|
||||
static const struct sprd_eic_variant_data sc9860_eic_async_data = {
|
||||
.type = SPRD_EIC_ASYNC,
|
||||
.num_eics = 8,
|
||||
};
|
||||
|
||||
static const struct sprd_eic_variant_data sc9860_eic_sync_data = {
|
||||
.type = SPRD_EIC_SYNC,
|
||||
.num_eics = 8,
|
||||
};
|
||||
|
||||
static inline void __iomem *sprd_eic_offset_base(struct sprd_eic *sprd_eic,
|
||||
unsigned int bank)
|
||||
{
|
||||
if (bank >= SPRD_EIC_MAX_BANK)
|
||||
return NULL;
|
||||
|
||||
return sprd_eic->base[bank];
|
||||
}
|
||||
|
||||
static void sprd_eic_update(struct gpio_chip *chip, unsigned int offset,
|
||||
u16 reg, unsigned int val)
|
||||
{
|
||||
struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
|
||||
void __iomem *base =
|
||||
sprd_eic_offset_base(sprd_eic, offset / SPRD_EIC_PER_BANK_NR);
|
||||
unsigned long flags;
|
||||
u32 tmp;
|
||||
|
||||
spin_lock_irqsave(&sprd_eic->lock, flags);
|
||||
tmp = readl_relaxed(base + reg);
|
||||
|
||||
if (val)
|
||||
tmp |= BIT(SPRD_EIC_BIT(offset));
|
||||
else
|
||||
tmp &= ~BIT(SPRD_EIC_BIT(offset));
|
||||
|
||||
writel_relaxed(tmp, base + reg);
|
||||
spin_unlock_irqrestore(&sprd_eic->lock, flags);
|
||||
}
|
||||
|
||||
static int sprd_eic_read(struct gpio_chip *chip, unsigned int offset, u16 reg)
|
||||
{
|
||||
struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
|
||||
void __iomem *base =
|
||||
sprd_eic_offset_base(sprd_eic, offset / SPRD_EIC_PER_BANK_NR);
|
||||
|
||||
return !!(readl_relaxed(base + reg) & BIT(SPRD_EIC_BIT(offset)));
|
||||
}
|
||||
|
||||
static int sprd_eic_request(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_DBNC_DMSK, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_eic_free(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_DBNC_DMSK, 0);
|
||||
}
|
||||
|
||||
static int sprd_eic_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
return sprd_eic_read(chip, offset, SPRD_EIC_DBNC_DATA);
|
||||
}
|
||||
|
||||
static int sprd_eic_direction_input(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
/* EICs are always input, nothing need to do here. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_eic_set(struct gpio_chip *chip, unsigned int offset, int value)
|
||||
{
|
||||
/* EICs are always input, nothing need to do here. */
|
||||
}
|
||||
|
||||
static int sprd_eic_set_debounce(struct gpio_chip *chip, unsigned int offset,
|
||||
unsigned int debounce)
|
||||
{
|
||||
struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
|
||||
void __iomem *base =
|
||||
sprd_eic_offset_base(sprd_eic, offset / SPRD_EIC_PER_BANK_NR);
|
||||
u32 reg = SPRD_EIC_DBNC_CTRL0 + SPRD_EIC_BIT(offset) * 0x4;
|
||||
u32 value = readl_relaxed(base + reg) & ~SPRD_EIC_DBNC_MASK;
|
||||
|
||||
value |= (debounce / 1000) & SPRD_EIC_DBNC_MASK;
|
||||
writel_relaxed(value, base + reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_eic_set_config(struct gpio_chip *chip, unsigned int offset,
|
||||
unsigned long config)
|
||||
{
|
||||
unsigned long param = pinconf_to_config_param(config);
|
||||
u32 arg = pinconf_to_config_argument(config);
|
||||
|
||||
if (param == PIN_CONFIG_INPUT_DEBOUNCE)
|
||||
return sprd_eic_set_debounce(chip, offset, arg);
|
||||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static void sprd_eic_irq_mask(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
|
||||
u32 offset = irqd_to_hwirq(data);
|
||||
|
||||
switch (sprd_eic->type) {
|
||||
case SPRD_EIC_DEBOUNCE:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IE, 0);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_DBNC_TRIG, 0);
|
||||
break;
|
||||
case SPRD_EIC_LATCH:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTEN, 0);
|
||||
break;
|
||||
case SPRD_EIC_ASYNC:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTIE, 0);
|
||||
break;
|
||||
case SPRD_EIC_SYNC:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTIE, 0);
|
||||
break;
|
||||
default:
|
||||
dev_err(chip->parent, "Unsupported EIC type.\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void sprd_eic_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
|
||||
u32 offset = irqd_to_hwirq(data);
|
||||
|
||||
switch (sprd_eic->type) {
|
||||
case SPRD_EIC_DEBOUNCE:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IE, 1);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_DBNC_TRIG, 1);
|
||||
break;
|
||||
case SPRD_EIC_LATCH:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTEN, 1);
|
||||
break;
|
||||
case SPRD_EIC_ASYNC:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTIE, 1);
|
||||
break;
|
||||
case SPRD_EIC_SYNC:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTIE, 1);
|
||||
break;
|
||||
default:
|
||||
dev_err(chip->parent, "Unsupported EIC type.\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void sprd_eic_irq_ack(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
|
||||
u32 offset = irqd_to_hwirq(data);
|
||||
|
||||
switch (sprd_eic->type) {
|
||||
case SPRD_EIC_DEBOUNCE:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IC, 1);
|
||||
break;
|
||||
case SPRD_EIC_LATCH:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTCLR, 1);
|
||||
break;
|
||||
case SPRD_EIC_ASYNC:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTCLR, 1);
|
||||
break;
|
||||
case SPRD_EIC_SYNC:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTCLR, 1);
|
||||
break;
|
||||
default:
|
||||
dev_err(chip->parent, "Unsupported EIC type.\n");
|
||||
}
|
||||
}
|
||||
|
||||
static int sprd_eic_irq_set_type(struct irq_data *data, unsigned int flow_type)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
|
||||
u32 offset = irqd_to_hwirq(data);
|
||||
|
||||
switch (sprd_eic->type) {
|
||||
case SPRD_EIC_DEBOUNCE:
|
||||
switch (flow_type) {
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IEV, 1);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_DBNC_IEV, 0);
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
irq_set_handler_locked(data, handle_level_irq);
|
||||
break;
|
||||
case SPRD_EIC_LATCH:
|
||||
switch (flow_type) {
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTPOL, 0);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_LATCH_INTPOL, 1);
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
irq_set_handler_locked(data, handle_level_irq);
|
||||
break;
|
||||
case SPRD_EIC_ASYNC:
|
||||
switch (flow_type) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 0);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 1);
|
||||
irq_set_handler_locked(data, handle_edge_irq);
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 0);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 0);
|
||||
irq_set_handler_locked(data, handle_edge_irq);
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 1);
|
||||
irq_set_handler_locked(data, handle_edge_irq);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 1);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 1);
|
||||
irq_set_handler_locked(data, handle_level_irq);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTBOTH, 0);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTMODE, 1);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_ASYNC_INTPOL, 0);
|
||||
irq_set_handler_locked(data, handle_level_irq);
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
break;
|
||||
case SPRD_EIC_SYNC:
|
||||
switch (flow_type) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 0);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 1);
|
||||
irq_set_handler_locked(data, handle_edge_irq);
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 0);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 0);
|
||||
irq_set_handler_locked(data, handle_edge_irq);
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 1);
|
||||
irq_set_handler_locked(data, handle_edge_irq);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 1);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 1);
|
||||
irq_set_handler_locked(data, handle_level_irq);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTBOTH, 0);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTMODE, 1);
|
||||
sprd_eic_update(chip, offset, SPRD_EIC_SYNC_INTPOL, 0);
|
||||
irq_set_handler_locked(data, handle_level_irq);
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
default:
|
||||
dev_err(chip->parent, "Unsupported EIC type.\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_eic_match_chip_by_type(struct gpio_chip *chip, void *data)
|
||||
{
|
||||
enum sprd_eic_type type = *(enum sprd_eic_type *)data;
|
||||
|
||||
return !strcmp(chip->label, sprd_eic_label_name[type]);
|
||||
}
|
||||
|
||||
static void sprd_eic_handle_one_type(struct gpio_chip *chip)
|
||||
{
|
||||
struct sprd_eic *sprd_eic = gpiochip_get_data(chip);
|
||||
u32 bank, n, girq;
|
||||
|
||||
for (bank = 0; bank * SPRD_EIC_PER_BANK_NR < chip->ngpio; bank++) {
|
||||
void __iomem *base = sprd_eic_offset_base(sprd_eic, bank);
|
||||
unsigned long reg;
|
||||
|
||||
switch (sprd_eic->type) {
|
||||
case SPRD_EIC_DEBOUNCE:
|
||||
reg = readl_relaxed(base + SPRD_EIC_DBNC_MIS) &
|
||||
SPRD_EIC_DATA_MASK;
|
||||
break;
|
||||
case SPRD_EIC_LATCH:
|
||||
reg = readl_relaxed(base + SPRD_EIC_LATCH_INTMSK) &
|
||||
SPRD_EIC_DATA_MASK;
|
||||
break;
|
||||
case SPRD_EIC_ASYNC:
|
||||
reg = readl_relaxed(base + SPRD_EIC_ASYNC_INTMSK) &
|
||||
SPRD_EIC_DATA_MASK;
|
||||
break;
|
||||
case SPRD_EIC_SYNC:
|
||||
reg = readl_relaxed(base + SPRD_EIC_SYNC_INTMSK) &
|
||||
SPRD_EIC_DATA_MASK;
|
||||
break;
|
||||
default:
|
||||
dev_err(chip->parent, "Unsupported EIC type.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for_each_set_bit(n, ®, SPRD_EIC_PER_BANK_NR) {
|
||||
girq = irq_find_mapping(chip->irq.domain,
|
||||
bank * SPRD_EIC_PER_BANK_NR + n);
|
||||
|
||||
generic_handle_irq(girq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void sprd_eic_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_chip *ic = irq_desc_get_chip(desc);
|
||||
struct gpio_chip *chip;
|
||||
enum sprd_eic_type type;
|
||||
|
||||
chained_irq_enter(ic, desc);
|
||||
|
||||
/*
|
||||
* Since the digital-chip EIC 4 sub-modules (debounce, latch, async
|
||||
* and sync) share one same interrupt line, we should iterate each
|
||||
* EIC module to check if there are EIC interrupts were triggered.
|
||||
*/
|
||||
for (type = SPRD_EIC_DEBOUNCE; type < SPRD_EIC_MAX; type++) {
|
||||
chip = gpiochip_find(&type, sprd_eic_match_chip_by_type);
|
||||
if (!chip)
|
||||
continue;
|
||||
|
||||
sprd_eic_handle_one_type(chip);
|
||||
}
|
||||
|
||||
chained_irq_exit(ic, desc);
|
||||
}
|
||||
|
||||
static int sprd_eic_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct sprd_eic_variant_data *pdata;
|
||||
struct gpio_irq_chip *irq;
|
||||
struct sprd_eic *sprd_eic;
|
||||
struct resource *res;
|
||||
int ret, i;
|
||||
|
||||
pdata = of_device_get_match_data(&pdev->dev);
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "No matching driver data found.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sprd_eic = devm_kzalloc(&pdev->dev, sizeof(*sprd_eic), GFP_KERNEL);
|
||||
if (!sprd_eic)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&sprd_eic->lock);
|
||||
sprd_eic->type = pdata->type;
|
||||
|
||||
sprd_eic->irq = platform_get_irq(pdev, 0);
|
||||
if (sprd_eic->irq < 0) {
|
||||
dev_err(&pdev->dev, "Failed to get EIC interrupt.\n");
|
||||
return sprd_eic->irq;
|
||||
}
|
||||
|
||||
for (i = 0; i < SPRD_EIC_MAX_BANK; i++) {
|
||||
/*
|
||||
* We can have maximum 3 banks EICs, and each EIC has
|
||||
* its own base address. But some platform maybe only
|
||||
* have one bank EIC, thus base[1] and base[2] can be
|
||||
* optional.
|
||||
*/
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
|
||||
if (!res)
|
||||
continue;
|
||||
|
||||
sprd_eic->base[i] = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(sprd_eic->base[i]))
|
||||
return PTR_ERR(sprd_eic->base[i]);
|
||||
}
|
||||
|
||||
sprd_eic->chip.label = sprd_eic_label_name[sprd_eic->type];
|
||||
sprd_eic->chip.ngpio = pdata->num_eics;
|
||||
sprd_eic->chip.base = -1;
|
||||
sprd_eic->chip.parent = &pdev->dev;
|
||||
sprd_eic->chip.of_node = pdev->dev.of_node;
|
||||
sprd_eic->chip.direction_input = sprd_eic_direction_input;
|
||||
switch (sprd_eic->type) {
|
||||
case SPRD_EIC_DEBOUNCE:
|
||||
sprd_eic->chip.request = sprd_eic_request;
|
||||
sprd_eic->chip.free = sprd_eic_free;
|
||||
sprd_eic->chip.set_config = sprd_eic_set_config;
|
||||
sprd_eic->chip.set = sprd_eic_set;
|
||||
/* fall-through */
|
||||
case SPRD_EIC_ASYNC:
|
||||
/* fall-through */
|
||||
case SPRD_EIC_SYNC:
|
||||
sprd_eic->chip.get = sprd_eic_get;
|
||||
break;
|
||||
case SPRD_EIC_LATCH:
|
||||
/* fall-through */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
sprd_eic->intc.name = dev_name(&pdev->dev);
|
||||
sprd_eic->intc.irq_ack = sprd_eic_irq_ack;
|
||||
sprd_eic->intc.irq_mask = sprd_eic_irq_mask;
|
||||
sprd_eic->intc.irq_unmask = sprd_eic_irq_unmask;
|
||||
sprd_eic->intc.irq_set_type = sprd_eic_irq_set_type;
|
||||
sprd_eic->intc.flags = IRQCHIP_SKIP_SET_WAKE;
|
||||
|
||||
irq = &sprd_eic->chip.irq;
|
||||
irq->chip = &sprd_eic->intc;
|
||||
irq->handler = handle_bad_irq;
|
||||
irq->default_type = IRQ_TYPE_NONE;
|
||||
irq->parent_handler = sprd_eic_irq_handler;
|
||||
irq->parent_handler_data = sprd_eic;
|
||||
irq->num_parents = 1;
|
||||
irq->parents = &sprd_eic->irq;
|
||||
|
||||
ret = devm_gpiochip_add_data(&pdev->dev, &sprd_eic->chip, sprd_eic);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Could not register gpiochip %d.\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, sprd_eic);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sprd_eic_of_match[] = {
|
||||
{
|
||||
.compatible = "sprd,sc9860-eic-debounce",
|
||||
.data = &sc9860_eic_dbnc_data,
|
||||
},
|
||||
{
|
||||
.compatible = "sprd,sc9860-eic-latch",
|
||||
.data = &sc9860_eic_latch_data,
|
||||
},
|
||||
{
|
||||
.compatible = "sprd,sc9860-eic-async",
|
||||
.data = &sc9860_eic_async_data,
|
||||
},
|
||||
{
|
||||
.compatible = "sprd,sc9860-eic-sync",
|
||||
.data = &sc9860_eic_sync_data,
|
||||
},
|
||||
{
|
||||
/* end of list */
|
||||
}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sprd_eic_of_match);
|
||||
|
||||
static struct platform_driver sprd_eic_driver = {
|
||||
.probe = sprd_eic_probe,
|
||||
.driver = {
|
||||
.name = "sprd-eic",
|
||||
.of_match_table = sprd_eic_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(sprd_eic_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Spreadtrum EIC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -27,7 +27,7 @@
|
|||
#include <linux/irqdomain.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
|
|
|
|||
|
|
@ -14,19 +14,20 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
/* GPIO registers definition */
|
||||
#define GPIO_DATA_OUT 0x00
|
||||
#define GPIO_DATA_IN 0x04
|
||||
#define GPIO_DIR 0x08
|
||||
#define GPIO_BYPASS_IN 0x0C
|
||||
#define GPIO_DATA_SET 0x10
|
||||
#define GPIO_DATA_CLR 0x14
|
||||
#define GPIO_PULL_EN 0x18
|
||||
#define GPIO_PULL_TYPE 0x1C
|
||||
#define GPIO_INT_EN 0x20
|
||||
#define GPIO_INT_STAT 0x24
|
||||
#define GPIO_INT_STAT_RAW 0x24
|
||||
#define GPIO_INT_STAT_MASKED 0x28
|
||||
#define GPIO_INT_MASK 0x2C
|
||||
#define GPIO_INT_CLR 0x30
|
||||
#define GPIO_INT_TYPE 0x34
|
||||
|
|
@ -147,7 +148,7 @@ static void ftgpio_gpio_irq_handler(struct irq_desc *desc)
|
|||
|
||||
chained_irq_enter(irqchip, desc);
|
||||
|
||||
stat = readl(g->base + GPIO_INT_STAT);
|
||||
stat = readl(g->base + GPIO_INT_STAT_RAW);
|
||||
if (stat)
|
||||
for_each_set_bit(offset, &stat, gc->ngpio)
|
||||
generic_handle_irq(irq_find_mapping(gc->irq.domain,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
* This driver supports the following Diamond Systems devices: GPIO-MM and
|
||||
* GPIO-MM-12.
|
||||
*/
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
|
|
@ -171,6 +172,51 @@ static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
|||
return !!(port_state & mask);
|
||||
}
|
||||
|
||||
static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask,
|
||||
unsigned long *bits)
|
||||
{
|
||||
struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip);
|
||||
size_t i;
|
||||
const size_t ports[] = { 0, 1, 2, 4, 5, 6 };
|
||||
const unsigned int gpio_reg_size = 8;
|
||||
unsigned int bits_offset;
|
||||
size_t word_index;
|
||||
unsigned int word_offset;
|
||||
unsigned long word_mask;
|
||||
const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
|
||||
unsigned long port_state;
|
||||
|
||||
/* 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_state = inb(gpiommgpio->base + ports[i]);
|
||||
|
||||
/* store acquired bits at respective bits array offset */
|
||||
bits[word_index] |= port_state << word_offset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
|
|
@ -268,6 +314,7 @@ static int gpiomm_probe(struct device *dev, unsigned int id)
|
|||
gpiommgpio->chip.direction_input = gpiomm_gpio_direction_input;
|
||||
gpiommgpio->chip.direction_output = gpiomm_gpio_direction_output;
|
||||
gpiommgpio->chip.get = gpiomm_gpio_get;
|
||||
gpiommgpio->chip.get_multiple = gpiomm_gpio_get_multiple;
|
||||
gpiommgpio->chip.set = gpiomm_gpio_set;
|
||||
gpiommgpio->chip.set_multiple = gpiomm_gpio_set_multiple;
|
||||
gpiommgpio->base = base[id];
|
||||
|
|
|
|||
|
|
@ -26,9 +26,8 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
|
|
|
|||
115
drivers/gpio/gpio-hlwd.c
Normal file
115
drivers/gpio/gpio-hlwd.c
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
// Copyright (C) 2008-2009 The GameCube Linux Team
|
||||
// Copyright (C) 2008,2009 Albert Herranz
|
||||
// Copyright (C) 2017-2018 Jonathan Neuschäfer
|
||||
//
|
||||
// Nintendo Wii (Hollywood) GPIO driver
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* Register names and offsets courtesy of WiiBrew:
|
||||
* https://wiibrew.org/wiki/Hardware/Hollywood_GPIOs
|
||||
*
|
||||
* Note that for most registers, there are two versions:
|
||||
* - HW_GPIOB_* Is always accessible by the Broadway PowerPC core, but does
|
||||
* always give access to all GPIO lines
|
||||
* - HW_GPIO_* Is only accessible by the Broadway PowerPC code if the memory
|
||||
* firewall (AHBPROT) in the Hollywood chipset has been configured to allow
|
||||
* such access.
|
||||
*
|
||||
* The ownership of each GPIO line can be configured in the HW_GPIO_OWNER
|
||||
* register: A one bit configures the line for access via the HW_GPIOB_*
|
||||
* registers, a zero bit indicates access via HW_GPIO_*. This driver uses
|
||||
* HW_GPIOB_*.
|
||||
*/
|
||||
#define HW_GPIOB_OUT 0x00
|
||||
#define HW_GPIOB_DIR 0x04
|
||||
#define HW_GPIOB_IN 0x08
|
||||
#define HW_GPIOB_INTLVL 0x0c
|
||||
#define HW_GPIOB_INTFLAG 0x10
|
||||
#define HW_GPIOB_INTMASK 0x14
|
||||
#define HW_GPIOB_INMIR 0x18
|
||||
#define HW_GPIO_ENABLE 0x1c
|
||||
#define HW_GPIO_OUT 0x20
|
||||
#define HW_GPIO_DIR 0x24
|
||||
#define HW_GPIO_IN 0x28
|
||||
#define HW_GPIO_INTLVL 0x2c
|
||||
#define HW_GPIO_INTFLAG 0x30
|
||||
#define HW_GPIO_INTMASK 0x34
|
||||
#define HW_GPIO_INMIR 0x38
|
||||
#define HW_GPIO_OWNER 0x3c
|
||||
|
||||
struct hlwd_gpio {
|
||||
struct gpio_chip gpioc;
|
||||
void __iomem *regs;
|
||||
};
|
||||
|
||||
static int hlwd_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct hlwd_gpio *hlwd;
|
||||
struct resource *regs_resource;
|
||||
u32 ngpios;
|
||||
int res;
|
||||
|
||||
hlwd = devm_kzalloc(&pdev->dev, sizeof(*hlwd), GFP_KERNEL);
|
||||
if (!hlwd)
|
||||
return -ENOMEM;
|
||||
|
||||
regs_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
hlwd->regs = devm_ioremap_resource(&pdev->dev, regs_resource);
|
||||
if (IS_ERR(hlwd->regs))
|
||||
return PTR_ERR(hlwd->regs);
|
||||
|
||||
/*
|
||||
* Claim all GPIOs using the OWNER register. This will not work on
|
||||
* systems where the AHBPROT memory firewall hasn't been configured to
|
||||
* permit PPC access to HW_GPIO_*.
|
||||
*
|
||||
* Note that this has to happen before bgpio_init reads the
|
||||
* HW_GPIOB_OUT and HW_GPIOB_DIR, because otherwise it reads the wrong
|
||||
* values.
|
||||
*/
|
||||
iowrite32be(0xffffffff, hlwd->regs + HW_GPIO_OWNER);
|
||||
|
||||
res = bgpio_init(&hlwd->gpioc, &pdev->dev, 4,
|
||||
hlwd->regs + HW_GPIOB_IN, hlwd->regs + HW_GPIOB_OUT,
|
||||
NULL, hlwd->regs + HW_GPIOB_DIR, NULL,
|
||||
BGPIOF_BIG_ENDIAN_BYTE_ORDER);
|
||||
if (res < 0) {
|
||||
dev_warn(&pdev->dev, "bgpio_init failed: %d\n", res);
|
||||
return res;
|
||||
}
|
||||
|
||||
res = of_property_read_u32(pdev->dev.of_node, "ngpios", &ngpios);
|
||||
if (res)
|
||||
ngpios = 32;
|
||||
hlwd->gpioc.ngpio = ngpios;
|
||||
|
||||
return devm_gpiochip_add_data(&pdev->dev, &hlwd->gpioc, hlwd);
|
||||
}
|
||||
|
||||
static const struct of_device_id hlwd_gpio_match[] = {
|
||||
{ .compatible = "nintendo,hollywood-gpio", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hlwd_gpio_match);
|
||||
|
||||
static struct platform_driver hlwd_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "gpio-hlwd",
|
||||
.of_match_table = hlwd_gpio_match,
|
||||
},
|
||||
.probe = hlwd_gpio_probe,
|
||||
};
|
||||
module_platform_driver(hlwd_gpio_driver);
|
||||
|
||||
MODULE_AUTHOR("Jonathan Neuschäfer <[email protected]>");
|
||||
MODULE_DESCRIPTION("Nintendo Wii GPIO driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
|
||||
struct egpio_chip {
|
||||
int reg_start;
|
||||
|
|
|
|||
|
|
@ -23,9 +23,10 @@
|
|||
#include <linux/ioport.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mfd/lpc_ich.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#define DRV_NAME "gpio_ich"
|
||||
|
||||
|
|
@ -131,9 +132,9 @@ static int ichx_write_bit(int reg, unsigned nr, int val, int verify)
|
|||
ichx_priv.gpio_base);
|
||||
|
||||
if (val)
|
||||
data |= 1 << bit;
|
||||
data |= BIT(bit);
|
||||
else
|
||||
data &= ~(1 << bit);
|
||||
data &= ~BIT(bit);
|
||||
ICHX_WRITE(data, ichx_priv.desc->regs[reg][reg_nr],
|
||||
ichx_priv.gpio_base);
|
||||
if (reg == GPIO_LVL && ichx_priv.desc->use_outlvl_cache)
|
||||
|
|
@ -166,17 +167,17 @@ static int ichx_read_bit(int reg, unsigned nr)
|
|||
|
||||
spin_unlock_irqrestore(&ichx_priv.lock, flags);
|
||||
|
||||
return data & (1 << bit) ? 1 : 0;
|
||||
return !!(data & BIT(bit));
|
||||
}
|
||||
|
||||
static bool ichx_gpio_check_available(struct gpio_chip *gpio, unsigned nr)
|
||||
{
|
||||
return !!(ichx_priv.use_gpio & (1 << (nr / 32)));
|
||||
return !!(ichx_priv.use_gpio & BIT(nr / 32));
|
||||
}
|
||||
|
||||
static int ichx_gpio_get_direction(struct gpio_chip *gpio, unsigned nr)
|
||||
{
|
||||
return ichx_read_bit(GPIO_IO_SEL, nr) ? GPIOF_DIR_IN : GPIOF_DIR_OUT;
|
||||
return ichx_read_bit(GPIO_IO_SEL, nr);
|
||||
}
|
||||
|
||||
static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)
|
||||
|
|
@ -232,12 +233,12 @@ static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr)
|
|||
spin_lock_irqsave(&ichx_priv.lock, flags);
|
||||
|
||||
/* GPI 0 - 15 are latched, write 1 to clear*/
|
||||
ICHX_WRITE(1 << (16 + nr), 0, ichx_priv.pm_base);
|
||||
ICHX_WRITE(BIT(16 + nr), 0, ichx_priv.pm_base);
|
||||
data = ICHX_READ(0, ichx_priv.pm_base);
|
||||
|
||||
spin_unlock_irqrestore(&ichx_priv.lock, flags);
|
||||
|
||||
return (data >> 16) & (1 << nr) ? 1 : 0;
|
||||
return !!((data >> 16) & BIT(nr));
|
||||
} else {
|
||||
return ichx_gpio_get(chip, nr);
|
||||
}
|
||||
|
|
@ -254,7 +255,7 @@ static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr)
|
|||
* the chipset's USE value can be trusted for this specific bit.
|
||||
* If it can't be trusted, assume that the pin can be used as a GPIO.
|
||||
*/
|
||||
if (ichx_priv.desc->use_sel_ignore[nr / 32] & (1 << (nr & 0x1f)))
|
||||
if (ichx_priv.desc->use_sel_ignore[nr / 32] & BIT(nr & 0x1f))
|
||||
return 0;
|
||||
|
||||
return ichx_read_bit(GPIO_USE_SEL, nr) ? 0 : -ENODEV;
|
||||
|
|
@ -394,7 +395,7 @@ static int ichx_gpio_request_regions(struct device *dev,
|
|||
return -ENODEV;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ichx_priv.desc->regs[0]); i++) {
|
||||
if (!(use_gpio & (1 << i)))
|
||||
if (!(use_gpio & BIT(i)))
|
||||
continue;
|
||||
if (!devm_request_region(dev,
|
||||
res_base->start + ichx_priv.desc->regs[0][i],
|
||||
|
|
|
|||
|
|
@ -361,10 +361,8 @@ static int intel_gpio_probe(struct pci_dev *pdev,
|
|||
pcim_iounmap_regions(pdev, 1 << 1);
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev, "can't allocate chip data\n");
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->reg_base = pcim_iomap_table(pdev)[0];
|
||||
priv->chip.label = dev_name(&pdev->dev);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
|
||||
/* Chip Id numbers */
|
||||
#define NO_DEV_ID 0xffff
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitops.h>
|
||||
|
||||
#include <linux/mfd/janz.h>
|
||||
|
||||
|
|
@ -33,9 +34,9 @@
|
|||
#define MASTER_INT_CTL 0x00
|
||||
#define MASTER_CONF_CTL 0x01
|
||||
|
||||
#define CONF_PAE (1 << 2)
|
||||
#define CONF_PBE (1 << 7)
|
||||
#define CONF_PCE (1 << 4)
|
||||
#define CONF_PAE BIT(2)
|
||||
#define CONF_PBE BIT(7)
|
||||
#define CONF_PCE BIT(4)
|
||||
|
||||
struct ttl_control_regs {
|
||||
__be16 portc;
|
||||
|
|
@ -74,7 +75,7 @@ static int ttl_get_value(struct gpio_chip *gpio, unsigned offset)
|
|||
}
|
||||
|
||||
spin_lock(&mod->lock);
|
||||
ret = *shadow & (1 << offset);
|
||||
ret = *shadow & BIT(offset);
|
||||
spin_unlock(&mod->lock);
|
||||
return !!ret;
|
||||
}
|
||||
|
|
@ -100,9 +101,9 @@ static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value)
|
|||
|
||||
spin_lock(&mod->lock);
|
||||
if (value)
|
||||
*shadow |= (1 << offset);
|
||||
*shadow |= BIT(offset);
|
||||
else
|
||||
*shadow &= ~(1 << offset);
|
||||
*shadow &= ~BIT(offset);
|
||||
|
||||
iowrite16be(*shadow, port);
|
||||
spin_unlock(&mod->lock);
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#include <linux/bitops.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/mfd/kempld.h>
|
||||
|
||||
#define KEMPLD_GPIO_MAX_NUM 16
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/init.h>
|
||||
|
|
|
|||
|
|
@ -315,12 +315,17 @@ static void gpiod_set_array_single_value_cansleep(unsigned int ndescs,
|
|||
struct gpio_desc **desc,
|
||||
int value)
|
||||
{
|
||||
int i, values[ndescs];
|
||||
int i, *values;
|
||||
|
||||
values = kmalloc_array(ndescs, sizeof(*values), GFP_KERNEL);
|
||||
if (!values)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ndescs; i++)
|
||||
values[i] = value;
|
||||
|
||||
gpiod_set_array_value_cansleep(ndescs, desc, values);
|
||||
kfree(values);
|
||||
}
|
||||
|
||||
static struct gpio_descs *devm_gpiod_get_array_optional_count(
|
||||
|
|
|
|||
|
|
@ -416,10 +416,8 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
|
|||
pcim_iounmap_regions(pdev, BIT(1));
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev, "can't allocate chip data\n");
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->dev = &pdev->dev;
|
||||
priv->reg_base = pcim_iomap_table(pdev)[0];
|
||||
|
|
|
|||
|
|
@ -445,7 +445,6 @@ static int ioh_gpio_probe(struct pci_dev *pdev,
|
|||
|
||||
chip_save = kzalloc(sizeof(*chip) * 8, GFP_KERNEL);
|
||||
if (chip_save == NULL) {
|
||||
dev_err(&pdev->dev, "%s : kzalloc failed", __func__);
|
||||
ret = -ENOMEM;
|
||||
goto err_kzalloc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* GPIO Testing Device Driver
|
||||
*
|
||||
* Copyright (C) 2014 Kamlakant Patel <kamlakant.patel@broadcom.com>
|
||||
* Copyright (C) 2015-2016 Bamvor Jian Zhang <bamvor.zhangjian@linaro.org>
|
||||
* Copyright (C) 2015-2016 Bamvor Jian Zhang <bamv2005@gmail.com>
|
||||
* Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
|
|
@ -314,7 +314,7 @@ static int gpio_mockup_probe(struct platform_device *pdev)
|
|||
if (rv)
|
||||
return rv;
|
||||
|
||||
if (gpio_mockup_dbg_dir)
|
||||
if (!IS_ERR_OR_NULL(gpio_mockup_dbg_dir))
|
||||
gpio_mockup_debugfs_setup(dev, chip);
|
||||
|
||||
return 0;
|
||||
|
|
@ -411,7 +411,7 @@ module_init(gpio_mockup_init);
|
|||
module_exit(gpio_mockup_exit);
|
||||
|
||||
MODULE_AUTHOR("Kamlakant Patel <[email protected]>");
|
||||
MODULE_AUTHOR("Bamvor Jian Zhang <bamv[email protected]>");
|
||||
MODULE_AUTHOR("Bamvor Jian Zhang <bamv[email protected]>");
|
||||
MODULE_AUTHOR("Bartosz Golaszewski <[email protected]>");
|
||||
MODULE_DESCRIPTION("GPIO Testing driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
|||
|
|
@ -1157,11 +1157,9 @@ static int omap_gpio_probe(struct platform_device *pdev)
|
|||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
bank = devm_kzalloc(dev, sizeof(struct gpio_bank), GFP_KERNEL);
|
||||
if (!bank) {
|
||||
dev_err(dev, "Memory alloc failed\n");
|
||||
bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
|
||||
if (!bank)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
irqc = devm_kzalloc(dev, sizeof(*irqc), GFP_KERNEL);
|
||||
if (!irqc)
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ static const struct i2c_device_id pca953x_id[] = {
|
|||
{ "pca9575", 16 | PCA957X_TYPE | PCA_INT, },
|
||||
{ "pca9698", 40 | PCA953X_TYPE, },
|
||||
|
||||
{ "pcal6524", 24 | PCA953X_TYPE | PCA_INT | PCA_PCAL, },
|
||||
{ "pcal9555a", 16 | PCA953X_TYPE | PCA_INT | PCA_PCAL, },
|
||||
|
||||
{ "max7310", 8 | PCA953X_TYPE, },
|
||||
|
|
@ -935,6 +936,9 @@ static const struct of_device_id pca953x_dt_ids[] = {
|
|||
{ .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), },
|
||||
{ .compatible = "nxp,pca9698", .data = OF_953X(40, 0), },
|
||||
|
||||
{ .compatible = "nxp,pcal6524", .data = OF_953X(24, PCA_INT), },
|
||||
{ .compatible = "nxp,pcal9555a", .data = OF_953X(16, PCA_INT), },
|
||||
|
||||
{ .compatible = "maxim,max7310", .data = OF_953X( 8, 0), },
|
||||
{ .compatible = "maxim,max7312", .data = OF_953X(16, PCA_INT), },
|
||||
{ .compatible = "maxim,max7313", .data = OF_953X(16, PCA_INT), },
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
|
|
@ -103,6 +104,54 @@ static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
|||
return !!(ioread8(&idio16gpio->reg->in8_15) & (mask >> 24));
|
||||
}
|
||||
|
||||
static int idio_16_gpio_get_multiple(struct gpio_chip *chip,
|
||||
unsigned long *mask, unsigned long *bits)
|
||||
{
|
||||
struct idio_16_gpio *const idio16gpio = 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 word_mask;
|
||||
const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
|
||||
unsigned long port_state;
|
||||
u8 __iomem ports[] = {
|
||||
idio16gpio->reg->out0_7, idio16gpio->reg->out8_15,
|
||||
idio16gpio->reg->in0_7, idio16gpio->reg->in8_15,
|
||||
};
|
||||
|
||||
/* 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_state = ioread8(ports + i);
|
||||
|
||||
/* store acquired bits at respective bits array offset */
|
||||
bits[word_index] |= port_state << word_offset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
|
|
@ -299,6 +348,7 @@ static int idio_16_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
idio16gpio->chip.direction_input = idio_16_gpio_direction_input;
|
||||
idio16gpio->chip.direction_output = idio_16_gpio_direction_output;
|
||||
idio16gpio->chip.get = idio_16_gpio_get;
|
||||
idio16gpio->chip.get_multiple = idio_16_gpio_get_multiple;
|
||||
idio16gpio->chip.set = idio_16_gpio_set;
|
||||
idio16gpio->chip.set_multiple = idio_16_gpio_set_multiple;
|
||||
|
||||
|
|
|
|||
|
|
@ -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 word_mask;
|
||||
const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
|
||||
unsigned long port_state;
|
||||
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,65 @@ 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 flags;
|
||||
unsigned int out_state;
|
||||
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 +512,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_multiple = idio_24_gpio_set_multiple;
|
||||
|
||||
raw_spin_lock_init(&idio24gpio->lock);
|
||||
|
||||
|
|
|
|||
330
drivers/gpio/gpio-pmic-eic-sprd.c
Normal file
330
drivers/gpio/gpio-pmic-eic-sprd.c
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018 Spreadtrum Communications Inc.
|
||||
* Copyright (C) 2018 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* EIC registers definition */
|
||||
#define SPRD_PMIC_EIC_DATA 0x0
|
||||
#define SPRD_PMIC_EIC_DMSK 0x4
|
||||
#define SPRD_PMIC_EIC_IEV 0x14
|
||||
#define SPRD_PMIC_EIC_IE 0x18
|
||||
#define SPRD_PMIC_EIC_RIS 0x1c
|
||||
#define SPRD_PMIC_EIC_MIS 0x20
|
||||
#define SPRD_PMIC_EIC_IC 0x24
|
||||
#define SPRD_PMIC_EIC_TRIG 0x28
|
||||
#define SPRD_PMIC_EIC_CTRL0 0x40
|
||||
|
||||
/*
|
||||
* The PMIC EIC controller only has one bank, and each bank now can contain
|
||||
* 16 EICs.
|
||||
*/
|
||||
#define SPRD_PMIC_EIC_PER_BANK_NR 16
|
||||
#define SPRD_PMIC_EIC_NR SPRD_PMIC_EIC_PER_BANK_NR
|
||||
#define SPRD_PMIC_EIC_DATA_MASK GENMASK(15, 0)
|
||||
#define SPRD_PMIC_EIC_BIT(x) ((x) & (SPRD_PMIC_EIC_PER_BANK_NR - 1))
|
||||
#define SPRD_PMIC_EIC_DBNC_MASK GENMASK(11, 0)
|
||||
|
||||
/*
|
||||
* These registers are modified under the irq bus lock and cached to avoid
|
||||
* unnecessary writes in bus_sync_unlock.
|
||||
*/
|
||||
enum {
|
||||
REG_IEV,
|
||||
REG_IE,
|
||||
REG_TRIG,
|
||||
CACHE_NR_REGS
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sprd_pmic_eic - PMIC EIC controller
|
||||
* @chip: the gpio_chip structure.
|
||||
* @intc: the irq_chip structure.
|
||||
* @regmap: the regmap from the parent device.
|
||||
* @offset: the EIC controller's offset address of the PMIC.
|
||||
* @reg: the array to cache the EIC registers.
|
||||
* @buslock: for bus lock/sync and unlock.
|
||||
* @irq: the interrupt number of the PMIC EIC conteroller.
|
||||
*/
|
||||
struct sprd_pmic_eic {
|
||||
struct gpio_chip chip;
|
||||
struct irq_chip intc;
|
||||
struct regmap *map;
|
||||
u32 offset;
|
||||
u8 reg[CACHE_NR_REGS];
|
||||
struct mutex buslock;
|
||||
int irq;
|
||||
};
|
||||
|
||||
static void sprd_pmic_eic_update(struct gpio_chip *chip, unsigned int offset,
|
||||
u16 reg, unsigned int val)
|
||||
{
|
||||
struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
|
||||
u32 shift = SPRD_PMIC_EIC_BIT(offset);
|
||||
|
||||
regmap_update_bits(pmic_eic->map, pmic_eic->offset + reg,
|
||||
BIT(shift), val << shift);
|
||||
}
|
||||
|
||||
static int sprd_pmic_eic_read(struct gpio_chip *chip, unsigned int offset,
|
||||
u16 reg)
|
||||
{
|
||||
struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
|
||||
u32 value;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(pmic_eic->map, pmic_eic->offset + reg, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return !!(value & BIT(SPRD_PMIC_EIC_BIT(offset)));
|
||||
}
|
||||
|
||||
static int sprd_pmic_eic_request(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_DMSK, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_pmic_eic_free(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_DMSK, 0);
|
||||
}
|
||||
|
||||
static int sprd_pmic_eic_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
return sprd_pmic_eic_read(chip, offset, SPRD_PMIC_EIC_DATA);
|
||||
}
|
||||
|
||||
static int sprd_pmic_eic_direction_input(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
/* EICs are always input, nothing need to do here. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_pmic_eic_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
/* EICs are always input, nothing need to do here. */
|
||||
}
|
||||
|
||||
static int sprd_pmic_eic_set_debounce(struct gpio_chip *chip,
|
||||
unsigned int offset,
|
||||
unsigned int debounce)
|
||||
{
|
||||
struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
|
||||
u32 reg, value;
|
||||
int ret;
|
||||
|
||||
reg = SPRD_PMIC_EIC_CTRL0 + SPRD_PMIC_EIC_BIT(offset) * 0x4;
|
||||
ret = regmap_read(pmic_eic->map, pmic_eic->offset + reg, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
value &= ~SPRD_PMIC_EIC_DBNC_MASK;
|
||||
value |= (debounce / 1000) & SPRD_PMIC_EIC_DBNC_MASK;
|
||||
return regmap_write(pmic_eic->map, pmic_eic->offset + reg, value);
|
||||
}
|
||||
|
||||
static int sprd_pmic_eic_set_config(struct gpio_chip *chip, unsigned int offset,
|
||||
unsigned long config)
|
||||
{
|
||||
unsigned long param = pinconf_to_config_param(config);
|
||||
u32 arg = pinconf_to_config_argument(config);
|
||||
|
||||
if (param == PIN_CONFIG_INPUT_DEBOUNCE)
|
||||
return sprd_pmic_eic_set_debounce(chip, offset, arg);
|
||||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static void sprd_pmic_eic_irq_mask(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
|
||||
|
||||
pmic_eic->reg[REG_IE] = 0;
|
||||
pmic_eic->reg[REG_TRIG] = 0;
|
||||
}
|
||||
|
||||
static void sprd_pmic_eic_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
|
||||
|
||||
pmic_eic->reg[REG_IE] = 1;
|
||||
pmic_eic->reg[REG_TRIG] = 1;
|
||||
}
|
||||
|
||||
static int sprd_pmic_eic_irq_set_type(struct irq_data *data,
|
||||
unsigned int flow_type)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
|
||||
|
||||
switch (flow_type) {
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
pmic_eic->reg[REG_IEV] = 1;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
pmic_eic->reg[REG_IEV] = 0;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_pmic_eic_bus_lock(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
|
||||
|
||||
mutex_lock(&pmic_eic->buslock);
|
||||
}
|
||||
|
||||
static void sprd_pmic_eic_bus_sync_unlock(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
struct sprd_pmic_eic *pmic_eic = gpiochip_get_data(chip);
|
||||
u32 offset = irqd_to_hwirq(data);
|
||||
|
||||
/* Set irq type */
|
||||
sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IEV,
|
||||
pmic_eic->reg[REG_IEV]);
|
||||
/* Set irq unmask */
|
||||
sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_IE,
|
||||
pmic_eic->reg[REG_IE]);
|
||||
/* Generate trigger start pulse for debounce EIC */
|
||||
sprd_pmic_eic_update(chip, offset, SPRD_PMIC_EIC_TRIG,
|
||||
pmic_eic->reg[REG_TRIG]);
|
||||
|
||||
mutex_unlock(&pmic_eic->buslock);
|
||||
}
|
||||
|
||||
static irqreturn_t sprd_pmic_eic_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct sprd_pmic_eic *pmic_eic = data;
|
||||
struct gpio_chip *chip = &pmic_eic->chip;
|
||||
unsigned long status;
|
||||
u32 n, girq, val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(pmic_eic->map, pmic_eic->offset + SPRD_PMIC_EIC_MIS,
|
||||
&val);
|
||||
if (ret)
|
||||
return IRQ_RETVAL(ret);
|
||||
|
||||
status = val & SPRD_PMIC_EIC_DATA_MASK;
|
||||
|
||||
for_each_set_bit(n, &status, chip->ngpio) {
|
||||
/* Clear the interrupt */
|
||||
sprd_pmic_eic_update(chip, n, SPRD_PMIC_EIC_IC, 1);
|
||||
|
||||
girq = irq_find_mapping(chip->irq.domain, n);
|
||||
handle_nested_irq(girq);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sprd_pmic_eic_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_irq_chip *irq;
|
||||
struct sprd_pmic_eic *pmic_eic;
|
||||
int ret;
|
||||
|
||||
pmic_eic = devm_kzalloc(&pdev->dev, sizeof(*pmic_eic), GFP_KERNEL);
|
||||
if (!pmic_eic)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&pmic_eic->buslock);
|
||||
|
||||
pmic_eic->irq = platform_get_irq(pdev, 0);
|
||||
if (pmic_eic->irq < 0) {
|
||||
dev_err(&pdev->dev, "Failed to get PMIC EIC interrupt.\n");
|
||||
return pmic_eic->irq;
|
||||
}
|
||||
|
||||
pmic_eic->map = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!pmic_eic->map)
|
||||
return -ENODEV;
|
||||
|
||||
ret = of_property_read_u32(pdev->dev.of_node, "reg", &pmic_eic->offset);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to get PMIC EIC base address.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, pmic_eic->irq, NULL,
|
||||
sprd_pmic_eic_irq_handler,
|
||||
IRQF_TRIGGER_LOW |
|
||||
IRQF_ONESHOT | IRQF_NO_SUSPEND,
|
||||
dev_name(&pdev->dev), pmic_eic);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to request PMIC EIC IRQ.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pmic_eic->chip.label = dev_name(&pdev->dev);
|
||||
pmic_eic->chip.ngpio = SPRD_PMIC_EIC_NR;
|
||||
pmic_eic->chip.base = -1;
|
||||
pmic_eic->chip.parent = &pdev->dev;
|
||||
pmic_eic->chip.of_node = pdev->dev.of_node;
|
||||
pmic_eic->chip.direction_input = sprd_pmic_eic_direction_input;
|
||||
pmic_eic->chip.request = sprd_pmic_eic_request;
|
||||
pmic_eic->chip.free = sprd_pmic_eic_free;
|
||||
pmic_eic->chip.set_config = sprd_pmic_eic_set_config;
|
||||
pmic_eic->chip.set = sprd_pmic_eic_set;
|
||||
pmic_eic->chip.get = sprd_pmic_eic_get;
|
||||
|
||||
pmic_eic->intc.name = dev_name(&pdev->dev);
|
||||
pmic_eic->intc.irq_mask = sprd_pmic_eic_irq_mask;
|
||||
pmic_eic->intc.irq_unmask = sprd_pmic_eic_irq_unmask;
|
||||
pmic_eic->intc.irq_set_type = sprd_pmic_eic_irq_set_type;
|
||||
pmic_eic->intc.irq_bus_lock = sprd_pmic_eic_bus_lock;
|
||||
pmic_eic->intc.irq_bus_sync_unlock = sprd_pmic_eic_bus_sync_unlock;
|
||||
pmic_eic->intc.flags = IRQCHIP_SKIP_SET_WAKE;
|
||||
|
||||
irq = &pmic_eic->chip.irq;
|
||||
irq->chip = &pmic_eic->intc;
|
||||
irq->threaded = true;
|
||||
|
||||
ret = devm_gpiochip_add_data(&pdev->dev, &pmic_eic->chip, pmic_eic);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Could not register gpiochip %d.\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, pmic_eic);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sprd_pmic_eic_of_match[] = {
|
||||
{ .compatible = "sprd,sc27xx-eic", },
|
||||
{ /* end of list */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sprd_pmic_eic_of_match);
|
||||
|
||||
static struct platform_driver sprd_pmic_eic_driver = {
|
||||
.probe = sprd_pmic_eic_probe,
|
||||
.driver = {
|
||||
.name = "sprd-pmic-eic",
|
||||
.of_match_table = sprd_pmic_eic_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(sprd_pmic_eic_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Spreadtrum PMIC EIC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
252
drivers/gpio/gpio-raspberrypi-exp.c
Normal file
252
drivers/gpio/gpio-raspberrypi-exp.c
Normal file
|
|
@ -0,0 +1,252 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Raspberry Pi 3 expander GPIO driver
|
||||
*
|
||||
* Uses the firmware mailbox service to communicate with the
|
||||
* GPIO expander on the VPU.
|
||||
*
|
||||
* Copyright (C) 2017 Raspberry Pi Trading Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <soc/bcm2835/raspberrypi-firmware.h>
|
||||
|
||||
#define MODULE_NAME "raspberrypi-exp-gpio"
|
||||
#define NUM_GPIO 8
|
||||
|
||||
#define RPI_EXP_GPIO_BASE 128
|
||||
|
||||
#define RPI_EXP_GPIO_DIR_IN 0
|
||||
#define RPI_EXP_GPIO_DIR_OUT 1
|
||||
|
||||
struct rpi_exp_gpio {
|
||||
struct gpio_chip gc;
|
||||
struct rpi_firmware *fw;
|
||||
};
|
||||
|
||||
/* VC4 firmware mailbox interface data structures */
|
||||
|
||||
struct gpio_set_config {
|
||||
u32 gpio;
|
||||
u32 direction;
|
||||
u32 polarity;
|
||||
u32 term_en;
|
||||
u32 term_pull_up;
|
||||
u32 state;
|
||||
};
|
||||
|
||||
struct gpio_get_config {
|
||||
u32 gpio;
|
||||
u32 direction;
|
||||
u32 polarity;
|
||||
u32 term_en;
|
||||
u32 term_pull_up;
|
||||
};
|
||||
|
||||
struct gpio_get_set_state {
|
||||
u32 gpio;
|
||||
u32 state;
|
||||
};
|
||||
|
||||
static int rpi_exp_gpio_get_polarity(struct gpio_chip *gc, unsigned int off)
|
||||
{
|
||||
struct rpi_exp_gpio *gpio;
|
||||
struct gpio_get_config get;
|
||||
int ret;
|
||||
|
||||
gpio = gpiochip_get_data(gc);
|
||||
|
||||
get.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */
|
||||
|
||||
ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_GET_GPIO_CONFIG,
|
||||
&get, sizeof(get));
|
||||
if (ret || get.gpio != 0) {
|
||||
dev_err(gc->parent, "Failed to get GPIO %u config (%d %x)\n",
|
||||
off, ret, get.gpio);
|
||||
return ret ? ret : -EIO;
|
||||
}
|
||||
return get.polarity;
|
||||
}
|
||||
|
||||
static int rpi_exp_gpio_dir_in(struct gpio_chip *gc, unsigned int off)
|
||||
{
|
||||
struct rpi_exp_gpio *gpio;
|
||||
struct gpio_set_config set_in;
|
||||
int ret;
|
||||
|
||||
gpio = gpiochip_get_data(gc);
|
||||
|
||||
set_in.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */
|
||||
set_in.direction = RPI_EXP_GPIO_DIR_IN;
|
||||
set_in.term_en = 0; /* termination disabled */
|
||||
set_in.term_pull_up = 0; /* n/a as termination disabled */
|
||||
set_in.state = 0; /* n/a as configured as an input */
|
||||
|
||||
ret = rpi_exp_gpio_get_polarity(gc, off);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
set_in.polarity = ret; /* Retain existing setting */
|
||||
|
||||
ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_SET_GPIO_CONFIG,
|
||||
&set_in, sizeof(set_in));
|
||||
if (ret || set_in.gpio != 0) {
|
||||
dev_err(gc->parent, "Failed to set GPIO %u to input (%d %x)\n",
|
||||
off, ret, set_in.gpio);
|
||||
return ret ? ret : -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rpi_exp_gpio_dir_out(struct gpio_chip *gc, unsigned int off, int val)
|
||||
{
|
||||
struct rpi_exp_gpio *gpio;
|
||||
struct gpio_set_config set_out;
|
||||
int ret;
|
||||
|
||||
gpio = gpiochip_get_data(gc);
|
||||
|
||||
set_out.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */
|
||||
set_out.direction = RPI_EXP_GPIO_DIR_OUT;
|
||||
set_out.term_en = 0; /* n/a as an output */
|
||||
set_out.term_pull_up = 0; /* n/a as termination disabled */
|
||||
set_out.state = val; /* Output state */
|
||||
|
||||
ret = rpi_exp_gpio_get_polarity(gc, off);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
set_out.polarity = ret; /* Retain existing setting */
|
||||
|
||||
ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_SET_GPIO_CONFIG,
|
||||
&set_out, sizeof(set_out));
|
||||
if (ret || set_out.gpio != 0) {
|
||||
dev_err(gc->parent, "Failed to set GPIO %u to output (%d %x)\n",
|
||||
off, ret, set_out.gpio);
|
||||
return ret ? ret : -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rpi_exp_gpio_get_direction(struct gpio_chip *gc, unsigned int off)
|
||||
{
|
||||
struct rpi_exp_gpio *gpio;
|
||||
struct gpio_get_config get;
|
||||
int ret;
|
||||
|
||||
gpio = gpiochip_get_data(gc);
|
||||
|
||||
get.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */
|
||||
|
||||
ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_GET_GPIO_CONFIG,
|
||||
&get, sizeof(get));
|
||||
if (ret || get.gpio != 0) {
|
||||
dev_err(gc->parent,
|
||||
"Failed to get GPIO %u config (%d %x)\n", off, ret,
|
||||
get.gpio);
|
||||
return ret ? ret : -EIO;
|
||||
}
|
||||
return !get.direction;
|
||||
}
|
||||
|
||||
static int rpi_exp_gpio_get(struct gpio_chip *gc, unsigned int off)
|
||||
{
|
||||
struct rpi_exp_gpio *gpio;
|
||||
struct gpio_get_set_state get;
|
||||
int ret;
|
||||
|
||||
gpio = gpiochip_get_data(gc);
|
||||
|
||||
get.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */
|
||||
get.state = 0; /* storage for returned value */
|
||||
|
||||
ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_GET_GPIO_STATE,
|
||||
&get, sizeof(get));
|
||||
if (ret || get.gpio != 0) {
|
||||
dev_err(gc->parent,
|
||||
"Failed to get GPIO %u state (%d %x)\n", off, ret,
|
||||
get.gpio);
|
||||
return ret ? ret : -EIO;
|
||||
}
|
||||
return !!get.state;
|
||||
}
|
||||
|
||||
static void rpi_exp_gpio_set(struct gpio_chip *gc, unsigned int off, int val)
|
||||
{
|
||||
struct rpi_exp_gpio *gpio;
|
||||
struct gpio_get_set_state set;
|
||||
int ret;
|
||||
|
||||
gpio = gpiochip_get_data(gc);
|
||||
|
||||
set.gpio = off + RPI_EXP_GPIO_BASE; /* GPIO to update */
|
||||
set.state = val; /* Output state */
|
||||
|
||||
ret = rpi_firmware_property(gpio->fw, RPI_FIRMWARE_SET_GPIO_STATE,
|
||||
&set, sizeof(set));
|
||||
if (ret || set.gpio != 0)
|
||||
dev_err(gc->parent,
|
||||
"Failed to set GPIO %u state (%d %x)\n", off, ret,
|
||||
set.gpio);
|
||||
}
|
||||
|
||||
static int rpi_exp_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *fw_node;
|
||||
struct rpi_firmware *fw;
|
||||
struct rpi_exp_gpio *rpi_gpio;
|
||||
|
||||
fw_node = of_get_parent(np);
|
||||
if (!fw_node) {
|
||||
dev_err(dev, "Missing firmware node\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
fw = rpi_firmware_get(fw_node);
|
||||
if (!fw)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
rpi_gpio = devm_kzalloc(dev, sizeof(*rpi_gpio), GFP_KERNEL);
|
||||
if (!rpi_gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
rpi_gpio->fw = fw;
|
||||
rpi_gpio->gc.parent = dev;
|
||||
rpi_gpio->gc.label = MODULE_NAME;
|
||||
rpi_gpio->gc.owner = THIS_MODULE;
|
||||
rpi_gpio->gc.of_node = np;
|
||||
rpi_gpio->gc.base = -1;
|
||||
rpi_gpio->gc.ngpio = NUM_GPIO;
|
||||
|
||||
rpi_gpio->gc.direction_input = rpi_exp_gpio_dir_in;
|
||||
rpi_gpio->gc.direction_output = rpi_exp_gpio_dir_out;
|
||||
rpi_gpio->gc.get_direction = rpi_exp_gpio_get_direction;
|
||||
rpi_gpio->gc.get = rpi_exp_gpio_get;
|
||||
rpi_gpio->gc.set = rpi_exp_gpio_set;
|
||||
rpi_gpio->gc.can_sleep = true;
|
||||
|
||||
return devm_gpiochip_add_data(dev, &rpi_gpio->gc, rpi_gpio);
|
||||
}
|
||||
|
||||
static const struct of_device_id rpi_exp_gpio_ids[] = {
|
||||
{ .compatible = "raspberrypi,firmware-gpio" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, rpi_exp_gpio_ids);
|
||||
|
||||
static struct platform_driver rpi_exp_gpio_driver = {
|
||||
.driver = {
|
||||
.name = MODULE_NAME,
|
||||
.of_match_table = of_match_ptr(rpi_exp_gpio_ids),
|
||||
},
|
||||
.probe = rpi_exp_gpio_probe,
|
||||
};
|
||||
module_platform_driver(rpi_exp_gpio_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Dave Stevenson <[email protected]>");
|
||||
MODULE_DESCRIPTION("Raspberry Pi 3 expander GPIO driver");
|
||||
MODULE_ALIAS("platform:rpi-exp-gpio");
|
||||
|
|
@ -30,6 +30,16 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct gpio_rcar_bank_info {
|
||||
u32 iointsel;
|
||||
u32 inoutsel;
|
||||
u32 outdt;
|
||||
u32 posneg;
|
||||
u32 edglevel;
|
||||
u32 bothedge;
|
||||
u32 intmsk;
|
||||
};
|
||||
|
||||
struct gpio_rcar_priv {
|
||||
void __iomem *base;
|
||||
spinlock_t lock;
|
||||
|
|
@ -39,6 +49,7 @@ struct gpio_rcar_priv {
|
|||
unsigned int irq_parent;
|
||||
atomic_t wakeup_path;
|
||||
bool has_both_edge_trigger;
|
||||
struct gpio_rcar_bank_info bank_info;
|
||||
};
|
||||
|
||||
#define IOINTSEL 0x00 /* General IO/Interrupt Switching Register */
|
||||
|
|
@ -512,17 +523,62 @@ static int gpio_rcar_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused gpio_rcar_suspend(struct device *dev)
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int gpio_rcar_suspend(struct device *dev)
|
||||
{
|
||||
struct gpio_rcar_priv *p = dev_get_drvdata(dev);
|
||||
|
||||
p->bank_info.iointsel = gpio_rcar_read(p, IOINTSEL);
|
||||
p->bank_info.inoutsel = gpio_rcar_read(p, INOUTSEL);
|
||||
p->bank_info.outdt = gpio_rcar_read(p, OUTDT);
|
||||
p->bank_info.intmsk = gpio_rcar_read(p, INTMSK);
|
||||
p->bank_info.posneg = gpio_rcar_read(p, POSNEG);
|
||||
p->bank_info.edglevel = gpio_rcar_read(p, EDGLEVEL);
|
||||
if (p->has_both_edge_trigger)
|
||||
p->bank_info.bothedge = gpio_rcar_read(p, BOTHEDGE);
|
||||
|
||||
if (atomic_read(&p->wakeup_path))
|
||||
device_set_wakeup_path(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, NULL);
|
||||
static int gpio_rcar_resume(struct device *dev)
|
||||
{
|
||||
struct gpio_rcar_priv *p = dev_get_drvdata(dev);
|
||||
unsigned int offset;
|
||||
u32 mask;
|
||||
|
||||
for (offset = 0; offset < p->gpio_chip.ngpio; offset++) {
|
||||
mask = BIT(offset);
|
||||
/* I/O pin */
|
||||
if (!(p->bank_info.iointsel & mask)) {
|
||||
if (p->bank_info.inoutsel & mask)
|
||||
gpio_rcar_direction_output(
|
||||
&p->gpio_chip, offset,
|
||||
!!(p->bank_info.outdt & mask));
|
||||
else
|
||||
gpio_rcar_direction_input(&p->gpio_chip,
|
||||
offset);
|
||||
} else {
|
||||
/* Interrupt pin */
|
||||
gpio_rcar_config_interrupt_input_mode(
|
||||
p,
|
||||
offset,
|
||||
!(p->bank_info.posneg & mask),
|
||||
!(p->bank_info.edglevel & mask),
|
||||
!!(p->bank_info.bothedge & mask));
|
||||
|
||||
if (p->bank_info.intmsk & mask)
|
||||
gpio_rcar_write(p, MSKCLR, mask);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP*/
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(gpio_rcar_pm_ops, gpio_rcar_suspend, gpio_rcar_resume);
|
||||
|
||||
static struct platform_driver gpio_rcar_device_driver = {
|
||||
.probe = gpio_rcar_probe,
|
||||
|
|
|
|||
290
drivers/gpio/gpio-sprd.c
Normal file
290
drivers/gpio/gpio-sprd.c
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018 Spreadtrum Communications Inc.
|
||||
* Copyright (C) 2018 Linaro Ltd.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/* GPIO registers definition */
|
||||
#define SPRD_GPIO_DATA 0x0
|
||||
#define SPRD_GPIO_DMSK 0x4
|
||||
#define SPRD_GPIO_DIR 0x8
|
||||
#define SPRD_GPIO_IS 0xc
|
||||
#define SPRD_GPIO_IBE 0x10
|
||||
#define SPRD_GPIO_IEV 0x14
|
||||
#define SPRD_GPIO_IE 0x18
|
||||
#define SPRD_GPIO_RIS 0x1c
|
||||
#define SPRD_GPIO_MIS 0x20
|
||||
#define SPRD_GPIO_IC 0x24
|
||||
#define SPRD_GPIO_INEN 0x28
|
||||
|
||||
/* We have 16 banks GPIOs and each bank contain 16 GPIOs */
|
||||
#define SPRD_GPIO_BANK_NR 16
|
||||
#define SPRD_GPIO_NR 256
|
||||
#define SPRD_GPIO_BANK_SIZE 0x80
|
||||
#define SPRD_GPIO_BANK_MASK GENMASK(15, 0)
|
||||
#define SPRD_GPIO_BIT(x) ((x) & (SPRD_GPIO_BANK_NR - 1))
|
||||
|
||||
struct sprd_gpio {
|
||||
struct gpio_chip chip;
|
||||
void __iomem *base;
|
||||
spinlock_t lock;
|
||||
int irq;
|
||||
};
|
||||
|
||||
static inline void __iomem *sprd_gpio_bank_base(struct sprd_gpio *sprd_gpio,
|
||||
unsigned int bank)
|
||||
{
|
||||
return sprd_gpio->base + SPRD_GPIO_BANK_SIZE * bank;
|
||||
}
|
||||
|
||||
static void sprd_gpio_update(struct gpio_chip *chip, unsigned int offset,
|
||||
u16 reg, int val)
|
||||
{
|
||||
struct sprd_gpio *sprd_gpio = gpiochip_get_data(chip);
|
||||
void __iomem *base = sprd_gpio_bank_base(sprd_gpio,
|
||||
offset / SPRD_GPIO_BANK_NR);
|
||||
unsigned long flags;
|
||||
u32 tmp;
|
||||
|
||||
spin_lock_irqsave(&sprd_gpio->lock, flags);
|
||||
tmp = readl_relaxed(base + reg);
|
||||
|
||||
if (val)
|
||||
tmp |= BIT(SPRD_GPIO_BIT(offset));
|
||||
else
|
||||
tmp &= ~BIT(SPRD_GPIO_BIT(offset));
|
||||
|
||||
writel_relaxed(tmp, base + reg);
|
||||
spin_unlock_irqrestore(&sprd_gpio->lock, flags);
|
||||
}
|
||||
|
||||
static int sprd_gpio_read(struct gpio_chip *chip, unsigned int offset, u16 reg)
|
||||
{
|
||||
struct sprd_gpio *sprd_gpio = gpiochip_get_data(chip);
|
||||
void __iomem *base = sprd_gpio_bank_base(sprd_gpio,
|
||||
offset / SPRD_GPIO_BANK_NR);
|
||||
|
||||
return !!(readl_relaxed(base + reg) & BIT(SPRD_GPIO_BIT(offset)));
|
||||
}
|
||||
|
||||
static int sprd_gpio_request(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_DMSK, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_gpio_free(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_DMSK, 0);
|
||||
}
|
||||
|
||||
static int sprd_gpio_direction_input(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_DIR, 0);
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_INEN, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned int offset, int value)
|
||||
{
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_DIR, 1);
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_INEN, 0);
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_DATA, value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
return sprd_gpio_read(chip, offset, SPRD_GPIO_DATA);
|
||||
}
|
||||
|
||||
static void sprd_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int value)
|
||||
{
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_DATA, value);
|
||||
}
|
||||
|
||||
static void sprd_gpio_irq_mask(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
u32 offset = irqd_to_hwirq(data);
|
||||
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IE, 0);
|
||||
}
|
||||
|
||||
static void sprd_gpio_irq_ack(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
u32 offset = irqd_to_hwirq(data);
|
||||
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IC, 1);
|
||||
}
|
||||
|
||||
static void sprd_gpio_irq_unmask(struct irq_data *data)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
u32 offset = irqd_to_hwirq(data);
|
||||
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IE, 1);
|
||||
}
|
||||
|
||||
static int sprd_gpio_irq_set_type(struct irq_data *data,
|
||||
unsigned int flow_type)
|
||||
{
|
||||
struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
|
||||
u32 offset = irqd_to_hwirq(data);
|
||||
|
||||
switch (flow_type) {
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 0);
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 1);
|
||||
irq_set_handler_locked(data, handle_edge_irq);
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 0);
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 0);
|
||||
irq_set_handler_locked(data, handle_edge_irq);
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 0);
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 1);
|
||||
irq_set_handler_locked(data, handle_edge_irq);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 1);
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 1);
|
||||
irq_set_handler_locked(data, handle_level_irq);
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IS, 1);
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IBE, 0);
|
||||
sprd_gpio_update(chip, offset, SPRD_GPIO_IEV, 0);
|
||||
irq_set_handler_locked(data, handle_level_irq);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_gpio_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
struct gpio_chip *chip = irq_desc_get_handler_data(desc);
|
||||
struct irq_chip *ic = irq_desc_get_chip(desc);
|
||||
struct sprd_gpio *sprd_gpio = gpiochip_get_data(chip);
|
||||
u32 bank, n, girq;
|
||||
|
||||
chained_irq_enter(ic, desc);
|
||||
|
||||
for (bank = 0; bank * SPRD_GPIO_BANK_NR < chip->ngpio; bank++) {
|
||||
void __iomem *base = sprd_gpio_bank_base(sprd_gpio, bank);
|
||||
unsigned long reg = readl_relaxed(base + SPRD_GPIO_MIS) &
|
||||
SPRD_GPIO_BANK_MASK;
|
||||
|
||||
for_each_set_bit(n, ®, SPRD_GPIO_BANK_NR) {
|
||||
girq = irq_find_mapping(chip->irq.domain,
|
||||
bank * SPRD_GPIO_BANK_NR + n);
|
||||
|
||||
generic_handle_irq(girq);
|
||||
}
|
||||
|
||||
}
|
||||
chained_irq_exit(ic, desc);
|
||||
}
|
||||
|
||||
static struct irq_chip sprd_gpio_irqchip = {
|
||||
.name = "sprd-gpio",
|
||||
.irq_ack = sprd_gpio_irq_ack,
|
||||
.irq_mask = sprd_gpio_irq_mask,
|
||||
.irq_unmask = sprd_gpio_irq_unmask,
|
||||
.irq_set_type = sprd_gpio_irq_set_type,
|
||||
.flags = IRQCHIP_SKIP_SET_WAKE,
|
||||
};
|
||||
|
||||
static int sprd_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct gpio_irq_chip *irq;
|
||||
struct sprd_gpio *sprd_gpio;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
sprd_gpio = devm_kzalloc(&pdev->dev, sizeof(*sprd_gpio), GFP_KERNEL);
|
||||
if (!sprd_gpio)
|
||||
return -ENOMEM;
|
||||
|
||||
sprd_gpio->irq = platform_get_irq(pdev, 0);
|
||||
if (sprd_gpio->irq < 0) {
|
||||
dev_err(&pdev->dev, "Failed to get GPIO interrupt.\n");
|
||||
return sprd_gpio->irq;
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
sprd_gpio->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(sprd_gpio->base))
|
||||
return PTR_ERR(sprd_gpio->base);
|
||||
|
||||
spin_lock_init(&sprd_gpio->lock);
|
||||
|
||||
sprd_gpio->chip.label = dev_name(&pdev->dev);
|
||||
sprd_gpio->chip.ngpio = SPRD_GPIO_NR;
|
||||
sprd_gpio->chip.base = -1;
|
||||
sprd_gpio->chip.parent = &pdev->dev;
|
||||
sprd_gpio->chip.of_node = pdev->dev.of_node;
|
||||
sprd_gpio->chip.request = sprd_gpio_request;
|
||||
sprd_gpio->chip.free = sprd_gpio_free;
|
||||
sprd_gpio->chip.get = sprd_gpio_get;
|
||||
sprd_gpio->chip.set = sprd_gpio_set;
|
||||
sprd_gpio->chip.direction_input = sprd_gpio_direction_input;
|
||||
sprd_gpio->chip.direction_output = sprd_gpio_direction_output;
|
||||
|
||||
irq = &sprd_gpio->chip.irq;
|
||||
irq->chip = &sprd_gpio_irqchip;
|
||||
irq->handler = handle_bad_irq;
|
||||
irq->default_type = IRQ_TYPE_NONE;
|
||||
irq->parent_handler = sprd_gpio_irq_handler;
|
||||
irq->parent_handler_data = sprd_gpio;
|
||||
irq->num_parents = 1;
|
||||
irq->parents = &sprd_gpio->irq;
|
||||
|
||||
ret = devm_gpiochip_add_data(&pdev->dev, &sprd_gpio->chip, sprd_gpio);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Could not register gpiochip %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, sprd_gpio);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sprd_gpio_of_match[] = {
|
||||
{ .compatible = "sprd,sc9860-gpio", },
|
||||
{ /* end of list */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sprd_gpio_of_match);
|
||||
|
||||
static struct platform_driver sprd_gpio_driver = {
|
||||
.probe = sprd_gpio_probe,
|
||||
.driver = {
|
||||
.name = "sprd-gpio",
|
||||
.of_match_table = sprd_gpio_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver_probe(sprd_gpio_driver, sprd_gpio_probe);
|
||||
|
||||
MODULE_DESCRIPTION("Spreadtrum GPIO driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -506,7 +506,7 @@ static int tegra_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
|
|||
#include <linux/debugfs.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
static int dbg_gpio_show(struct seq_file *s, void *unused)
|
||||
static int tegra_dbg_gpio_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct tegra_gpio_info *tgi = s->private;
|
||||
unsigned int i, j;
|
||||
|
|
@ -530,22 +530,12 @@ static int dbg_gpio_show(struct seq_file *s, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dbg_gpio_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, dbg_gpio_show, inode->i_private);
|
||||
}
|
||||
|
||||
static const struct file_operations debug_fops = {
|
||||
.open = dbg_gpio_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
DEFINE_SHOW_ATTRIBUTE(tegra_dbg_gpio);
|
||||
|
||||
static void tegra_gpio_debuginit(struct tegra_gpio_info *tgi)
|
||||
{
|
||||
(void) debugfs_create_file("tegra_gpio", 0444,
|
||||
NULL, tgi, &debug_fops);
|
||||
NULL, tgi, &tegra_dbg_gpio_fops);
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -238,11 +238,10 @@ static int timbgpio_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
tgpio = devm_kzalloc(dev, sizeof(struct timbgpio), GFP_KERNEL);
|
||||
if (!tgpio) {
|
||||
dev_err(dev, "Memory alloc failed\n");
|
||||
tgpio = devm_kzalloc(dev, sizeof(*tgpio), GFP_KERNEL);
|
||||
if (!tgpio)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tgpio->irq_base = pdata->irq_base;
|
||||
|
||||
spin_lock_init(&tgpio->lock);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* GPIO driver for TPS68470 PMIC
|
||||
*
|
||||
|
|
@ -8,15 +9,6 @@
|
|||
* Tianshu Qiu <tian.shu.qiu@intel.com>
|
||||
* Jian Xu Zheng <jian.xu.zheng@intel.com>
|
||||
* Yuning Pu <yuning.pu@intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
|
||||
* kind, whether express or implied; without even the implied warranty
|
||||
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
|
|
|
|||
|
|
@ -1,231 +0,0 @@
|
|||
/*
|
||||
* Toumaz Xenif TZ1090 PDC GPIO handling.
|
||||
*
|
||||
* Copyright (C) 2012-2013 Imagination Technologies Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <asm/global_lock.h>
|
||||
|
||||
/* Register offsets from SOC_GPIO_CONTROL0 */
|
||||
#define REG_SOC_GPIO_CONTROL0 0x00
|
||||
#define REG_SOC_GPIO_CONTROL1 0x04
|
||||
#define REG_SOC_GPIO_CONTROL2 0x08
|
||||
#define REG_SOC_GPIO_CONTROL3 0x0c
|
||||
#define REG_SOC_GPIO_STATUS 0x80
|
||||
|
||||
/* PDC GPIOs go after normal GPIOs */
|
||||
#define GPIO_PDC_BASE 90
|
||||
#define GPIO_PDC_NGPIO 7
|
||||
|
||||
/* Out of PDC gpios, only syswakes have irqs */
|
||||
#define GPIO_PDC_IRQ_FIRST 2
|
||||
#define GPIO_PDC_NIRQ 3
|
||||
|
||||
/**
|
||||
* struct tz1090_pdc_gpio - GPIO bank private data
|
||||
* @chip: Generic GPIO chip for GPIO bank
|
||||
* @reg: Base of registers, offset for this GPIO bank
|
||||
* @irq: IRQ numbers for Syswake GPIOs
|
||||
*
|
||||
* This is the main private data for the PDC GPIO driver. It encapsulates a
|
||||
* gpio_chip, and the callbacks for the gpio_chip can access the private data
|
||||
* with the to_pdc() macro below.
|
||||
*/
|
||||
struct tz1090_pdc_gpio {
|
||||
struct gpio_chip chip;
|
||||
void __iomem *reg;
|
||||
int irq[GPIO_PDC_NIRQ];
|
||||
};
|
||||
|
||||
/* Register accesses into the PDC MMIO area */
|
||||
|
||||
static inline void pdc_write(struct tz1090_pdc_gpio *priv, unsigned int reg_offs,
|
||||
unsigned int data)
|
||||
{
|
||||
writel(data, priv->reg + reg_offs);
|
||||
}
|
||||
|
||||
static inline unsigned int pdc_read(struct tz1090_pdc_gpio *priv,
|
||||
unsigned int reg_offs)
|
||||
{
|
||||
return readl(priv->reg + reg_offs);
|
||||
}
|
||||
|
||||
/* Generic GPIO interface */
|
||||
|
||||
static int tz1090_pdc_gpio_direction_input(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip);
|
||||
u32 value;
|
||||
int lstat;
|
||||
|
||||
__global_lock2(lstat);
|
||||
value = pdc_read(priv, REG_SOC_GPIO_CONTROL1);
|
||||
value |= BIT(offset);
|
||||
pdc_write(priv, REG_SOC_GPIO_CONTROL1, value);
|
||||
__global_unlock2(lstat);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tz1090_pdc_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned int offset,
|
||||
int output_value)
|
||||
{
|
||||
struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip);
|
||||
u32 value;
|
||||
int lstat;
|
||||
|
||||
__global_lock2(lstat);
|
||||
/* EXT_POWER doesn't seem to have an output value bit */
|
||||
if (offset < 6) {
|
||||
value = pdc_read(priv, REG_SOC_GPIO_CONTROL0);
|
||||
if (output_value)
|
||||
value |= BIT(offset);
|
||||
else
|
||||
value &= ~BIT(offset);
|
||||
pdc_write(priv, REG_SOC_GPIO_CONTROL0, value);
|
||||
}
|
||||
|
||||
value = pdc_read(priv, REG_SOC_GPIO_CONTROL1);
|
||||
value &= ~BIT(offset);
|
||||
pdc_write(priv, REG_SOC_GPIO_CONTROL1, value);
|
||||
__global_unlock2(lstat);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tz1090_pdc_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip);
|
||||
return !!(pdc_read(priv, REG_SOC_GPIO_STATUS) & BIT(offset));
|
||||
}
|
||||
|
||||
static void tz1090_pdc_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int output_value)
|
||||
{
|
||||
struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip);
|
||||
u32 value;
|
||||
int lstat;
|
||||
|
||||
/* EXT_POWER doesn't seem to have an output value bit */
|
||||
if (offset >= 6)
|
||||
return;
|
||||
|
||||
__global_lock2(lstat);
|
||||
value = pdc_read(priv, REG_SOC_GPIO_CONTROL0);
|
||||
if (output_value)
|
||||
value |= BIT(offset);
|
||||
else
|
||||
value &= ~BIT(offset);
|
||||
pdc_write(priv, REG_SOC_GPIO_CONTROL0, value);
|
||||
__global_unlock2(lstat);
|
||||
}
|
||||
|
||||
static int tz1090_pdc_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct tz1090_pdc_gpio *priv = gpiochip_get_data(chip);
|
||||
unsigned int syswake = offset - GPIO_PDC_IRQ_FIRST;
|
||||
int irq;
|
||||
|
||||
/* only syswakes have irqs */
|
||||
if (syswake >= GPIO_PDC_NIRQ)
|
||||
return -EINVAL;
|
||||
|
||||
irq = priv->irq[syswake];
|
||||
if (!irq)
|
||||
return -EINVAL;
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
static int tz1090_pdc_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct resource *res_regs;
|
||||
struct tz1090_pdc_gpio *priv;
|
||||
unsigned int i;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "must be instantiated via devicetree\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res_regs) {
|
||||
dev_err(&pdev->dev, "cannot find registers resource\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev, "unable to allocate driver data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Ioremap the registers */
|
||||
priv->reg = devm_ioremap(&pdev->dev, res_regs->start,
|
||||
resource_size(res_regs));
|
||||
if (!priv->reg) {
|
||||
dev_err(&pdev->dev, "unable to ioremap registers\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Set up GPIO chip */
|
||||
priv->chip.label = "tz1090-pdc-gpio";
|
||||
priv->chip.parent = &pdev->dev;
|
||||
priv->chip.direction_input = tz1090_pdc_gpio_direction_input;
|
||||
priv->chip.direction_output = tz1090_pdc_gpio_direction_output;
|
||||
priv->chip.get = tz1090_pdc_gpio_get;
|
||||
priv->chip.set = tz1090_pdc_gpio_set;
|
||||
priv->chip.free = gpiochip_generic_free;
|
||||
priv->chip.request = gpiochip_generic_request;
|
||||
priv->chip.to_irq = tz1090_pdc_gpio_to_irq;
|
||||
priv->chip.of_node = np;
|
||||
|
||||
/* GPIO numbering */
|
||||
priv->chip.base = GPIO_PDC_BASE;
|
||||
priv->chip.ngpio = GPIO_PDC_NGPIO;
|
||||
|
||||
/* Map the syswake irqs */
|
||||
for (i = 0; i < GPIO_PDC_NIRQ; ++i)
|
||||
priv->irq[i] = irq_of_parse_and_map(np, i);
|
||||
|
||||
/* Add the GPIO bank */
|
||||
gpiochip_add_data(&priv->chip, priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id tz1090_pdc_gpio_of_match[] = {
|
||||
{ .compatible = "img,tz1090-pdc-gpio" },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver tz1090_pdc_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "tz1090-pdc-gpio",
|
||||
.of_match_table = tz1090_pdc_gpio_of_match,
|
||||
},
|
||||
.probe = tz1090_pdc_gpio_probe,
|
||||
};
|
||||
|
||||
static int __init tz1090_pdc_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&tz1090_pdc_gpio_driver);
|
||||
}
|
||||
subsys_initcall(tz1090_pdc_gpio_init);
|
||||
|
|
@ -1,602 +0,0 @@
|
|||
/*
|
||||
* Toumaz Xenif TZ1090 GPIO handling.
|
||||
*
|
||||
* Copyright (C) 2008-2013 Imagination Technologies Ltd.
|
||||
*
|
||||
* Based on ARM PXA code and others.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/irqdomain.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/syscore_ops.h>
|
||||
#include <asm/global_lock.h>
|
||||
|
||||
/* Register offsets from bank base address */
|
||||
#define REG_GPIO_DIR 0x00
|
||||
#define REG_GPIO_IRQ_PLRT 0x20
|
||||
#define REG_GPIO_IRQ_TYPE 0x30
|
||||
#define REG_GPIO_IRQ_EN 0x40
|
||||
#define REG_GPIO_IRQ_STS 0x50
|
||||
#define REG_GPIO_BIT_EN 0x60
|
||||
#define REG_GPIO_DIN 0x70
|
||||
#define REG_GPIO_DOUT 0x80
|
||||
|
||||
/* REG_GPIO_IRQ_PLRT */
|
||||
#define REG_GPIO_IRQ_PLRT_LOW 0
|
||||
#define REG_GPIO_IRQ_PLRT_HIGH 1
|
||||
|
||||
/* REG_GPIO_IRQ_TYPE */
|
||||
#define REG_GPIO_IRQ_TYPE_LEVEL 0
|
||||
#define REG_GPIO_IRQ_TYPE_EDGE 1
|
||||
|
||||
/**
|
||||
* struct tz1090_gpio_bank - GPIO bank private data
|
||||
* @chip: Generic GPIO chip for GPIO bank
|
||||
* @domain: IRQ domain for GPIO bank (may be NULL)
|
||||
* @reg: Base of registers, offset for this GPIO bank
|
||||
* @irq: IRQ number for GPIO bank
|
||||
* @label: Debug GPIO bank label, used for storage of chip->label
|
||||
*
|
||||
* This is the main private data for a GPIO bank. It encapsulates a gpio_chip,
|
||||
* and the callbacks for the gpio_chip can access the private data with the
|
||||
* to_bank() macro below.
|
||||
*/
|
||||
struct tz1090_gpio_bank {
|
||||
struct gpio_chip chip;
|
||||
struct irq_domain *domain;
|
||||
void __iomem *reg;
|
||||
int irq;
|
||||
char label[16];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tz1090_gpio - Overall GPIO device private data
|
||||
* @dev: Device (from platform device)
|
||||
* @reg: Base of GPIO registers
|
||||
*
|
||||
* Represents the overall GPIO device. This structure is actually only
|
||||
* temporary, and used during init.
|
||||
*/
|
||||
struct tz1090_gpio {
|
||||
struct device *dev;
|
||||
void __iomem *reg;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tz1090_gpio_bank_info - Temporary registration info for GPIO bank
|
||||
* @priv: Overall GPIO device private data
|
||||
* @node: Device tree node specific to this GPIO bank
|
||||
* @index: Index of bank in range 0-2
|
||||
*/
|
||||
struct tz1090_gpio_bank_info {
|
||||
struct tz1090_gpio *priv;
|
||||
struct device_node *node;
|
||||
unsigned int index;
|
||||
};
|
||||
|
||||
/* Convenience register accessors */
|
||||
static inline void tz1090_gpio_write(struct tz1090_gpio_bank *bank,
|
||||
unsigned int reg_offs, u32 data)
|
||||
{
|
||||
iowrite32(data, bank->reg + reg_offs);
|
||||
}
|
||||
|
||||
static inline u32 tz1090_gpio_read(struct tz1090_gpio_bank *bank,
|
||||
unsigned int reg_offs)
|
||||
{
|
||||
return ioread32(bank->reg + reg_offs);
|
||||
}
|
||||
|
||||
/* caller must hold LOCK2 */
|
||||
static inline void _tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
|
||||
unsigned int reg_offs,
|
||||
unsigned int offset)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = tz1090_gpio_read(bank, reg_offs);
|
||||
value &= ~BIT(offset);
|
||||
tz1090_gpio_write(bank, reg_offs, value);
|
||||
}
|
||||
|
||||
static void tz1090_gpio_clear_bit(struct tz1090_gpio_bank *bank,
|
||||
unsigned int reg_offs,
|
||||
unsigned int offset)
|
||||
{
|
||||
int lstat;
|
||||
|
||||
__global_lock2(lstat);
|
||||
_tz1090_gpio_clear_bit(bank, reg_offs, offset);
|
||||
__global_unlock2(lstat);
|
||||
}
|
||||
|
||||
/* caller must hold LOCK2 */
|
||||
static inline void _tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
|
||||
unsigned int reg_offs,
|
||||
unsigned int offset)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = tz1090_gpio_read(bank, reg_offs);
|
||||
value |= BIT(offset);
|
||||
tz1090_gpio_write(bank, reg_offs, value);
|
||||
}
|
||||
|
||||
static void tz1090_gpio_set_bit(struct tz1090_gpio_bank *bank,
|
||||
unsigned int reg_offs,
|
||||
unsigned int offset)
|
||||
{
|
||||
int lstat;
|
||||
|
||||
__global_lock2(lstat);
|
||||
_tz1090_gpio_set_bit(bank, reg_offs, offset);
|
||||
__global_unlock2(lstat);
|
||||
}
|
||||
|
||||
/* caller must hold LOCK2 */
|
||||
static inline void _tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
|
||||
unsigned int reg_offs,
|
||||
unsigned int offset,
|
||||
bool val)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
value = tz1090_gpio_read(bank, reg_offs);
|
||||
value &= ~BIT(offset);
|
||||
if (val)
|
||||
value |= BIT(offset);
|
||||
tz1090_gpio_write(bank, reg_offs, value);
|
||||
}
|
||||
|
||||
static void tz1090_gpio_mod_bit(struct tz1090_gpio_bank *bank,
|
||||
unsigned int reg_offs,
|
||||
unsigned int offset,
|
||||
bool val)
|
||||
{
|
||||
int lstat;
|
||||
|
||||
__global_lock2(lstat);
|
||||
_tz1090_gpio_mod_bit(bank, reg_offs, offset, val);
|
||||
__global_unlock2(lstat);
|
||||
}
|
||||
|
||||
static inline int tz1090_gpio_read_bit(struct tz1090_gpio_bank *bank,
|
||||
unsigned int reg_offs,
|
||||
unsigned int offset)
|
||||
{
|
||||
return tz1090_gpio_read(bank, reg_offs) & BIT(offset);
|
||||
}
|
||||
|
||||
/* GPIO chip callbacks */
|
||||
|
||||
static int tz1090_gpio_direction_input(struct gpio_chip *chip,
|
||||
unsigned int offset)
|
||||
{
|
||||
struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
|
||||
tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tz1090_gpio_direction_output(struct gpio_chip *chip,
|
||||
unsigned int offset, int output_value)
|
||||
{
|
||||
struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
|
||||
int lstat;
|
||||
|
||||
__global_lock2(lstat);
|
||||
_tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
|
||||
_tz1090_gpio_clear_bit(bank, REG_GPIO_DIR, offset);
|
||||
__global_unlock2(lstat);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return GPIO level
|
||||
*/
|
||||
static int tz1090_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
|
||||
|
||||
return !!tz1090_gpio_read_bit(bank, REG_GPIO_DIN, offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set output GPIO level
|
||||
*/
|
||||
static void tz1090_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
||||
int output_value)
|
||||
{
|
||||
struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
|
||||
|
||||
tz1090_gpio_mod_bit(bank, REG_GPIO_DOUT, offset, output_value);
|
||||
}
|
||||
|
||||
static int tz1090_gpio_request(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
|
||||
int ret;
|
||||
|
||||
ret = pinctrl_gpio_request(chip->base + offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
tz1090_gpio_set_bit(bank, REG_GPIO_DIR, offset);
|
||||
tz1090_gpio_set_bit(bank, REG_GPIO_BIT_EN, offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tz1090_gpio_free(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
|
||||
|
||||
pinctrl_gpio_free(chip->base + offset);
|
||||
|
||||
tz1090_gpio_clear_bit(bank, REG_GPIO_BIT_EN, offset);
|
||||
}
|
||||
|
||||
static int tz1090_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
|
||||
{
|
||||
struct tz1090_gpio_bank *bank = gpiochip_get_data(chip);
|
||||
|
||||
if (!bank->domain)
|
||||
return -EINVAL;
|
||||
|
||||
return irq_create_mapping(bank->domain, offset);
|
||||
}
|
||||
|
||||
/* IRQ chip handlers */
|
||||
|
||||
/* Get TZ1090 GPIO chip from irq data provided to generic IRQ callbacks */
|
||||
static inline struct tz1090_gpio_bank *irqd_to_gpio_bank(struct irq_data *data)
|
||||
{
|
||||
return (struct tz1090_gpio_bank *)data->domain->host_data;
|
||||
}
|
||||
|
||||
static void tz1090_gpio_irq_polarity(struct tz1090_gpio_bank *bank,
|
||||
unsigned int offset, unsigned int polarity)
|
||||
{
|
||||
tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_PLRT, offset, polarity);
|
||||
}
|
||||
|
||||
static void tz1090_gpio_irq_type(struct tz1090_gpio_bank *bank,
|
||||
unsigned int offset, unsigned int type)
|
||||
{
|
||||
tz1090_gpio_mod_bit(bank, REG_GPIO_IRQ_TYPE, offset, type);
|
||||
}
|
||||
|
||||
/* set polarity to trigger on next edge, whether rising or falling */
|
||||
static void tz1090_gpio_irq_next_edge(struct tz1090_gpio_bank *bank,
|
||||
unsigned int offset)
|
||||
{
|
||||
unsigned int value_p, value_i;
|
||||
int lstat;
|
||||
|
||||
/*
|
||||
* Set the GPIO's interrupt polarity to the opposite of the current
|
||||
* input value so that the next edge triggers an interrupt.
|
||||
*/
|
||||
__global_lock2(lstat);
|
||||
value_i = ~tz1090_gpio_read(bank, REG_GPIO_DIN);
|
||||
value_p = tz1090_gpio_read(bank, REG_GPIO_IRQ_PLRT);
|
||||
value_p &= ~BIT(offset);
|
||||
value_p |= value_i & BIT(offset);
|
||||
tz1090_gpio_write(bank, REG_GPIO_IRQ_PLRT, value_p);
|
||||
__global_unlock2(lstat);
|
||||
}
|
||||
|
||||
static unsigned int gpio_startup_irq(struct irq_data *data)
|
||||
{
|
||||
/*
|
||||
* This warning indicates that the type of the irq hasn't been set
|
||||
* before enabling the irq. This would normally be done by passing some
|
||||
* trigger flags to request_irq().
|
||||
*/
|
||||
WARN(irqd_get_trigger_type(data) == IRQ_TYPE_NONE,
|
||||
"irq type not set before enabling gpio irq %d", data->irq);
|
||||
|
||||
irq_gc_ack_clr_bit(data);
|
||||
irq_gc_mask_set_bit(data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_set_irq_type(struct irq_data *data, unsigned int flow_type)
|
||||
{
|
||||
struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
|
||||
unsigned int type;
|
||||
unsigned int polarity;
|
||||
|
||||
switch (flow_type) {
|
||||
case IRQ_TYPE_EDGE_BOTH:
|
||||
type = REG_GPIO_IRQ_TYPE_EDGE;
|
||||
polarity = REG_GPIO_IRQ_PLRT_LOW;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_RISING:
|
||||
type = REG_GPIO_IRQ_TYPE_EDGE;
|
||||
polarity = REG_GPIO_IRQ_PLRT_HIGH;
|
||||
break;
|
||||
case IRQ_TYPE_EDGE_FALLING:
|
||||
type = REG_GPIO_IRQ_TYPE_EDGE;
|
||||
polarity = REG_GPIO_IRQ_PLRT_LOW;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_HIGH:
|
||||
type = REG_GPIO_IRQ_TYPE_LEVEL;
|
||||
polarity = REG_GPIO_IRQ_PLRT_HIGH;
|
||||
break;
|
||||
case IRQ_TYPE_LEVEL_LOW:
|
||||
type = REG_GPIO_IRQ_TYPE_LEVEL;
|
||||
polarity = REG_GPIO_IRQ_PLRT_LOW;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tz1090_gpio_irq_type(bank, data->hwirq, type);
|
||||
irq_setup_alt_chip(data, flow_type);
|
||||
|
||||
if (flow_type == IRQ_TYPE_EDGE_BOTH)
|
||||
tz1090_gpio_irq_next_edge(bank, data->hwirq);
|
||||
else
|
||||
tz1090_gpio_irq_polarity(bank, data->hwirq, polarity);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
static int gpio_set_irq_wake(struct irq_data *data, unsigned int on)
|
||||
{
|
||||
struct tz1090_gpio_bank *bank = irqd_to_gpio_bank(data);
|
||||
|
||||
#ifdef CONFIG_PM_DEBUG
|
||||
pr_info("irq_wake irq%d state:%d\n", data->irq, on);
|
||||
#endif
|
||||
|
||||
/* wake on gpio block interrupt */
|
||||
return irq_set_irq_wake(bank->irq, on);
|
||||
}
|
||||
#else
|
||||
#define gpio_set_irq_wake NULL
|
||||
#endif
|
||||
|
||||
static void tz1090_gpio_irq_handler(struct irq_desc *desc)
|
||||
{
|
||||
irq_hw_number_t hw;
|
||||
unsigned int irq_stat, irq_no;
|
||||
struct tz1090_gpio_bank *bank;
|
||||
struct irq_desc *child_desc;
|
||||
|
||||
bank = (struct tz1090_gpio_bank *)irq_desc_get_handler_data(desc);
|
||||
irq_stat = tz1090_gpio_read(bank, REG_GPIO_DIR) &
|
||||
tz1090_gpio_read(bank, REG_GPIO_IRQ_STS) &
|
||||
tz1090_gpio_read(bank, REG_GPIO_IRQ_EN) &
|
||||
0x3FFFFFFF; /* 30 bits only */
|
||||
|
||||
for (hw = 0; irq_stat; irq_stat >>= 1, ++hw) {
|
||||
if (!(irq_stat & 1))
|
||||
continue;
|
||||
|
||||
irq_no = irq_linear_revmap(bank->domain, hw);
|
||||
child_desc = irq_to_desc(irq_no);
|
||||
|
||||
/* Toggle edge for pin with both edges triggering enabled */
|
||||
if (irqd_get_trigger_type(&child_desc->irq_data)
|
||||
== IRQ_TYPE_EDGE_BOTH)
|
||||
tz1090_gpio_irq_next_edge(bank, hw);
|
||||
|
||||
generic_handle_irq_desc(child_desc);
|
||||
}
|
||||
}
|
||||
|
||||
static int tz1090_gpio_bank_probe(struct tz1090_gpio_bank_info *info)
|
||||
{
|
||||
struct device_node *np = info->node;
|
||||
struct device *dev = info->priv->dev;
|
||||
struct tz1090_gpio_bank *bank;
|
||||
struct irq_chip_generic *gc;
|
||||
int err;
|
||||
|
||||
bank = devm_kzalloc(dev, sizeof(*bank), GFP_KERNEL);
|
||||
if (!bank) {
|
||||
dev_err(dev, "unable to allocate driver data\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Offset the main registers to the first register in this bank */
|
||||
bank->reg = info->priv->reg + info->index * 4;
|
||||
|
||||
/* Set up GPIO chip */
|
||||
snprintf(bank->label, sizeof(bank->label), "tz1090-gpio-%u",
|
||||
info->index);
|
||||
bank->chip.label = bank->label;
|
||||
bank->chip.parent = dev;
|
||||
bank->chip.direction_input = tz1090_gpio_direction_input;
|
||||
bank->chip.direction_output = tz1090_gpio_direction_output;
|
||||
bank->chip.get = tz1090_gpio_get;
|
||||
bank->chip.set = tz1090_gpio_set;
|
||||
bank->chip.free = tz1090_gpio_free;
|
||||
bank->chip.request = tz1090_gpio_request;
|
||||
bank->chip.to_irq = tz1090_gpio_to_irq;
|
||||
bank->chip.of_node = np;
|
||||
|
||||
/* GPIO numbering from 0 */
|
||||
bank->chip.base = info->index * 30;
|
||||
bank->chip.ngpio = 30;
|
||||
|
||||
/* Add the GPIO bank */
|
||||
gpiochip_add_data(&bank->chip, bank);
|
||||
|
||||
/* Get the GPIO bank IRQ if provided */
|
||||
bank->irq = irq_of_parse_and_map(np, 0);
|
||||
|
||||
/* The interrupt is optional (it may be used by another core on chip) */
|
||||
if (!bank->irq) {
|
||||
dev_info(dev, "IRQ not provided for bank %u, IRQs disabled\n",
|
||||
info->index);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_info(dev, "Setting up IRQs for GPIO bank %u\n",
|
||||
info->index);
|
||||
|
||||
/*
|
||||
* Initialise all interrupts to disabled so we don't get
|
||||
* spurious ones on a dirty boot and hit the BUG_ON in the
|
||||
* handler.
|
||||
*/
|
||||
tz1090_gpio_write(bank, REG_GPIO_IRQ_EN, 0);
|
||||
|
||||
/* Add a virtual IRQ for each GPIO */
|
||||
bank->domain = irq_domain_add_linear(np,
|
||||
bank->chip.ngpio,
|
||||
&irq_generic_chip_ops,
|
||||
bank);
|
||||
|
||||
/* Set up a generic irq chip with 2 chip types (level and edge) */
|
||||
err = irq_alloc_domain_generic_chips(bank->domain, bank->chip.ngpio, 2,
|
||||
bank->label, handle_bad_irq, 0, 0,
|
||||
IRQ_GC_INIT_NESTED_LOCK);
|
||||
if (err) {
|
||||
dev_info(dev,
|
||||
"irq_alloc_domain_generic_chips failed for bank %u, IRQs disabled\n",
|
||||
info->index);
|
||||
irq_domain_remove(bank->domain);
|
||||
return 0;
|
||||
}
|
||||
|
||||
gc = irq_get_domain_generic_chip(bank->domain, 0);
|
||||
gc->reg_base = bank->reg;
|
||||
|
||||
/* level chip type */
|
||||
gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK;
|
||||
gc->chip_types[0].handler = handle_level_irq;
|
||||
gc->chip_types[0].regs.ack = REG_GPIO_IRQ_STS;
|
||||
gc->chip_types[0].regs.mask = REG_GPIO_IRQ_EN;
|
||||
gc->chip_types[0].chip.irq_startup = gpio_startup_irq;
|
||||
gc->chip_types[0].chip.irq_ack = irq_gc_ack_clr_bit;
|
||||
gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
|
||||
gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
|
||||
gc->chip_types[0].chip.irq_set_type = gpio_set_irq_type;
|
||||
gc->chip_types[0].chip.irq_set_wake = gpio_set_irq_wake;
|
||||
gc->chip_types[0].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
|
||||
|
||||
/* edge chip type */
|
||||
gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
|
||||
gc->chip_types[1].handler = handle_edge_irq;
|
||||
gc->chip_types[1].regs.ack = REG_GPIO_IRQ_STS;
|
||||
gc->chip_types[1].regs.mask = REG_GPIO_IRQ_EN;
|
||||
gc->chip_types[1].chip.irq_startup = gpio_startup_irq;
|
||||
gc->chip_types[1].chip.irq_ack = irq_gc_ack_clr_bit;
|
||||
gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit;
|
||||
gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit;
|
||||
gc->chip_types[1].chip.irq_set_type = gpio_set_irq_type;
|
||||
gc->chip_types[1].chip.irq_set_wake = gpio_set_irq_wake;
|
||||
gc->chip_types[1].chip.flags = IRQCHIP_MASK_ON_SUSPEND;
|
||||
|
||||
/* Setup chained handler for this GPIO bank */
|
||||
irq_set_chained_handler_and_data(bank->irq, tz1090_gpio_irq_handler,
|
||||
bank);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tz1090_gpio_register_banks(struct tz1090_gpio *priv)
|
||||
{
|
||||
struct device_node *np = priv->dev->of_node;
|
||||
struct device_node *node;
|
||||
|
||||
for_each_available_child_of_node(np, node) {
|
||||
struct tz1090_gpio_bank_info info;
|
||||
u32 addr;
|
||||
int ret;
|
||||
|
||||
ret = of_property_read_u32(node, "reg", &addr);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "invalid reg on %pOF\n", node);
|
||||
continue;
|
||||
}
|
||||
if (addr >= 3) {
|
||||
dev_err(priv->dev, "index %u in %pOF out of range\n",
|
||||
addr, node);
|
||||
continue;
|
||||
}
|
||||
|
||||
info.index = addr;
|
||||
info.node = of_node_get(node);
|
||||
info.priv = priv;
|
||||
|
||||
ret = tz1090_gpio_bank_probe(&info);
|
||||
if (ret) {
|
||||
dev_err(priv->dev, "failure registering %pOF\n", node);
|
||||
of_node_put(node);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int tz1090_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct resource *res_regs;
|
||||
struct tz1090_gpio priv;
|
||||
|
||||
if (!np) {
|
||||
dev_err(&pdev->dev, "must be instantiated via devicetree\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res_regs) {
|
||||
dev_err(&pdev->dev, "cannot find registers resource\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
priv.dev = &pdev->dev;
|
||||
|
||||
/* Ioremap the registers */
|
||||
priv.reg = devm_ioremap(&pdev->dev, res_regs->start,
|
||||
resource_size(res_regs));
|
||||
if (!priv.reg) {
|
||||
dev_err(&pdev->dev, "unable to ioremap registers\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Look for banks */
|
||||
tz1090_gpio_register_banks(&priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id tz1090_gpio_of_match[] = {
|
||||
{ .compatible = "img,tz1090-gpio" },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver tz1090_gpio_driver = {
|
||||
.driver = {
|
||||
.name = "tz1090-gpio",
|
||||
.of_match_table = tz1090_gpio_of_match,
|
||||
},
|
||||
.probe = tz1090_gpio_probe,
|
||||
};
|
||||
|
||||
static int __init tz1090_gpio_init(void)
|
||||
{
|
||||
return platform_driver_register(&tz1090_gpio_driver);
|
||||
}
|
||||
subsys_initcall(tz1090_gpio_init);
|
||||
|
|
@ -182,7 +182,7 @@ static void wm831x_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|||
dev_err(wm831x->dev,
|
||||
"GPIO control %d read failed: %d\n",
|
||||
gpio, reg);
|
||||
seq_printf(s, "\n");
|
||||
seq_putc(s, '\n');
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/errno.h>
|
||||
|
|
@ -129,6 +130,51 @@ static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset)
|
|||
return !!(port_state & mask);
|
||||
}
|
||||
|
||||
static int ws16c48_gpio_get_multiple(struct gpio_chip *chip,
|
||||
unsigned long *mask, unsigned long *bits)
|
||||
{
|
||||
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
|
||||
const unsigned int gpio_reg_size = 8;
|
||||
size_t i;
|
||||
const size_t num_ports = chip->ngpio / gpio_reg_size;
|
||||
unsigned int bits_offset;
|
||||
size_t word_index;
|
||||
unsigned int word_offset;
|
||||
unsigned long word_mask;
|
||||
const unsigned long port_mask = GENMASK(gpio_reg_size - 1, 0);
|
||||
unsigned long port_state;
|
||||
|
||||
/* 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 < num_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_state = inb(ws16c48gpio->base + i);
|
||||
|
||||
/* store acquired bits at respective bits array offset */
|
||||
bits[word_index] |= port_state << word_offset;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
|
||||
{
|
||||
struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
|
||||
|
|
@ -383,6 +429,7 @@ static int ws16c48_probe(struct device *dev, unsigned int id)
|
|||
ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input;
|
||||
ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output;
|
||||
ws16c48gpio->chip.get = ws16c48_gpio_get;
|
||||
ws16c48gpio->chip.get_multiple = ws16c48_gpio_get_multiple;
|
||||
ws16c48gpio->chip.set = ws16c48_gpio_set;
|
||||
ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple;
|
||||
ws16c48gpio->base = base[id];
|
||||
|
|
|
|||
|
|
@ -126,11 +126,16 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|||
{
|
||||
int reg;
|
||||
struct xra1403 *xra = gpiochip_get_data(chip);
|
||||
int value[xra1403_regmap_cfg.max_register];
|
||||
int *value;
|
||||
int i;
|
||||
unsigned int gcr;
|
||||
unsigned int gsr;
|
||||
|
||||
value = kmalloc_array(xra1403_regmap_cfg.max_register, sizeof(*value),
|
||||
GFP_KERNEL);
|
||||
if (!value)
|
||||
return;
|
||||
|
||||
seq_puts(s, "xra reg:");
|
||||
for (reg = 0; reg <= xra1403_regmap_cfg.max_register; reg++)
|
||||
seq_printf(s, " %2.2x", reg);
|
||||
|
|
@ -154,6 +159,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
|||
(gcr & BIT(i)) ? "in" : "out",
|
||||
(gsr & BIT(i)) ? "hi" : "lo");
|
||||
}
|
||||
kfree(value);
|
||||
}
|
||||
#else
|
||||
#define xra1403_dbg_show NULL
|
||||
|
|
|
|||
|
|
@ -511,6 +511,28 @@ void of_mm_gpiochip_remove(struct of_mm_gpio_chip *mm_gc)
|
|||
}
|
||||
EXPORT_SYMBOL(of_mm_gpiochip_remove);
|
||||
|
||||
static void of_gpiochip_init_valid_mask(struct gpio_chip *chip)
|
||||
{
|
||||
int len, i;
|
||||
u32 start, count;
|
||||
struct device_node *np = chip->of_node;
|
||||
|
||||
len = of_property_count_u32_elems(np, "gpio-reserved-ranges");
|
||||
if (len < 0 || len % 2 != 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < len; i += 2) {
|
||||
of_property_read_u32_index(np, "gpio-reserved-ranges",
|
||||
i, &start);
|
||||
of_property_read_u32_index(np, "gpio-reserved-ranges",
|
||||
i + 1, &count);
|
||||
if (start >= chip->ngpio || start + count >= chip->ngpio)
|
||||
continue;
|
||||
|
||||
bitmap_clear(chip->valid_mask, start, count);
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PINCTRL
|
||||
static int of_gpiochip_add_pin_range(struct gpio_chip *chip)
|
||||
{
|
||||
|
|
@ -615,6 +637,8 @@ int of_gpiochip_add(struct gpio_chip *chip)
|
|||
if (chip->of_gpio_n_cells > MAX_PHANDLE_ARGS)
|
||||
return -EINVAL;
|
||||
|
||||
of_gpiochip_init_valid_mask(chip);
|
||||
|
||||
status = of_gpiochip_add_pin_range(chip);
|
||||
if (status)
|
||||
return status;
|
||||
|
|
|
|||
|
|
@ -337,6 +337,57 @@ static int gpiochip_set_desc_names(struct gpio_chip *gc)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long *gpiochip_allocate_mask(struct gpio_chip *chip)
|
||||
{
|
||||
unsigned long *p;
|
||||
|
||||
p = kmalloc_array(BITS_TO_LONGS(chip->ngpio), sizeof(*p), GFP_KERNEL);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
/* Assume by default all GPIOs are valid */
|
||||
bitmap_fill(p, chip->ngpio);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static int gpiochip_init_valid_mask(struct gpio_chip *gpiochip)
|
||||
{
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
int size;
|
||||
struct device_node *np = gpiochip->of_node;
|
||||
|
||||
size = of_property_count_u32_elems(np, "gpio-reserved-ranges");
|
||||
if (size > 0 && size % 2 == 0)
|
||||
gpiochip->need_valid_mask = true;
|
||||
#endif
|
||||
|
||||
if (!gpiochip->need_valid_mask)
|
||||
return 0;
|
||||
|
||||
gpiochip->valid_mask = gpiochip_allocate_mask(gpiochip);
|
||||
if (!gpiochip->valid_mask)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gpiochip_free_valid_mask(struct gpio_chip *gpiochip)
|
||||
{
|
||||
kfree(gpiochip->valid_mask);
|
||||
gpiochip->valid_mask = NULL;
|
||||
}
|
||||
|
||||
bool gpiochip_line_is_valid(const struct gpio_chip *gpiochip,
|
||||
unsigned int offset)
|
||||
{
|
||||
/* No mask means all valid */
|
||||
if (likely(!gpiochip->valid_mask))
|
||||
return true;
|
||||
return test_bit(offset, gpiochip->valid_mask);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiochip_line_is_valid);
|
||||
|
||||
/*
|
||||
* GPIO line handle management
|
||||
*/
|
||||
|
|
@ -1261,6 +1312,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
|
|||
if (status)
|
||||
goto err_remove_from_list;
|
||||
|
||||
status = gpiochip_init_valid_mask(chip);
|
||||
if (status)
|
||||
goto err_remove_irqchip_mask;
|
||||
|
||||
status = gpiochip_add_irqchip(chip, lock_key, request_key);
|
||||
if (status)
|
||||
goto err_remove_chip;
|
||||
|
|
@ -1290,6 +1345,8 @@ err_remove_chip:
|
|||
acpi_gpiochip_remove(chip);
|
||||
gpiochip_free_hogs(chip);
|
||||
of_gpiochip_remove(chip);
|
||||
gpiochip_free_valid_mask(chip);
|
||||
err_remove_irqchip_mask:
|
||||
gpiochip_irqchip_free_valid_mask(chip);
|
||||
err_remove_from_list:
|
||||
spin_lock_irqsave(&gpio_lock, flags);
|
||||
|
|
@ -1346,6 +1403,7 @@ void gpiochip_remove(struct gpio_chip *chip)
|
|||
acpi_gpiochip_remove(chip);
|
||||
gpiochip_remove_pin_ranges(chip);
|
||||
of_gpiochip_remove(chip);
|
||||
gpiochip_free_valid_mask(chip);
|
||||
/*
|
||||
* We accept no more calls into the driver from this point, so
|
||||
* NULL the driver data pointer
|
||||
|
|
@ -1506,14 +1564,10 @@ static int gpiochip_irqchip_init_valid_mask(struct gpio_chip *gpiochip)
|
|||
if (!gpiochip->irq.need_valid_mask)
|
||||
return 0;
|
||||
|
||||
gpiochip->irq.valid_mask = kcalloc(BITS_TO_LONGS(gpiochip->ngpio),
|
||||
sizeof(long), GFP_KERNEL);
|
||||
gpiochip->irq.valid_mask = gpiochip_allocate_mask(gpiochip);
|
||||
if (!gpiochip->irq.valid_mask)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Assume by default all GPIOs are valid */
|
||||
bitmap_fill(gpiochip->irq.valid_mask, gpiochip->ngpio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1526,6 +1580,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gpiochip)
|
|||
bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gpiochip,
|
||||
unsigned int offset)
|
||||
{
|
||||
if (!gpiochip_line_is_valid(gpiochip, offset))
|
||||
return false;
|
||||
/* No mask means all valid */
|
||||
if (likely(!gpiochip->irq.valid_mask))
|
||||
return true;
|
||||
|
|
@ -3689,7 +3745,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev,
|
|||
}
|
||||
|
||||
if (IS_ERR(desc)) {
|
||||
dev_dbg(dev, "lookup for GPIO %s failed\n", con_id);
|
||||
dev_dbg(dev, "No GPIO consumer %s found\n", con_id);
|
||||
return desc;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@
|
|||
#define GPIOLIB_H
|
||||
|
||||
#include <linux/gpio/driver.h>
|
||||
#include <linux/gpio/consumer.h> /* for enum gpiod_flags */
|
||||
#include <linux/err.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/cdev.h>
|
||||
|
||||
enum of_gpio_flags;
|
||||
enum gpiod_flags;
|
||||
enum gpio_lookup_flags;
|
||||
struct acpi_device;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue