Basic support for IOT2000 devices
Subsystems (8)
cover Basic support for IOT2000 devices
This is the first of 3 chunks to enable the CIP kernel for the IOT2000 devices. It has the side effect of improving also the support of the by now discontinued Galileo Gen 2 board.
Features added:
- Ethernet MACs on IOT2000
- TI ADC108S102 on IOT2000 and Galileo
- I2C frequency tuning on IOT2000
- GPIO support for pca9685 PWMs on IOT2000 and Galileo
- interrupt support for PCAL9535 on IOT2000 and Galileo
Jan
Alison Schofield (1):
iio: core: implement iio_device_{claim|release}_direct_mode()
Jan Kiszka (4):
stmmac: Add support for SIMATIC IOT2000 platform
iio: adc: Add support for TI ADC108S102 and ADC128S102
mfd: intel_quark_i2c_gpio: Use dmi_system_id table for retrieving
frequency
mfd: intel_quark_i2c_gpio: Add support for SIMATIC IOT2000 platform
Mika Westerberg (1):
pwm: pca9685: Allow any of the 16 PWMs to be used as a GPIO
Sven Van Asbroeck (1):
pwm: pca9685: Fix GPIO-only operation
Yong Li (1):
gpio: pca953x: add PCAL9535 interrupt support for Galileo Gen2
.../devicetree/bindings/iio/adc/ti-adc108s102.txt | 18 ++ drivers/gpio/gpio-pca953x.c | 42 ++- drivers/iio/adc/Kconfig | 12 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ti-adc108s102.c | 348 +++++++++++++++++++++ drivers/iio/industrialio-core.c | 39 +++ drivers/mfd/intel_quark_i2c_gpio.c | 49 +-- drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c | 26 +- drivers/pwm/pwm-pca9685.c | 248 +++++++++++++-- include/linux/iio/iio.h | 2 + 10 files changed, 743 insertions(+), 42 deletions(-) create mode 100644 Documentation/devicetree/bindings/iio/adc/ti-adc108s102.txt create mode 100644 drivers/iio/adc/ti-adc108s102.c
Signature · 1 line
2.12.3
PATCH 1/8 stmmac: Add support for SIMATIC IOT2000 platform
From: Jan Kiszka <[email protected]>
commit 212c7fd614377fef4415d94856a59e9f484aa439 upstream.
The IOT2000 is industrial controller platform, derived from the Intel Galileo Gen2 board. The variant IOT2020 comes with one LAN port, the IOT2040 has two of them. They can be told apart based on the board asset tag in the DMI table.
Based on patch by Sascha Weisenberger.
--- drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c | 26 +++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-)
drivers/net/ethernet/stmicro/stmmac/stmmac_pci.c +25 -1
@@ -36,6 +36,7 @@*/ struct stmmac_pci_dmi_data {const char *name; +const char *asset_tag;unsigned int func;int phy_addr; };
@@ -50,6 +51,7 @@ struct stmmac_pci_info {static int stmmac_pci_find_phy_addr (struct stmmac_pci_info *info) {const char *name =dmi_get_system_info (DMI_BOARD_NAME ); +const char *asset_tag =dmi_get_system_info (DMI_BOARD_ASSET_TAG );unsigned int func =PCI_FUNC (info->pdev->devfn);struct stmmac_pci_dmi_data *dmi;
@@ -61,8 +63,12 @@ static int stmmac_pci_find_phy_addr(struct stmmac_pci_info *info)return 1 ;for (dmi = info->dmi; dmi->name && *dmi->name; dmi++) { -if (!strcmp (dmi->name, name) && dmi->func == func) +if (!strcmp (dmi->name, name) && dmi->func == func) { +/* If asset tag is provided, match on it as well. */ +if (dmi->asset_tag &&strcmp (dmi->asset_tag, asset_tag)) +continue ;return dmi->phy_addr; + } }return -ENODEV ;
@@ -138,6 +144,24 @@ static struct stmmac_pci_dmi_data quark_pci_dmi_data[] = { .func = 6, .phy_addr = 1, }, + { + .name = "SIMATIC IOT2000", + .asset_tag = "6ES7647-0AA00-0YA2", + .func = 6, + .phy_addr = 1, + }, + { + .name = "SIMATIC IOT2000", + .asset_tag = "6ES7647-0AA00-1YA2", + .func = 6, + .phy_addr = 1, + }, + { + .name = "SIMATIC IOT2000", + .asset_tag = "6ES7647-0AA00-1YA2", + .func = 7, + .phy_addr = 1, + }, {} };
Signature · 1 line
2.12.3
PATCH 2/8 iio: core: implement iio_device_{claim|release}_direct_mode()
In reply to Jan KiszkaFrom: Alison Schofield <[email protected]>
commit 08a33805518e7845486f88287e8aace6f8439391 upstream.
It is often the case that the driver wants to be sure a device stays in direct mode while it is executing a task or series of tasks. To accomplish this today, the driver performs this sequence: 1) take the device state lock, 2) verify it is not in a buffered mode, 3) execute some tasks, and 4) release that lock.
This patch introduces a pair of helper functions that simplify these steps and make it more semantically expressive.
iio_device_claim_direct_mode()
If the device is not in any buffered mode it is guaranteed to stay that way until iio_release_direct_mode() is called.
iio_device_release_direct_mode()
Release the claim. Device is no longer guaranteed to stay in direct mode.
--- drivers/iio/industrialio-core.c | 39 +++++++++++++++++++++++++++++++++++++++ include/linux/iio/iio.h | 2 ++ 2 files changed, 41 insertions(+)
drivers/iio/industrialio-core.c +39 -0
@@ -25,6 +25,7 @@#include <linux/slab.h> #include <linux/anon_inodes.h> #include <linux/debugfs.h> +#include <linux/mutex.h> #include <linux/iio/iio.h> #include "iio_core.h" #include "iio_core_trigger.h"
@@ -1363,6 +1364,44 @@ void devm_iio_device_unregister(struct device *dev, struct iio_dev *indio_dev) }EXPORT_SYMBOL_GPL (devm_iio_device_unregister); +/** +* iio_device_claim_direct_mode - Keep device in direct mode +* @indio_dev: the iio_dev associated with the device +* +* If the device is in direct mode it is guaranteed to stay +* that way until iio_device_release_direct_mode() is called. +* +* Use with iio_device_release_direct_mode() +* +* Returns: 0 on success, -EBUSY on failure +*/ +int iio_device_claim_direct_mode (struct iio_dev *indio_dev) +{ +mutex_lock (&indio_dev->mlock); + +if (iio_buffer_enabled (indio_dev)) { +mutex_unlock (&indio_dev->mlock); +return -EBUSY ; + } +return 0 ; +} +EXPORT_SYMBOL_GPL (iio_device_claim_direct_mode); + +/** +* iio_device_release_direct_mode - releases claim on direct mode +* @indio_dev: the iio_dev associated with the device +* +* Release the claim. Device is no longer guaranteed to stay +* in direct mode. +* +* Use with iio_device_claim_direct_mode() +*/ +void iio_device_release_direct_mode (struct iio_dev *indio_dev) +{ +mutex_unlock (&indio_dev->mlock); +} +EXPORT_SYMBOL_GPL (iio_device_release_direct_mode); +subsys_initcall (iio_init);module_exit (iio_exit);
include/linux/iio/iio.h +2 -0
@@ -527,6 +527,8 @@ void iio_device_unregister(struct iio_dev *indio_dev);int devm_iio_device_register (struct device *dev,struct iio_dev *indio_dev);void devm_iio_device_unregister (struct device *dev,struct iio_dev *indio_dev);int iio_push_event (struct iio_dev *indio_dev,u64 ev_code,s64 timestamp); +int iio_device_claim_direct_mode (struct iio_dev *indio_dev); +void iio_device_release_direct_mode (struct iio_dev *indio_dev);extern struct bus_type iio_bus_type;
Signature · 1 line
2.12.3
PATCH 3/8 iio: adc: Add support for TI ADC108S102 and ADC128S102
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
commit 7e87d11c9bda75816ced8d0895e8d24e5c52833a upstream.
This is an upstream port of an IIO driver for the TI ADC108S102 and ADC128S102. The former can be found on the Intel Galileo Gen2 and the Siemens SIMATIC IOT2000. For those boards, ACPI-based enumeration is included.
Due to the lack of regulators under ACPI, we hard-code the voltage provided to the VA pin of the ADC to 5 V, the value used on Galileo and IOT2000. For DT usage, the regulator "vref-supply" provides this information. Note that DT usage has not been tested.
Original author: Bogdan Pricop <[email protected]> Ported from Intel Galileo Gen2 BSP to Intel Yocto kernel:
Todor Minchev <[email protected]>.
--- .../devicetree/bindings/iio/adc/ti-adc108s102.txt | 18 ++ drivers/iio/adc/Kconfig | 12 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/ti-adc108s102.c | 348 +++++++++++++++++++++ 4 files changed, 379 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/ti-adc108s102.txt create mode 100644 drivers/iio/adc/ti-adc108s102.c
Documentation/devicetree/bindings/iio/adc/ti-adc108s102.txt new file +18 -0
@@ -0,0 +1,18 @@ +* Texas Instruments' ADC108S102 and ADC128S102 ADC chip + +Required properties: + - compatible: Should be "ti,adc108s102" + - reg: spi chip select number for the device + - vref-supply: The regulator supply for ADC reference voltage + +Recommended properties: + - spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt + +Example: +adc at 0 { + compatible = "ti,adc108s102"; + reg = <0>; + vref-supply = <&vdd_supply>; + spi-max-frequency = <1000000>; +};
drivers/iio/adc/Kconfig +12 -0
@@ -324,6 +324,18 @@ config TI_ADC081C This driver can also be built as a module. If so, the module will be called ti-adc081c. +config TI_ADC108S102 + tristate "Texas Instruments ADC108S102 and ADC128S102 driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Texas Instruments ADC108S102 and + ADC128S102 ADC. + + To compile this driver as a module, choose M here: the module will + be called ti-adc108s102. + config TI_ADC128S052 tristate "Texas Instruments ADC128S052/ADC122S021" depends on SPI
drivers/iio/adc/Makefile +1 -0
@@ -31,6 +31,7 @@ obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o +obj-$(CONFIG_TI_ADC108S102) += ti-adc108s102.o obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
drivers/iio/adc/ti-adc108s102.c new file +348 -0
348 lines not on this page · raw message
Signature · 1 line
2.12.3
PATCH 4/8 mfd: intel_quark_i2c_gpio: Use dmi_system_id table for retrieving frequency
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
commit b518d4adb83e3e139d3de96d172d9b4dec6def09 upstream.
Avoids reimplementation of DMI matching in intel_quark_i2c_setup.
--- drivers/mfd/intel_quark_i2c_gpio.c | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-)
drivers/mfd/intel_quark_i2c_gpio.c +13 -20
@@ -60,19 +60,18 @@ struct intel_quark_mfd {struct clk_lookup *i2c_clk_lookup; }; -struct i2c_mode_info { -const char *name; -unsigned int i2c_scl_freq; -}; - -static const struct i2c_mode_info platform_i2c_mode_info[] = { +static const struct dmi_system_id dmi_platform_info[] = { { - .name ="Galileo" , - .i2c_scl_freq =100000 , + .matches = { +DMI_EXACT_MATCH (DMI_BOARD_NAME ,"Galileo" ), + }, + .driver_data = (void *)100000 , }, { - .name ="GalileoGen2" , - .i2c_scl_freq =400000 , + .matches = { +DMI_EXACT_MATCH (DMI_BOARD_NAME ,"GalileoGen2" ), + }, + .driver_data = (void *)400000 , }, {} };
@@ -167,8 +166,7 @@ static void intel_quark_unregister_i2c_clk(struct pci_dev *pdev)static int intel_quark_i2c_setup (struct pci_dev *pdev,struct mfd_cell *cell) { -const char *board_name =dmi_get_system_info (DMI_BOARD_NAME ); -const struct i2c_mode_info *info; +const struct dmi_system_id *dmi_id;struct dw_i2c_platform_data *pdata;struct resource *res = (struct resource *)cell->resources;struct device *dev = &pdev->dev;
@@ -188,14 +186,9 @@ static int intel_quark_i2c_setup(struct pci_dev *pdev, struct mfd_cell *cell)/* Normal mode by default */ pdata->i2c_scl_freq =100000 ; -if (board_name) { -for (info = platform_i2c_mode_info; info->name; info++) { -if (!strcmp (board_name, info->name)) { - pdata->i2c_scl_freq = info->i2c_scl_freq; -break ; - } - } - } + dmi_id =dmi_first_match (dmi_platform_info); +if (dmi_id) + pdata->i2c_scl_freq = (uintptr_t )dmi_id->driver_data; cell->platform_data = pdata; cell->pdata_size =sizeof (*pdata);
Signature · 1 line
2.12.3
PATCH 5/8 mfd: intel_quark_i2c_gpio: Add support for SIMATIC IOT2000 platform
In reply to Jan KiszkaFrom: Jan Kiszka <[email protected]>
commit 842086d2b5a6cec23d598edffb5137c72b265c50 upstream.
The SIMATIC IOT2020 and IOT2040 are derived from the Galileo Gen2 board and share its I2C frequency.
--- drivers/mfd/intel_quark_i2c_gpio.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+)
drivers/mfd/intel_quark_i2c_gpio.c +16 -0
@@ -73,6 +73,22 @@ static const struct dmi_system_id dmi_platform_info[] = { }, .driver_data = (void *)400000, }, + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), + DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG, + "6ES7647-0AA00-0YA2"), + }, + .driver_data = (void *)400000, + }, + { + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_NAME, "SIMATIC IOT2000"), + DMI_EXACT_MATCH(DMI_BOARD_ASSET_TAG, + "6ES7647-0AA00-1YA2"), + }, + .driver_data = (void *)400000, + }, {} };
Signature · 1 line
2.12.3
PATCH 6/8 pwm: pca9685: Allow any of the 16 PWMs to be used as a GPIO
In reply to Jan KiszkaFrom: Mika Westerberg <[email protected]>
commit bccec89f0a35f65302734d1cdb01479df0f33ac9 upstream.
The PCA9685 controller has full on/off bit for each PWM channel. Setting this bit bypasses the PWM control and the line works just as it would be a GPIO. Furthermore in Intel Galileo it is actually used as GPIO output for discreet muxes on the board.
This patch adds GPIO output only support for the driver so that we can control the muxes on Galileo using standard GPIO interfaces available in the kernel. GPIO and PWM functionality is exclusive so only one can be active at a time on a single PWM channel.
--- drivers/pwm/pwm-pca9685.c | 164 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 163 insertions(+), 1 deletion(-)
drivers/pwm/pwm-pca9685.c +163 -1
@@ -20,8 +20,10 @@*/ #include <linux/acpi.h> +#include <linux/gpio/driver.h> #include <linux/i2c.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/pwm.h>
@@ -80,6 +82,10 @@ struct pca9685 {int active_cnt;int duty_ns;int period_ns; +#if IS_ENABLED (CONFIG_GPIOLIB ) +struct mutex lock; +struct gpio_chip gpio; +#endif };static inline struct pca9685 *to_pca (struct pwm_chip *chip)
@@ -87,6 +93,151 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip) return container_of(chip, struct pca9685, chip); } +#if IS_ENABLED (CONFIG_GPIOLIB ) +static int pca9685_pwm_gpio_request (struct gpio_chip *gpio,unsigned int offset) +{ +struct pca9685 *pca =gpiochip_get_data (gpio); +struct pwm_device *pwm; + +mutex_lock (&pca->lock); + + pwm = &pca->chip.pwms[offset]; + +if (pwm->flags & (PWMF_REQUESTED |PWMF_EXPORTED )) { +mutex_unlock (&pca->lock); +return -EBUSY ; + } + +pwm_set_chip_data (pwm, (void *)1 ); + +mutex_unlock (&pca->lock); +return 0 ; +} + +static void pca9685_pwm_gpio_free (struct gpio_chip *gpio,unsigned int offset) +{ +struct pca9685 *pca =gpiochip_get_data (gpio); +struct pwm_device *pwm; + +mutex_lock (&pca->lock); + pwm = &pca->chip.pwms[offset]; +pwm_set_chip_data (pwm,NULL ); +mutex_unlock (&pca->lock); +} + +static bool pca9685_pwm_is_gpio (struct pca9685 *pca,struct pwm_device *pwm) +{ +bool is_gpio = false; + +mutex_lock (&pca->lock); + +if (pwm->hwpwm >=PCA9685_MAXCHAN ) { +unsigned int i; + +/* +* Check if any of the GPIOs are requested and in that case +* prevent using the "all LEDs" channel. +*/ +for (i =0 ; i < pca->gpio.ngpio; i++) +if (gpiochip_is_requested (&pca->gpio, i)) { + is_gpio = true; +break ; + } + }else if (pwm_get_chip_data (pwm)) { + is_gpio = true; + } + +mutex_unlock (&pca->lock); +return is_gpio; +} + +static int pca9685_pwm_gpio_get (struct gpio_chip *gpio,unsigned int offset) +{ +struct pca9685 *pca =gpiochip_get_data (gpio); +struct pwm_device *pwm = &pca->chip.pwms[offset]; +unsigned int value; + +regmap_read (pca->regmap,LED_N_ON_H (pwm->hwpwm), &value); + +return value &LED_FULL ; +} + +static void pca9685_pwm_gpio_set (struct gpio_chip *gpio,unsigned int offset, +int value) +{ +struct pca9685 *pca =gpiochip_get_data (gpio); +struct pwm_device *pwm = &pca->chip.pwms[offset]; +unsigned int on = value ?LED_FULL :0 ; + +/* Clear both OFF registers */ +regmap_write (pca->regmap,LED_N_OFF_L (pwm->hwpwm),0 ); +regmap_write (pca->regmap,LED_N_OFF_H (pwm->hwpwm),0 ); + +/* Set the full ON bit */ +regmap_write (pca->regmap,LED_N_ON_H (pwm->hwpwm), on); +} + +static int pca9685_pwm_gpio_get_direction (struct gpio_chip *chip, +unsigned int offset) +{ +/* Always out */ +return 0 ; +} + +static int pca9685_pwm_gpio_direction_input (struct gpio_chip *gpio, +unsigned int offset) +{ +return -EINVAL ; +} + +static int pca9685_pwm_gpio_direction_output (struct gpio_chip *gpio, +unsigned int offset,int value) +{ +pca9685_pwm_gpio_set (gpio, offset, value); + +return 0 ; +} + +/* +* The PCA9685 has a bit for turning the PWM output full off or on. Some +* boards like Intel Galileo actually uses these as normal GPIOs so we +* expose a GPIO chip here which can exclusively take over the underlying +* PWM channel. +*/ +static int pca9685_pwm_gpio_probe (struct pca9685 *pca) +{ +struct device *dev = pca->chip.dev; + +mutex_init (&pca->lock); + + pca->gpio.label =dev_name (dev); + pca->gpio.dev = dev; + pca->gpio.request = pca9685_pwm_gpio_request; + pca->gpio.free = pca9685_pwm_gpio_free; + pca->gpio.get_direction = pca9685_pwm_gpio_get_direction; + pca->gpio.direction_input = pca9685_pwm_gpio_direction_input; + pca->gpio.direction_output = pca9685_pwm_gpio_direction_output; + pca->gpio.get = pca9685_pwm_gpio_get; + pca->gpio.set = pca9685_pwm_gpio_set; + pca->gpio.base =-1 ; + pca->gpio.ngpio =PCA9685_MAXCHAN ; + pca->gpio.can_sleep = true; + +return devm_gpiochip_add_data (dev, &pca->gpio, pca); +} +#else +static inline bool pca9685_pwm_is_gpio (struct pca9685 *pca, +struct pwm_device *pwm) +{ +return false; +} + +static inline int pca9685_pwm_gpio_probe (struct pca9685 *pca) +{ +return 0 ; +} +#endif +static int pca9685_pwm_config (struct pwm_chip *chip,struct pwm_device *pwm,int duty_ns,int period_ns) {
@@ -253,6 +404,9 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) {struct pca9685 *pca =to_pca (chip); +if (pca9685_pwm_is_gpio (pca, pwm)) +return -EBUSY ; +if (pca->active_cnt++ ==0 )return regmap_update_bits (pca->regmap,PCA9685_MODE1 ,MODE1_SLEEP ,0x0 );
@@ -334,7 +488,15 @@ static int pca9685_pwm_probe(struct i2c_client *client, pca->chip.base =-1 ; pca->chip.can_sleep = true; -return pwmchip_add (&pca->chip); + ret =pwmchip_add (&pca->chip); +if (ret <0 ) +return ret; + + ret =pca9685_pwm_gpio_probe (pca); +if (ret <0 ) +pwmchip_remove (&pca->chip); + +return ret; }static int pca9685_pwm_remove (struct i2c_client *client)
Signature · 1 line
2.12.3
PATCH 6/8 pwm: pca9685: Allow any of the 16 PWMs to be used as a GPIO
Quoted 3 lines · Jan Kiszka
From: Mika Westerberg <[email protected]>
commit
bccec89f0a35f65302734d1cdb01479df0f33ac9upstream.
[...]
drivers/pwm/pwm-pca9685.c static inline struct pca9685 *to_pca(struct pwm_chip *chip) · line 99
Quoted 8 lines
drivers/pwm/pwm-pca9685.c +4
-0@@ -87,6 +93,151 @@ static inline struct pca9685 *to_pca(struct pwm_chip *chip) return container_of(chip, struct pca9685, chip); } +#if IS_ENABLED (CONFIG_GPIOLIB ) +static int pca9685_pwm_gpio_request (struct gpio_chip *gpio,unsigned int offset) +{ +struct pca9685 *pca =gpiochip_get_data (gpio);
[...]
gpiochip_get_data() doesn't exist in 4.4.
I've cherry-picked commits b08ea35a3296 "gpio: add a data pointer to gpio_chip", 0cf3292cde22 "gpio: Add devm_ apis for gpiochip_add_data and gpiochip_remove", and 3988d663c02f "gpiolib: Fix a WARN_ON that can never trigger" before this. But you should have included them in this patch series.
Ben.
Signature · 2 lines
Ben Hutchings Software Developer, Codethink Ltd.
PATCH 7/8 pwm: pca9685: Fix GPIO-only operation
In reply to Jan KiszkaFrom: Sven Van Asbroeck <[email protected]>
commit c40c461e1944b9cfb520e04184ec1e5c80fb210b upstream.
GPIO-only driver operation never clears the SLEEP bit, which can cause the GPIOs to become unusable.
Example:
1. user requests first PWM -> driver clears SLEEP bit 2. user frees last PWM -> driver sets SLEEP bit
3. user requests GPIO
4. user switches GPIO on -> output does not turn on
because SLEEP bit is set
Prevent this behaviour by letting the runtime PM framework control the SLEEP bit. This will put the chip to SLEEP if no PWMs/GPIOs are exported or in use.
--- drivers/pwm/pwm-pca9685.c | 112 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 79 insertions(+), 33 deletions(-)
drivers/pwm/pwm-pca9685.c +79 -33
@@ -30,6 +30,7 @@#include <linux/regmap.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/pm_runtime.h> /* * Because the PCA9685 has only one prescaler per chip, changing the period of
@@ -79,7 +80,6 @@struct pca9685 {struct pwm_chip chip;struct regmap *regmap; -int active_cnt;int duty_ns;int period_ns;#if IS_ENABLED (CONFIG_GPIOLIB )
@@ -111,20 +111,10 @@ static int pca9685_pwm_gpio_request(struct gpio_chip *gpio, unsigned int offset)pwm_set_chip_data (pwm, (void *)1 );mutex_unlock (&pca->lock); +pm_runtime_get_sync (pca->chip.dev);return 0 ; } -static void pca9685_pwm_gpio_free (struct gpio_chip *gpio,unsigned int offset) -{ -struct pca9685 *pca =gpiochip_get_data (gpio); -struct pwm_device *pwm; - -mutex_lock (&pca->lock); - pwm = &pca->chip.pwms[offset]; -pwm_set_chip_data (pwm,NULL ); -mutex_unlock (&pca->lock); -} -static bool pca9685_pwm_is_gpio (struct pca9685 *pca,struct pwm_device *pwm) {bool is_gpio = false;
@@ -177,6 +167,19 @@ static void pca9685_pwm_gpio_set(struct gpio_chip *gpio, unsigned int offset,regmap_write (pca->regmap,LED_N_ON_H (pwm->hwpwm), on); } +static void pca9685_pwm_gpio_free (struct gpio_chip *gpio,unsigned int offset) +{ +struct pca9685 *pca =gpiochip_get_data (gpio); +struct pwm_device *pwm; + +pca9685_pwm_gpio_set (gpio, offset,0 ); +pm_runtime_put (pca->chip.dev); +mutex_lock (&pca->lock); + pwm = &pca->chip.pwms[offset]; +pwm_set_chip_data (pwm,NULL ); +mutex_unlock (&pca->lock); +} +static int pca9685_pwm_gpio_get_direction (struct gpio_chip *chip,unsigned int offset) {
@@ -238,6 +241,16 @@ static inline int pca9685_pwm_gpio_probe(struct pca9685 *pca) }#endif +static void pca9685_set_sleep_mode (struct pca9685 *pca,int sleep) +{ +regmap_update_bits (pca->regmap,PCA9685_MODE1 , +MODE1_SLEEP , sleep ?MODE1_SLEEP :0 ); +if (!sleep) { +/* Wait 500us for the oscillator to be back up */ +udelay (500 ); + } +} +static int pca9685_pwm_config (struct pwm_chip *chip,struct pwm_device *pwm,int duty_ns,int period_ns) {
@@ -252,19 +265,20 @@ static int pca9685_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,if (prescale >=PCA9685_PRESCALE_MIN && prescale <=PCA9685_PRESCALE_MAX ) { +/* +* putting the chip briefly into SLEEP mode +*@this point won't interfere with the +* pm_runtime framework, because the pm_runtime +* state is guaranteed active here. +*/ /* Put chip into sleep mode */ -regmap_update_bits (pca->regmap,PCA9685_MODE1 , -MODE1_SLEEP ,MODE1_SLEEP ); +pca9685_set_sleep_mode (pca,1 );/* Change the chip-wide output frequency */ regmap_write (pca->regmap,PCA9685_PRESCALE , prescale);/* Wake the chip up */ -regmap_update_bits (pca->regmap,PCA9685_MODE1 , -MODE1_SLEEP ,0x0 ); - -/* Wait 500us for the oscillator to be back up */ -udelay (500 ); +pca9685_set_sleep_mode (pca,0 ); pca->period_ns = period_ns; }else {
@@ -406,21 +420,15 @@ static int pca9685_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)if (pca9685_pwm_is_gpio (pca, pwm))return -EBUSY ; - -if (pca->active_cnt++ ==0 ) -return regmap_update_bits (pca->regmap,PCA9685_MODE1 , -MODE1_SLEEP ,0x0 ); +pm_runtime_get_sync (chip->dev);return 0 ; }static void pca9685_pwm_free (struct pwm_chip *chip,struct pwm_device *pwm) { -struct pca9685 *pca =to_pca (chip); - -if (--pca->active_cnt ==0 ) -regmap_update_bits (pca->regmap,PCA9685_MODE1 ,MODE1_SLEEP , -MODE1_SLEEP ); +pca9685_pwm_disable (chip, pwm); +pm_runtime_put (chip->dev); }static const struct pwm_ops pca9685_pwm_ops = {
@@ -493,22 +501,54 @@ static int pca9685_pwm_probe(struct i2c_client *client,return ret; ret =pca9685_pwm_gpio_probe (pca); -if (ret <0 ) +if (ret <0 ) {pwmchip_remove (&pca->chip); +return ret; + } + +/* the chip comes out of power-up in the active state */ +pm_runtime_set_active (&client->dev); +/* +* enable will put the chip into suspend, which is what we +* want as all outputs are disabled at this point +*/ +pm_runtime_enable (&client->dev); -return ret; +return 0 ; }static int pca9685_pwm_remove (struct i2c_client *client) {struct pca9685 *pca =i2c_get_clientdata (client); +int ret; -regmap_update_bits (pca->regmap,PCA9685_MODE1 ,MODE1_SLEEP , -MODE1_SLEEP ); + ret =pwmchip_remove (&pca->chip); +if (ret) +return ret; +pm_runtime_disable (&client->dev); +return 0 ; +} -return pwmchip_remove (&pca->chip); +#ifdef CONFIG_PM +static int pca9685_pwm_runtime_suspend (struct device *dev) +{ +struct i2c_client *client =to_i2c_client (dev); +struct pca9685 *pca =i2c_get_clientdata (client); + +pca9685_set_sleep_mode (pca,1 ); +return 0 ; } +static int pca9685_pwm_runtime_resume (struct device *dev) +{ +struct i2c_client *client =to_i2c_client (dev); +struct pca9685 *pca =i2c_get_clientdata (client); + +pca9685_set_sleep_mode (pca,0 ); +return 0 ; +} +#endif +static const struct i2c_device_id pca9685_id[] = { {"pca9685" ,0 }, {/* sentinel */ },
@@ -531,11 +571,17 @@ static const struct of_device_id pca9685_dt_ids[] = {MODULE_DEVICE_TABLE (of, pca9685_dt_ids);#endif +static const struct dev_pm_ops pca9685_pwm_pm = { +SET_RUNTIME_PM_OPS (pca9685_pwm_runtime_suspend, + pca9685_pwm_runtime_resume,NULL ) +}; +static struct i2c_driver pca9685_i2c_driver = { .driver = { .name ="pca9685-pwm" , .acpi_match_table =ACPI_PTR (pca9685_acpi_ids), .of_match_table =of_match_ptr (pca9685_dt_ids), + .pm = &pca9685_pwm_pm, }, .probe = pca9685_pwm_probe, .remove = pca9685_pwm_remove,
Signature · 1 line
2.12.3
PATCH 8/8 gpio: pca953x: add PCAL9535 interrupt support for Galileo Gen2
In reply to Jan KiszkaFrom: Yong Li <[email protected]>
commit 44896beae605b93f2232301befccb7ef42953198 upstream.
Galileo Gen2 board uses the PCAL9535 as the GPIO expansion,
it is different from PCA9535 and includes interrupt mask/status registers, The current driver does not support the interrupt registers configuration, it causes some gpio pins cannot trigger interrupt events,
this patch fix this issue.
The original patch was submitted by
Josef Ahmad <[email protected]>
http://git.yoctoproject.org/cgit/cgit.cgi/meta-intel-quark/tree/recipes-kernel/linux/files/0015-Quark-GPIO-1-2-quark.patch
--- drivers/gpio/gpio-pca953x.c | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-)
drivers/gpio/gpio-pca953x.c +41 -1
@@ -39,8 +39,13 @@#define PCA957X_MSK 6#define PCA957X_INTS 7 +#define PCAL953X_IN_LATCH 34 +#define PCAL953X_INT_MASK 37 +#define PCAL953X_INT_STAT 38 +#define PCA_GPIO_MASK 0x00FF#define PCA_INT 0x0100 +#define PCA_PCAL 0x0200#define PCA953X_TYPE 0x1000#define PCA957X_TYPE 0x2000#define PCA_TYPE_MASK 0xF000
@@ -78,7 +83,7 @@ static const struct i2c_device_id pca953x_id[] = {MODULE_DEVICE_TABLE (i2c, pca953x_id);static const struct acpi_device_id pca953x_acpi_ids[] = { - {"INT3491" ,16 |PCA953X_TYPE |PCA_INT , }, + {"INT3491" ,16 |PCA953X_TYPE |PCA_INT |PCA_PCAL , }, { } };MODULE_DEVICE_TABLE (acpi, pca953x_acpi_ids);
@@ -403,6 +408,18 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d)struct pca953x_chip *chip =to_pca (gc);u8 new_irqs;int level, i; +u8 invert_irq_mask[MAX_BANK ]; + +if (chip->driver_data &PCA_PCAL ) { +/* Enable latch on interrupt-enabled inputs */ +pca953x_write_regs (chip,PCAL953X_IN_LATCH , chip->irq_mask); + +for (i =0 ; i <NBANK (chip); i++) + invert_irq_mask[i] = ~chip->irq_mask[i]; + +/* Unmask enabled interrupts */ +pca953x_write_regs (chip,PCAL953X_INT_MASK , invert_irq_mask); + }/* Look for any newly setup interrupt */ for (i =0 ; i <NBANK (chip); i++) {
@@ -464,6 +481,29 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, u8 *pending)u8 trigger[MAX_BANK ];int ret, i, offset =0 ; +if (chip->driver_data &PCA_PCAL ) { +/* Read the current interrupt status from the device */ + ret =pca953x_read_regs (chip,PCAL953X_INT_STAT , trigger); +if (ret) +return false; + +/* Check latched inputs and clear interrupt status */ + ret =pca953x_read_regs (chip,PCA953X_INPUT , cur_stat); +if (ret) +return false; + +for (i =0 ; i <NBANK (chip); i++) { +/* Apply filter for rising/falling edge selection */ + pending[i] = (~cur_stat[i] & chip->irq_trig_fall[i]) | + (cur_stat[i] & chip->irq_trig_raise[i]); + pending[i] &= trigger[i]; +if (pending[i]) + pending_seen = true; + } + +return pending_seen; + } +switch (chip->chip_type) {case PCA953X_TYPE : offset =PCA953X_INPUT ;
Signature · 1 line
2.12.3
cover Basic support for IOT2000 devices
In reply to Jan KiszkaQuoted 10 lines · Jan Kiszka
This is the first of 3 chunks to enable the CIP kernel for the IOT2000 devices. It has the side effect of improving also the support of the by now discontinued Galileo Gen 2 board.
Features added:
- Ethernet MACs on IOT2000
- TI ADC108S102 on IOT2000 and Galileo
- I2C frequency tuning on IOT2000
- GPIO support for pca9685 PWMs on IOT2000 and Galileo
- interrupt support for PCAL9535 on IOT2000 and Galileo
[...]
Applied this series, along with the gpiolib additions already noted.
Ben.
Signature · 2 lines
Ben Hutchings Software Developer, Codethink Ltd.