diff options
| -rw-r--r-- | Documentation/devicetree/bindings/mfd/s2mps11.txt | 22 | ||||
| -rw-r--r-- | drivers/clk/clk-s2mps11.c | 24 | ||||
| -rw-r--r-- | drivers/gpio/Kconfig | 12 | ||||
| -rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
| -rw-r--r-- | drivers/gpio/gpio-dln2.c | 553 | ||||
| -rw-r--r-- | drivers/i2c/busses/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/i2c/busses/Makefile | 1 | ||||
| -rw-r--r-- | drivers/i2c/busses/i2c-dln2.c | 262 | ||||
| -rw-r--r-- | drivers/iio/adc/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/iio/adc/Makefile | 1 | ||||
| -rw-r--r-- | drivers/iio/adc/axp288_adc.c | 261 | ||||
| -rw-r--r-- | drivers/mfd/Kconfig | 13 | ||||
| -rw-r--r-- | drivers/mfd/Makefile | 1 | ||||
| -rw-r--r-- | drivers/mfd/axp20x.c | 361 | ||||
| -rw-r--r-- | drivers/mfd/dln2.c | 769 | ||||
| -rw-r--r-- | drivers/mfd/sec-core.c | 29 | ||||
| -rw-r--r-- | drivers/mfd/sec-irq.c | 23 | ||||
| -rw-r--r-- | drivers/regulator/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/regulator/s2mps11.c | 102 | ||||
| -rw-r--r-- | include/linux/mfd/axp20x.h | 59 | ||||
| -rw-r--r-- | include/linux/mfd/core.h | 7 | ||||
| -rw-r--r-- | include/linux/mfd/dln2.h | 103 | ||||
| -rw-r--r-- | include/linux/mfd/samsung/core.h | 2 | ||||
| -rw-r--r-- | include/linux/mfd/samsung/s2mps13.h | 186 | ||||
| -rw-r--r-- | include/linux/of.h | 11 |
25 files changed, 2751 insertions, 80 deletions
diff --git a/Documentation/devicetree/bindings/mfd/s2mps11.txt b/Documentation/devicetree/bindings/mfd/s2mps11.txt index 0e4026a6cbbf..57a045016fca 100644 --- a/Documentation/devicetree/bindings/mfd/s2mps11.txt +++ b/Documentation/devicetree/bindings/mfd/s2mps11.txt | |||
| @@ -1,5 +1,5 @@ | |||
| 1 | 1 | ||
| 2 | * Samsung S2MPS11, S2MPS14 and S2MPU02 Voltage and Current Regulator | 2 | * Samsung S2MPS11, S2MPS13, S2MPS14 and S2MPU02 Voltage and Current Regulator |
| 3 | 3 | ||
| 4 | The Samsung S2MPS11 is a multi-function device which includes voltage and | 4 | The Samsung S2MPS11 is a multi-function device which includes voltage and |
| 5 | current regulators, RTC, charger controller and other sub-blocks. It is | 5 | current regulators, RTC, charger controller and other sub-blocks. It is |
| @@ -7,8 +7,8 @@ interfaced to the host controller using an I2C interface. Each sub-block is | |||
| 7 | addressed by the host system using different I2C slave addresses. | 7 | addressed by the host system using different I2C slave addresses. |
| 8 | 8 | ||
| 9 | Required properties: | 9 | Required properties: |
| 10 | - compatible: Should be "samsung,s2mps11-pmic" or "samsung,s2mps14-pmic" | 10 | - compatible: Should be "samsung,s2mps11-pmic" or "samsung,s2mps13-pmic" |
| 11 | or "samsung,s2mpu02-pmic". | 11 | or "samsung,s2mps14-pmic" or "samsung,s2mpu02-pmic". |
| 12 | - reg: Specifies the I2C slave address of the pmic block. It should be 0x66. | 12 | - reg: Specifies the I2C slave address of the pmic block. It should be 0x66. |
| 13 | 13 | ||
| 14 | Optional properties: | 14 | Optional properties: |
| @@ -17,8 +17,8 @@ Optional properties: | |||
| 17 | - interrupts: Interrupt specifiers for interrupt sources. | 17 | - interrupts: Interrupt specifiers for interrupt sources. |
| 18 | 18 | ||
| 19 | Optional nodes: | 19 | Optional nodes: |
| 20 | - clocks: s2mps11 and s5m8767 provide three(AP/CP/BT) buffered 32.768 KHz | 20 | - clocks: s2mps11, s2mps13 and s5m8767 provide three(AP/CP/BT) buffered 32.768 |
| 21 | outputs, so to register these as clocks with common clock framework | 21 | KHz outputs, so to register these as clocks with common clock framework |
| 22 | instantiate a sub-node named "clocks". It uses the common clock binding | 22 | instantiate a sub-node named "clocks". It uses the common clock binding |
| 23 | documented in : | 23 | documented in : |
| 24 | [Documentation/devicetree/bindings/clock/clock-bindings.txt] | 24 | [Documentation/devicetree/bindings/clock/clock-bindings.txt] |
| @@ -30,12 +30,12 @@ Optional nodes: | |||
| 30 | the clock which they consume. | 30 | the clock which they consume. |
| 31 | Clock ID Devices | 31 | Clock ID Devices |
| 32 | ---------------------------------------------------------- | 32 | ---------------------------------------------------------- |
| 33 | 32KhzAP 0 S2MPS11, S2MPS14, S5M8767 | 33 | 32KhzAP 0 S2MPS11, S2MPS13, S2MPS14, S5M8767 |
| 34 | 32KhzCP 1 S2MPS11, S5M8767 | 34 | 32KhzCP 1 S2MPS11, S2MPS13, S5M8767 |
| 35 | 32KhzBT 2 S2MPS11, S2MPS14, S5M8767 | 35 | 32KhzBT 2 S2MPS11, S2MPS13, S2MPS14, S5M8767 |
| 36 | 36 | ||
| 37 | - compatible: Should be one of: "samsung,s2mps11-clk", "samsung,s2mps14-clk", | 37 | - compatible: Should be one of: "samsung,s2mps11-clk", "samsung,s2mps13-clk", |
| 38 | "samsung,s5m8767-clk" | 38 | "samsung,s2mps14-clk", "samsung,s5m8767-clk" |
| 39 | 39 | ||
| 40 | - regulators: The regulators of s2mps11 that have to be instantiated should be | 40 | - regulators: The regulators of s2mps11 that have to be instantiated should be |
| 41 | included in a sub-node named 'regulators'. Regulator nodes included in this | 41 | included in a sub-node named 'regulators'. Regulator nodes included in this |
| @@ -81,12 +81,14 @@ as per the datasheet of s2mps11. | |||
| 81 | - LDOn | 81 | - LDOn |
| 82 | - valid values for n are: | 82 | - valid values for n are: |
| 83 | - S2MPS11: 1 to 38 | 83 | - S2MPS11: 1 to 38 |
| 84 | - S2MPS13: 1 to 40 | ||
| 84 | - S2MPS14: 1 to 25 | 85 | - S2MPS14: 1 to 25 |
| 85 | - S2MPU02: 1 to 28 | 86 | - S2MPU02: 1 to 28 |
| 86 | - Example: LDO1, LDO2, LDO28 | 87 | - Example: LDO1, LDO2, LDO28 |
| 87 | - BUCKn | 88 | - BUCKn |
| 88 | - valid values for n are: | 89 | - valid values for n are: |
| 89 | - S2MPS11: 1 to 10 | 90 | - S2MPS11: 1 to 10 |
| 91 | - S2MPS13: 1 to 10 | ||
| 90 | - S2MPS14: 1 to 5 | 92 | - S2MPS14: 1 to 5 |
| 91 | - S2MPU02: 1 to 7 | 93 | - S2MPU02: 1 to 7 |
| 92 | - Example: BUCK1, BUCK2, BUCK9 | 94 | - Example: BUCK1, BUCK2, BUCK9 |
diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index b7797fb12e12..7bb13af8e214 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <linux/clk-provider.h> | 23 | #include <linux/clk-provider.h> |
| 24 | #include <linux/platform_device.h> | 24 | #include <linux/platform_device.h> |
| 25 | #include <linux/mfd/samsung/s2mps11.h> | 25 | #include <linux/mfd/samsung/s2mps11.h> |
| 26 | #include <linux/mfd/samsung/s2mps13.h> | ||
| 26 | #include <linux/mfd/samsung/s2mps14.h> | 27 | #include <linux/mfd/samsung/s2mps14.h> |
| 27 | #include <linux/mfd/samsung/s5m8767.h> | 28 | #include <linux/mfd/samsung/s5m8767.h> |
| 28 | #include <linux/mfd/samsung/core.h> | 29 | #include <linux/mfd/samsung/core.h> |
| @@ -120,6 +121,24 @@ static struct clk_init_data s2mps11_clks_init[S2MPS11_CLKS_NUM] = { | |||
| 120 | }, | 121 | }, |
| 121 | }; | 122 | }; |
| 122 | 123 | ||
| 124 | static struct clk_init_data s2mps13_clks_init[S2MPS11_CLKS_NUM] = { | ||
| 125 | [S2MPS11_CLK_AP] = { | ||
| 126 | .name = "s2mps13_ap", | ||
| 127 | .ops = &s2mps11_clk_ops, | ||
| 128 | .flags = CLK_IS_ROOT, | ||
| 129 | }, | ||
| 130 | [S2MPS11_CLK_CP] = { | ||
| 131 | .name = "s2mps13_cp", | ||
| 132 | .ops = &s2mps11_clk_ops, | ||
| 133 | .flags = CLK_IS_ROOT, | ||
| 134 | }, | ||
| 135 | [S2MPS11_CLK_BT] = { | ||
| 136 | .name = "s2mps13_bt", | ||
| 137 | .ops = &s2mps11_clk_ops, | ||
| 138 | .flags = CLK_IS_ROOT, | ||
| 139 | }, | ||
| 140 | }; | ||
| 141 | |||
| 123 | static struct clk_init_data s2mps14_clks_init[S2MPS11_CLKS_NUM] = { | 142 | static struct clk_init_data s2mps14_clks_init[S2MPS11_CLKS_NUM] = { |
| 124 | [S2MPS11_CLK_AP] = { | 143 | [S2MPS11_CLK_AP] = { |
| 125 | .name = "s2mps14_ap", | 144 | .name = "s2mps14_ap", |
| @@ -184,6 +203,10 @@ static int s2mps11_clk_probe(struct platform_device *pdev) | |||
| 184 | s2mps11_reg = S2MPS11_REG_RTC_CTRL; | 203 | s2mps11_reg = S2MPS11_REG_RTC_CTRL; |
| 185 | clks_init = s2mps11_clks_init; | 204 | clks_init = s2mps11_clks_init; |
| 186 | break; | 205 | break; |
| 206 | case S2MPS13X: | ||
| 207 | s2mps11_reg = S2MPS13_REG_RTCCTRL; | ||
| 208 | clks_init = s2mps13_clks_init; | ||
| 209 | break; | ||
| 187 | case S2MPS14X: | 210 | case S2MPS14X: |
| 188 | s2mps11_reg = S2MPS14_REG_RTCCTRL; | 211 | s2mps11_reg = S2MPS14_REG_RTCCTRL; |
| 189 | clks_init = s2mps14_clks_init; | 212 | clks_init = s2mps14_clks_init; |
| @@ -279,6 +302,7 @@ static int s2mps11_clk_remove(struct platform_device *pdev) | |||
| 279 | 302 | ||
| 280 | static const struct platform_device_id s2mps11_clk_id[] = { | 303 | static const struct platform_device_id s2mps11_clk_id[] = { |
| 281 | { "s2mps11-clk", S2MPS11X}, | 304 | { "s2mps11-clk", S2MPS11X}, |
| 305 | { "s2mps13-clk", S2MPS13X}, | ||
| 282 | { "s2mps14-clk", S2MPS14X}, | 306 | { "s2mps14-clk", S2MPS14X}, |
| 283 | { "s5m8767-clk", S5M8767X}, | 307 | { "s5m8767-clk", S5M8767X}, |
| 284 | { }, | 308 | { }, |
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 0959ca9b6b27..23dfd5f59b39 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig | |||
| @@ -905,4 +905,16 @@ config GPIO_VIPERBOARD | |||
| 905 | River Tech's viperboard.h for detailed meaning | 905 | River Tech's viperboard.h for detailed meaning |
| 906 | of the module parameters. | 906 | of the module parameters. |
| 907 | 907 | ||
| 908 | config GPIO_DLN2 | ||
| 909 | tristate "Diolan DLN2 GPIO support" | ||
| 910 | depends on MFD_DLN2 | ||
| 911 | select GPIOLIB_IRQCHIP | ||
| 912 | |||
| 913 | help | ||
| 914 | Select this option to enable GPIO driver for the Diolan DLN2 | ||
| 915 | board. | ||
| 916 | |||
| 917 | This driver can also be built as a module. If so, the module | ||
| 918 | will be called gpio-dln2. | ||
| 919 | |||
| 908 | endif | 920 | endif |
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index e5d346cf3b6e..e60677b8ccb4 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile | |||
| @@ -26,6 +26,7 @@ obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o | |||
| 26 | obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o | 26 | obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o |
| 27 | obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o | 27 | obj-$(CONFIG_GPIO_DA9055) += gpio-da9055.o |
| 28 | obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o | 28 | obj-$(CONFIG_GPIO_DAVINCI) += gpio-davinci.o |
| 29 | obj-$(CONFIG_GPIO_DLN2) += gpio-dln2.o | ||
| 29 | obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o | 30 | obj-$(CONFIG_GPIO_DWAPB) += gpio-dwapb.o |
| 30 | obj-$(CONFIG_GPIO_EM) += gpio-em.o | 31 | obj-$(CONFIG_GPIO_EM) += gpio-em.o |
| 31 | obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o | 32 | obj-$(CONFIG_GPIO_EP93XX) += gpio-ep93xx.o |
diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c new file mode 100644 index 000000000000..978b51eae2ec --- /dev/null +++ b/drivers/gpio/gpio-dln2.c | |||
| @@ -0,0 +1,553 @@ | |||
| 1 | /* | ||
| 2 | * Driver for the Diolan DLN-2 USB-GPIO adapter | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Intel Corporation | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License as | ||
| 8 | * published by the Free Software Foundation, version 2. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/slab.h> | ||
| 14 | #include <linux/types.h> | ||
| 15 | #include <linux/irqdomain.h> | ||
| 16 | #include <linux/irq.h> | ||
| 17 | #include <linux/irqchip/chained_irq.h> | ||
| 18 | #include <linux/gpio.h> | ||
| 19 | #include <linux/gpio/driver.h> | ||
| 20 | #include <linux/platform_device.h> | ||
| 21 | #include <linux/mfd/dln2.h> | ||
| 22 | |||
| 23 | #define DLN2_GPIO_ID 0x01 | ||
| 24 | |||
| 25 | #define DLN2_GPIO_GET_PIN_COUNT DLN2_CMD(0x01, DLN2_GPIO_ID) | ||
| 26 | #define DLN2_GPIO_SET_DEBOUNCE DLN2_CMD(0x04, DLN2_GPIO_ID) | ||
| 27 | #define DLN2_GPIO_GET_DEBOUNCE DLN2_CMD(0x05, DLN2_GPIO_ID) | ||
| 28 | #define DLN2_GPIO_PORT_GET_VAL DLN2_CMD(0x06, DLN2_GPIO_ID) | ||
| 29 | #define DLN2_GPIO_PIN_GET_VAL DLN2_CMD(0x0B, DLN2_GPIO_ID) | ||
| 30 | #define DLN2_GPIO_PIN_SET_OUT_VAL DLN2_CMD(0x0C, DLN2_GPIO_ID) | ||
| 31 | #define DLN2_GPIO_PIN_GET_OUT_VAL DLN2_CMD(0x0D, DLN2_GPIO_ID) | ||
| 32 | #define DLN2_GPIO_CONDITION_MET_EV DLN2_CMD(0x0F, DLN2_GPIO_ID) | ||
| 33 | #define DLN2_GPIO_PIN_ENABLE DLN2_CMD(0x10, DLN2_GPIO_ID) | ||
| 34 | #define DLN2_GPIO_PIN_DISABLE DLN2_CMD(0x11, DLN2_GPIO_ID) | ||
| 35 | #define DLN2_GPIO_PIN_SET_DIRECTION DLN2_CMD(0x13, DLN2_GPIO_ID) | ||
| 36 | #define DLN2_GPIO_PIN_GET_DIRECTION DLN2_CMD(0x14, DLN2_GPIO_ID) | ||
| 37 | #define DLN2_GPIO_PIN_SET_EVENT_CFG DLN2_CMD(0x1E, DLN2_GPIO_ID) | ||
| 38 | #define DLN2_GPIO_PIN_GET_EVENT_CFG DLN2_CMD(0x1F, DLN2_GPIO_ID) | ||
| 39 | |||
| 40 | #define DLN2_GPIO_EVENT_NONE 0 | ||
| 41 | #define DLN2_GPIO_EVENT_CHANGE 1 | ||
| 42 | #define DLN2_GPIO_EVENT_LVL_HIGH 2 | ||
| 43 | #define DLN2_GPIO_EVENT_LVL_LOW 3 | ||
| 44 | #define DLN2_GPIO_EVENT_CHANGE_RISING 0x11 | ||
| 45 | #define DLN2_GPIO_EVENT_CHANGE_FALLING 0x21 | ||
| 46 | #define DLN2_GPIO_EVENT_MASK 0x0F | ||
| 47 | |||
| 48 | #define DLN2_GPIO_MAX_PINS 32 | ||
| 49 | |||
| 50 | struct dln2_irq_work { | ||
| 51 | struct work_struct work; | ||
| 52 | struct dln2_gpio *dln2; | ||
| 53 | int pin; | ||
| 54 | int type; | ||
| 55 | }; | ||
| 56 | |||
| 57 | struct dln2_gpio { | ||
| 58 | struct platform_device *pdev; | ||
| 59 | struct gpio_chip gpio; | ||
| 60 | |||
| 61 | /* | ||
| 62 | * Cache pin direction to save us one transfer, since the hardware has | ||
| 63 | * separate commands to read the in and out values. | ||
| 64 | */ | ||
| 65 | DECLARE_BITMAP(output_enabled, DLN2_GPIO_MAX_PINS); | ||
| 66 | |||
| 67 | DECLARE_BITMAP(irqs_masked, DLN2_GPIO_MAX_PINS); | ||
| 68 | DECLARE_BITMAP(irqs_enabled, DLN2_GPIO_MAX_PINS); | ||
| 69 | DECLARE_BITMAP(irqs_pending, DLN2_GPIO_MAX_PINS); | ||
| 70 | struct dln2_irq_work *irq_work; | ||
| 71 | }; | ||
| 72 | |||
| 73 | struct dln2_gpio_pin { | ||
| 74 | __le16 pin; | ||
| 75 | }; | ||
| 76 | |||
| 77 | struct dln2_gpio_pin_val { | ||
| 78 | __le16 pin __packed; | ||
| 79 | u8 value; | ||
| 80 | }; | ||
| 81 | |||
| 82 | static int dln2_gpio_get_pin_count(struct platform_device *pdev) | ||
| 83 | { | ||
| 84 | int ret; | ||
| 85 | __le16 count; | ||
| 86 | int len = sizeof(count); | ||
| 87 | |||
| 88 | ret = dln2_transfer_rx(pdev, DLN2_GPIO_GET_PIN_COUNT, &count, &len); | ||
| 89 | if (ret < 0) | ||
| 90 | return ret; | ||
| 91 | if (len < sizeof(count)) | ||
| 92 | return -EPROTO; | ||
| 93 | |||
| 94 | return le16_to_cpu(count); | ||
| 95 | } | ||
| 96 | |||
| 97 | static int dln2_gpio_pin_cmd(struct dln2_gpio *dln2, int cmd, unsigned pin) | ||
| 98 | { | ||
| 99 | struct dln2_gpio_pin req = { | ||
| 100 | .pin = cpu_to_le16(pin), | ||
| 101 | }; | ||
| 102 | |||
| 103 | return dln2_transfer_tx(dln2->pdev, cmd, &req, sizeof(req)); | ||
| 104 | } | ||
| 105 | |||
| 106 | static int dln2_gpio_pin_val(struct dln2_gpio *dln2, int cmd, unsigned int pin) | ||
| 107 | { | ||
| 108 | int ret; | ||
| 109 | struct dln2_gpio_pin req = { | ||
| 110 | .pin = cpu_to_le16(pin), | ||
| 111 | }; | ||
| 112 | struct dln2_gpio_pin_val rsp; | ||
| 113 | int len = sizeof(rsp); | ||
| 114 | |||
| 115 | ret = dln2_transfer(dln2->pdev, cmd, &req, sizeof(req), &rsp, &len); | ||
| 116 | if (ret < 0) | ||
| 117 | return ret; | ||
| 118 | if (len < sizeof(rsp) || req.pin != rsp.pin) | ||
| 119 | return -EPROTO; | ||
| 120 | |||
| 121 | return rsp.value; | ||
| 122 | } | ||
| 123 | |||
| 124 | static int dln2_gpio_pin_get_in_val(struct dln2_gpio *dln2, unsigned int pin) | ||
| 125 | { | ||
| 126 | int ret; | ||
| 127 | |||
| 128 | ret = dln2_gpio_pin_val(dln2, DLN2_GPIO_PIN_GET_VAL, pin); | ||
| 129 | if (ret < 0) | ||
| 130 | return ret; | ||
| 131 | return !!ret; | ||
| 132 | } | ||
| 133 | |||
| 134 | static int dln2_gpio_pin_get_out_val(struct dln2_gpio *dln2, unsigned int pin) | ||
| 135 | { | ||
| 136 | int ret; | ||
| 137 | |||
| 138 | ret = dln2_gpio_pin_val(dln2, DLN2_GPIO_PIN_GET_OUT_VAL, pin); | ||
| 139 | if (ret < 0) | ||
| 140 | return ret; | ||
| 141 | return !!ret; | ||
| 142 | } | ||
| 143 | |||
| 144 | static void dln2_gpio_pin_set_out_val(struct dln2_gpio *dln2, | ||
| 145 | unsigned int pin, int value) | ||
| 146 | { | ||
| 147 | struct dln2_gpio_pin_val req = { | ||
| 148 | .pin = cpu_to_le16(pin), | ||
| 149 | .value = value, | ||
| 150 | }; | ||
| 151 | |||
| 152 | dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_OUT_VAL, &req, | ||
| 153 | sizeof(req)); | ||
| 154 | } | ||
| 155 | |||
| 156 | #define DLN2_GPIO_DIRECTION_IN 0 | ||
| 157 | #define DLN2_GPIO_DIRECTION_OUT 1 | ||
| 158 | |||
| 159 | static int dln2_gpio_request(struct gpio_chip *chip, unsigned offset) | ||
| 160 | { | ||
| 161 | struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); | ||
| 162 | struct dln2_gpio_pin req = { | ||
| 163 | .pin = cpu_to_le16(offset), | ||
| 164 | }; | ||
| 165 | struct dln2_gpio_pin_val rsp; | ||
| 166 | int len = sizeof(rsp); | ||
| 167 | int ret; | ||
| 168 | |||
| 169 | ret = dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_ENABLE, offset); | ||
| 170 | if (ret < 0) | ||
| 171 | return ret; | ||
| 172 | |||
| 173 | /* cache the pin direction */ | ||
| 174 | ret = dln2_transfer(dln2->pdev, DLN2_GPIO_PIN_GET_DIRECTION, | ||
| 175 | &req, sizeof(req), &rsp, &len); | ||
| 176 | if (ret < 0) | ||
| 177 | return ret; | ||
| 178 | if (len < sizeof(rsp) || req.pin != rsp.pin) { | ||
| 179 | ret = -EPROTO; | ||
| 180 | goto out_disable; | ||
| 181 | } | ||
| 182 | |||
| 183 | switch (rsp.value) { | ||
| 184 | case DLN2_GPIO_DIRECTION_IN: | ||
| 185 | clear_bit(offset, dln2->output_enabled); | ||
| 186 | return 0; | ||
| 187 | case DLN2_GPIO_DIRECTION_OUT: | ||
| 188 | set_bit(offset, dln2->output_enabled); | ||
| 189 | return 0; | ||
| 190 | default: | ||
| 191 | ret = -EPROTO; | ||
| 192 | goto out_disable; | ||
| 193 | } | ||
| 194 | |||
| 195 | out_disable: | ||
| 196 | dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_DISABLE, offset); | ||
| 197 | return ret; | ||
| 198 | } | ||
| 199 | |||
| 200 | static void dln2_gpio_free(struct gpio_chip *chip, unsigned offset) | ||
| 201 | { | ||
| 202 | struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); | ||
| 203 | |||
| 204 | dln2_gpio_pin_cmd(dln2, DLN2_GPIO_PIN_DISABLE, offset); | ||
| 205 | } | ||
| 206 | |||
| 207 | static int dln2_gpio_get_direction(struct gpio_chip *chip, unsigned offset) | ||
| 208 | { | ||
| 209 | struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); | ||
| 210 | |||
| 211 | if (test_bit(offset, dln2->output_enabled)) | ||
| 212 | return GPIOF_DIR_OUT; | ||
| 213 | |||
| 214 | return GPIOF_DIR_IN; | ||
| 215 | } | ||
| 216 | |||
| 217 | static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset) | ||
| 218 | { | ||
| 219 | struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); | ||
| 220 | int dir; | ||
| 221 | |||
| 222 | dir = dln2_gpio_get_direction(chip, offset); | ||
| 223 | if (dir < 0) | ||
| 224 | return dir; | ||
| 225 | |||
| 226 | if (dir == GPIOF_DIR_IN) | ||
| 227 | return dln2_gpio_pin_get_in_val(dln2, offset); | ||
| 228 | |||
| 229 | return dln2_gpio_pin_get_out_val(dln2, offset); | ||
| 230 | } | ||
| 231 | |||
| 232 | static void dln2_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | ||
| 233 | { | ||
| 234 | struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); | ||
| 235 | |||
| 236 | dln2_gpio_pin_set_out_val(dln2, offset, value); | ||
| 237 | } | ||
| 238 | |||
| 239 | static int dln2_gpio_set_direction(struct gpio_chip *chip, unsigned offset, | ||
| 240 | unsigned dir) | ||
| 241 | { | ||
| 242 | struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); | ||
| 243 | struct dln2_gpio_pin_val req = { | ||
| 244 | .pin = cpu_to_le16(offset), | ||
| 245 | .value = dir, | ||
| 246 | }; | ||
| 247 | int ret; | ||
| 248 | |||
| 249 | ret = dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_DIRECTION, | ||
| 250 | &req, sizeof(req)); | ||
| 251 | if (ret < 0) | ||
| 252 | return ret; | ||
| 253 | |||
| 254 | if (dir == DLN2_GPIO_DIRECTION_OUT) | ||
| 255 | set_bit(offset, dln2->output_enabled); | ||
| 256 | else | ||
| 257 | clear_bit(offset, dln2->output_enabled); | ||
| 258 | |||
| 259 | return ret; | ||
| 260 | } | ||
| 261 | |||
| 262 | static int dln2_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | ||
| 263 | { | ||
| 264 | return dln2_gpio_set_direction(chip, offset, DLN2_GPIO_DIRECTION_IN); | ||
| 265 | } | ||
| 266 | |||
| 267 | static int dln2_gpio_direction_output(struct gpio_chip *chip, unsigned offset, | ||
| 268 | int value) | ||
| 269 | { | ||
| 270 | return dln2_gpio_set_direction(chip, offset, DLN2_GPIO_DIRECTION_OUT); | ||
| 271 | } | ||
| 272 | |||
| 273 | static int dln2_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, | ||
| 274 | unsigned debounce) | ||
| 275 | { | ||
| 276 | struct dln2_gpio *dln2 = container_of(chip, struct dln2_gpio, gpio); | ||
| 277 | __le32 duration = cpu_to_le32(debounce); | ||
| 278 | |||
| 279 | return dln2_transfer_tx(dln2->pdev, DLN2_GPIO_SET_DEBOUNCE, | ||
| 280 | &duration, sizeof(duration)); | ||
| 281 | } | ||
| 282 | |||
| 283 | static int dln2_gpio_set_event_cfg(struct dln2_gpio *dln2, unsigned pin, | ||
| 284 | unsigned type, unsigned period) | ||
| 285 | { | ||
| 286 | struct { | ||
| 287 | __le16 pin; | ||
| 288 | u8 type; | ||
| 289 | __le16 period; | ||
| 290 | } __packed req = { | ||
| 291 | .pin = cpu_to_le16(pin), | ||
| 292 | .type = type, | ||
| 293 | .period = cpu_to_le16(period), | ||
| 294 | }; | ||
| 295 | |||
| 296 | return dln2_transfer_tx(dln2->pdev, DLN2_GPIO_PIN_SET_EVENT_CFG, | ||
| 297 | &req, sizeof(req)); | ||
| 298 | } | ||
| 299 | |||
| 300 | static void dln2_irq_work(struct work_struct *w) | ||
| 301 | { | ||
| 302 | struct dln2_irq_work *iw = container_of(w, struct dln2_irq_work, work); | ||
| 303 | struct dln2_gpio *dln2 = iw->dln2; | ||
| 304 | u8 type = iw->type & DLN2_GPIO_EVENT_MASK; | ||
| 305 | |||
| 306 | if (test_bit(iw->pin, dln2->irqs_enabled)) | ||
| 307 | dln2_gpio_set_event_cfg(dln2, iw->pin, type, 0); | ||
| 308 | else | ||
| 309 | dln2_gpio_set_event_cfg(dln2, iw->pin, DLN2_GPIO_EVENT_NONE, 0); | ||
| 310 | } | ||
| 311 | |||
| 312 | static void dln2_irq_enable(struct irq_data *irqd) | ||
| 313 | { | ||
| 314 | struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); | ||
| 315 | struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); | ||
| 316 | int pin = irqd_to_hwirq(irqd); | ||
| 317 | |||
| 318 | set_bit(pin, dln2->irqs_enabled); | ||
| 319 | schedule_work(&dln2->irq_work[pin].work); | ||
| 320 | } | ||
| 321 | |||
| 322 | static void dln2_irq_disable(struct irq_data *irqd) | ||
| 323 | { | ||
| 324 | struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); | ||
| 325 | struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); | ||
| 326 | int pin = irqd_to_hwirq(irqd); | ||
| 327 | |||
| 328 | clear_bit(pin, dln2->irqs_enabled); | ||
| 329 | schedule_work(&dln2->irq_work[pin].work); | ||
| 330 | } | ||
| 331 | |||
| 332 | static void dln2_irq_mask(struct irq_data *irqd) | ||
| 333 | { | ||
| 334 | struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); | ||
| 335 | struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); | ||
| 336 | int pin = irqd_to_hwirq(irqd); | ||
| 337 | |||
| 338 | set_bit(pin, dln2->irqs_masked); | ||
| 339 | } | ||
| 340 | |||
| 341 | static void dln2_irq_unmask(struct irq_data *irqd) | ||
| 342 | { | ||
| 343 | struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); | ||
| 344 | struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); | ||
| 345 | struct device *dev = dln2->gpio.dev; | ||
| 346 | int pin = irqd_to_hwirq(irqd); | ||
| 347 | |||
| 348 | if (test_and_clear_bit(pin, dln2->irqs_pending)) { | ||
| 349 | int irq; | ||
| 350 | |||
| 351 | irq = irq_find_mapping(dln2->gpio.irqdomain, pin); | ||
| 352 | if (!irq) { | ||
| 353 | dev_err(dev, "pin %d not mapped to IRQ\n", pin); | ||
| 354 | return; | ||
| 355 | } | ||
| 356 | |||
| 357 | generic_handle_irq(irq); | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | static int dln2_irq_set_type(struct irq_data *irqd, unsigned type) | ||
| 362 | { | ||
| 363 | struct gpio_chip *gc = irq_data_get_irq_chip_data(irqd); | ||
| 364 | struct dln2_gpio *dln2 = container_of(gc, struct dln2_gpio, gpio); | ||
| 365 | int pin = irqd_to_hwirq(irqd); | ||
| 366 | |||
| 367 | switch (type) { | ||
| 368 | case IRQ_TYPE_LEVEL_HIGH: | ||
| 369 | dln2->irq_work[pin].type = DLN2_GPIO_EVENT_LVL_HIGH; | ||
| 370 | break; | ||
| 371 | case IRQ_TYPE_LEVEL_LOW: | ||
| 372 | dln2->irq_work[pin].type = DLN2_GPIO_EVENT_LVL_LOW; | ||
| 373 | break; | ||
| 374 | case IRQ_TYPE_EDGE_BOTH: | ||
| 375 | dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE; | ||
| 376 | break; | ||
| 377 | case IRQ_TYPE_EDGE_RISING: | ||
| 378 | dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE_RISING; | ||
| 379 | break; | ||
| 380 | case IRQ_TYPE_EDGE_FALLING: | ||
| 381 | dln2->irq_work[pin].type = DLN2_GPIO_EVENT_CHANGE_FALLING; | ||
| 382 | break; | ||
| 383 | default: | ||
| 384 | return -EINVAL; | ||
| 385 | } | ||
| 386 | |||
| 387 | return 0; | ||
| 388 | } | ||
| 389 | |||
| 390 | static struct irq_chip dln2_gpio_irqchip = { | ||
| 391 | .name = "dln2-irq", | ||
| 392 | .irq_enable = dln2_irq_enable, | ||
| 393 | .irq_disable = dln2_irq_disable, | ||
| 394 | .irq_mask = dln2_irq_mask, | ||
| 395 | .irq_unmask = dln2_irq_unmask, | ||
| 396 | .irq_set_type = dln2_irq_set_type, | ||
| 397 | }; | ||
| 398 | |||
| 399 | static void dln2_gpio_event(struct platform_device *pdev, u16 echo, | ||
| 400 | const void *data, int len) | ||
| 401 | { | ||
| 402 | int pin, irq; | ||
| 403 | const struct { | ||
| 404 | __le16 count; | ||
| 405 | __u8 type; | ||
| 406 | __le16 pin; | ||
| 407 | __u8 value; | ||
| 408 | } __packed *event = data; | ||
| 409 | struct dln2_gpio *dln2 = platform_get_drvdata(pdev); | ||
| 410 | |||
| 411 | if (len < sizeof(*event)) { | ||
| 412 | dev_err(dln2->gpio.dev, "short event message\n"); | ||
| 413 | return; | ||
| 414 | } | ||
| 415 | |||
| 416 | pin = le16_to_cpu(event->pin); | ||
| 417 | if (pin >= dln2->gpio.ngpio) { | ||
| 418 | dev_err(dln2->gpio.dev, "out of bounds pin %d\n", pin); | ||
| 419 | return; | ||
| 420 | } | ||
| 421 | |||
| 422 | irq = irq_find_mapping(dln2->gpio.irqdomain, pin); | ||
| 423 | if (!irq) { | ||
| 424 | dev_err(dln2->gpio.dev, "pin %d not mapped to IRQ\n", pin); | ||
| 425 | return; | ||
| 426 | } | ||
| 427 | |||
| 428 | if (!test_bit(pin, dln2->irqs_enabled)) | ||
| 429 | return; | ||
| 430 | if (test_bit(pin, dln2->irqs_masked)) { | ||
| 431 | set_bit(pin, dln2->irqs_pending); | ||
| 432 | return; | ||
| 433 | } | ||
| 434 | |||
| 435 | switch (dln2->irq_work[pin].type) { | ||
| 436 | case DLN2_GPIO_EVENT_CHANGE_RISING: | ||
| 437 | if (event->value) | ||
| 438 | generic_handle_irq(irq); | ||
| 439 | break; | ||
| 440 | case DLN2_GPIO_EVENT_CHANGE_FALLING: | ||
| 441 | if (!event->value) | ||
| 442 | generic_handle_irq(irq); | ||
| 443 | break; | ||
| 444 | default: | ||
| 445 | generic_handle_irq(irq); | ||
| 446 | } | ||
| 447 | } | ||
| 448 | |||
| 449 | static int dln2_gpio_probe(struct platform_device *pdev) | ||
| 450 | { | ||
| 451 | struct dln2_gpio *dln2; | ||
| 452 | struct device *dev = &pdev->dev; | ||
| 453 | int pins; | ||
| 454 | int i, ret; | ||
| 455 | |||
| 456 | pins = dln2_gpio_get_pin_count(pdev); | ||
| 457 | if (pins < 0) { | ||
| 458 | dev_err(dev, "failed to get pin count: %d\n", pins); | ||
| 459 | return pins; | ||
| 460 | } | ||
| 461 | if (pins > DLN2_GPIO_MAX_PINS) { | ||
| 462 | pins = DLN2_GPIO_MAX_PINS; | ||
| 463 | dev_warn(dev, "clamping pins to %d\n", DLN2_GPIO_MAX_PINS); | ||
| 464 | } | ||
| 465 | |||
| 466 | dln2 = devm_kzalloc(&pdev->dev, sizeof(*dln2), GFP_KERNEL); | ||
| 467 | if (!dln2) | ||
| 468 | return -ENOMEM; | ||
| 469 | |||
| 470 | dln2->irq_work = devm_kcalloc(&pdev->dev, pins, | ||
| 471 | sizeof(struct dln2_irq_work), GFP_KERNEL); | ||
| 472 | if (!dln2->irq_work) | ||
| 473 | return -ENOMEM; | ||
| 474 | for (i = 0; i < pins; i++) { | ||
| 475 | INIT_WORK(&dln2->irq_work[i].work, dln2_irq_work); | ||
| 476 | dln2->irq_work[i].pin = i; | ||
| 477 | dln2->irq_work[i].dln2 = dln2; | ||
| 478 | } | ||
| 479 | |||
| 480 | dln2->pdev = pdev; | ||
| 481 | |||
| 482 | dln2->gpio.label = "dln2"; | ||
| 483 | dln2->gpio.dev = dev; | ||
| 484 | dln2->gpio.owner = THIS_MODULE; | ||
| 485 | dln2->gpio.base = -1; | ||
| 486 | dln2->gpio.ngpio = pins; | ||
| 487 | dln2->gpio.exported = true; | ||
| 488 | dln2->gpio.can_sleep = true; | ||
| 489 | dln2->gpio.irq_not_threaded = true; | ||
| 490 | dln2->gpio.set = dln2_gpio_set; | ||
| 491 | dln2->gpio.get = dln2_gpio_get; | ||
| 492 | dln2->gpio.request = dln2_gpio_request; | ||
| 493 | dln2->gpio.free = dln2_gpio_free; | ||
| 494 | dln2->gpio.get_direction = dln2_gpio_get_direction; | ||
| 495 | dln2->gpio.direction_input = dln2_gpio_direction_input; | ||
| 496 | dln2->gpio.direction_output = dln2_gpio_direction_output; | ||
| 497 | dln2->gpio.set_debounce = dln2_gpio_set_debounce; | ||
| 498 | |||
| 499 | platform_set_drvdata(pdev, dln2); | ||
| 500 | |||
| 501 | ret = gpiochip_add(&dln2->gpio); | ||
| 502 | if (ret < 0) { | ||
| 503 | dev_err(dev, "failed to add gpio chip: %d\n", ret); | ||
| 504 | goto out; | ||
| 505 | } | ||
| 506 | |||
| 507 | ret = gpiochip_irqchip_add(&dln2->gpio, &dln2_gpio_irqchip, 0, | ||
| 508 | handle_simple_irq, IRQ_TYPE_NONE); | ||
| 509 | if (ret < 0) { | ||
| 510 | dev_err(dev, "failed to add irq chip: %d\n", ret); | ||
| 511 | goto out_gpiochip_remove; | ||
| 512 | } | ||
| 513 | |||
| 514 | ret = dln2_register_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV, | ||
| 515 | dln2_gpio_event); | ||
| 516 | if (ret) { | ||
| 517 | dev_err(dev, "failed to register event cb: %d\n", ret); | ||
| 518 | goto out_gpiochip_remove; | ||
| 519 | } | ||
| 520 | |||
| 521 | return 0; | ||
| 522 | |||
| 523 | out_gpiochip_remove: | ||
| 524 | gpiochip_remove(&dln2->gpio); | ||
| 525 | out: | ||
| 526 | return ret; | ||
| 527 | } | ||
| 528 | |||
| 529 | static int dln2_gpio_remove(struct platform_device *pdev) | ||
| 530 | { | ||
| 531 | struct dln2_gpio *dln2 = platform_get_drvdata(pdev); | ||
| 532 | int i; | ||
| 533 | |||
| 534 | dln2_unregister_event_cb(pdev, DLN2_GPIO_CONDITION_MET_EV); | ||
| 535 | for (i = 0; i < dln2->gpio.ngpio; i++) | ||
| 536 | flush_work(&dln2->irq_work[i].work); | ||
| 537 | gpiochip_remove(&dln2->gpio); | ||
| 538 | |||
| 539 | return 0; | ||
| 540 | } | ||
| 541 | |||
| 542 | static struct platform_driver dln2_gpio_driver = { | ||
| 543 | .driver.name = "dln2-gpio", | ||
| 544 | .probe = dln2_gpio_probe, | ||
| 545 | .remove = dln2_gpio_remove, | ||
| 546 | }; | ||
| 547 | |||
| 548 | module_platform_driver(dln2_gpio_driver); | ||
| 549 | |||
| 550 | MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com"); | ||
| 551 | MODULE_DESCRIPTION("Driver for the Diolan DLN2 GPIO interface"); | ||
| 552 | MODULE_LICENSE("GPL v2"); | ||
| 553 | MODULE_ALIAS("platform:dln2-gpio"); | ||
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 917c3585f45b..b4d135cc2f39 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig | |||
| @@ -881,6 +881,16 @@ config I2C_DIOLAN_U2C | |||
| 881 | This driver can also be built as a module. If so, the module | 881 | This driver can also be built as a module. If so, the module |
| 882 | will be called i2c-diolan-u2c. | 882 | will be called i2c-diolan-u2c. |
| 883 | 883 | ||
| 884 | config I2C_DLN2 | ||
| 885 | tristate "Diolan DLN-2 USB I2C adapter" | ||
| 886 | depends on MFD_DLN2 | ||
| 887 | help | ||
| 888 | If you say yes to this option, support will be included for Diolan | ||
| 889 | DLN2, a USB to I2C interface. | ||
| 890 | |||
| 891 | This driver can also be built as a module. If so, the module | ||
| 892 | will be called i2c-dln2. | ||
| 893 | |||
| 884 | config I2C_PARPORT | 894 | config I2C_PARPORT |
| 885 | tristate "Parallel port adapter" | 895 | tristate "Parallel port adapter" |
| 886 | depends on PARPORT | 896 | depends on PARPORT |
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 78d56c54ba2b..cdac7f15eab5 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile | |||
| @@ -87,6 +87,7 @@ obj-$(CONFIG_I2C_RCAR) += i2c-rcar.o | |||
| 87 | 87 | ||
| 88 | # External I2C/SMBus adapter drivers | 88 | # External I2C/SMBus adapter drivers |
| 89 | obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o | 89 | obj-$(CONFIG_I2C_DIOLAN_U2C) += i2c-diolan-u2c.o |
| 90 | obj-$(CONFIG_I2C_DLN2) += i2c-dln2.o | ||
| 90 | obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o | 91 | obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o |
| 91 | obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o | 92 | obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o |
| 92 | obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o | 93 | obj-$(CONFIG_I2C_ROBOTFUZZ_OSIF) += i2c-robotfuzz-osif.o |
diff --git a/drivers/i2c/busses/i2c-dln2.c b/drivers/i2c/busses/i2c-dln2.c new file mode 100644 index 000000000000..b3fb86af4cbb --- /dev/null +++ b/drivers/i2c/busses/i2c-dln2.c | |||
| @@ -0,0 +1,262 @@ | |||
| 1 | /* | ||
| 2 | * Driver for the Diolan DLN-2 USB-I2C adapter | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Intel Corporation | ||
| 5 | * | ||
| 6 | * Derived from: | ||
| 7 | * i2c-diolan-u2c.c | ||
| 8 | * Copyright (c) 2010-2011 Ericsson AB | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or | ||
| 11 | * modify it under the terms of the GNU General Public License as | ||
| 12 | * published by the Free Software Foundation, version 2. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/kernel.h> | ||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/types.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/i2c.h> | ||
| 20 | #include <linux/platform_device.h> | ||
| 21 | #include <linux/mfd/dln2.h> | ||
| 22 | |||
| 23 | #define DLN2_I2C_MODULE_ID 0x03 | ||
| 24 | #define DLN2_I2C_CMD(cmd) DLN2_CMD(cmd, DLN2_I2C_MODULE_ID) | ||
| 25 | |||
| 26 | /* I2C commands */ | ||
| 27 | #define DLN2_I2C_GET_PORT_COUNT DLN2_I2C_CMD(0x00) | ||
| 28 | #define DLN2_I2C_ENABLE DLN2_I2C_CMD(0x01) | ||
| 29 | #define DLN2_I2C_DISABLE DLN2_I2C_CMD(0x02) | ||
| 30 | #define DLN2_I2C_IS_ENABLED DLN2_I2C_CMD(0x03) | ||
| 31 | #define DLN2_I2C_WRITE DLN2_I2C_CMD(0x06) | ||
| 32 | #define DLN2_I2C_READ DLN2_I2C_CMD(0x07) | ||
| 33 | #define DLN2_I2C_SCAN_DEVICES DLN2_I2C_CMD(0x08) | ||
| 34 | #define DLN2_I2C_PULLUP_ENABLE DLN2_I2C_CMD(0x09) | ||
| 35 | #define DLN2_I2C_PULLUP_DISABLE DLN2_I2C_CMD(0x0A) | ||
| 36 | #define DLN2_I2C_PULLUP_IS_ENABLED DLN2_I2C_CMD(0x0B) | ||
| 37 | #define DLN2_I2C_TRANSFER DLN2_I2C_CMD(0x0C) | ||
| 38 | #define DLN2_I2C_SET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0D) | ||
| 39 | #define DLN2_I2C_GET_MAX_REPLY_COUNT DLN2_I2C_CMD(0x0E) | ||
| 40 | |||
| 41 | #define DLN2_I2C_MAX_XFER_SIZE 256 | ||
| 42 | #define DLN2_I2C_BUF_SIZE (DLN2_I2C_MAX_XFER_SIZE + 16) | ||
| 43 | |||
| 44 | struct dln2_i2c { | ||
| 45 | struct platform_device *pdev; | ||
| 46 | struct i2c_adapter adapter; | ||
| 47 | u8 port; | ||
| 48 | /* | ||
| 49 | * Buffer to hold the packet for read or write transfers. One is enough | ||
| 50 | * since we can't have multiple transfers in parallel on the i2c bus. | ||
| 51 | */ | ||
| 52 | void *buf; | ||
| 53 | }; | ||
| 54 | |||
| 55 | static int dln2_i2c_enable(struct dln2_i2c *dln2, bool enable) | ||
| 56 | { | ||
| 57 | u16 cmd; | ||
| 58 | struct { | ||
| 59 | u8 port; | ||
| 60 | } tx; | ||
| 61 | |||
| 62 | tx.port = dln2->port; | ||
| 63 | |||
| 64 | if (enable) | ||
| 65 | cmd = DLN2_I2C_ENABLE; | ||
| 66 | else | ||
| 67 | cmd = DLN2_I2C_DISABLE; | ||
| 68 | |||
| 69 | return dln2_transfer_tx(dln2->pdev, cmd, &tx, sizeof(tx)); | ||
| 70 | } | ||
| 71 | |||
| 72 | static int dln2_i2c_write(struct dln2_i2c *dln2, u8 addr, | ||
| 73 | u8 *data, u16 data_len) | ||
| 74 | { | ||
| 75 | int ret; | ||
| 76 | struct { | ||
| 77 | u8 port; | ||
| 78 | u8 addr; | ||
| 79 | u8 mem_addr_len; | ||
| 80 | __le32 mem_addr; | ||
| 81 | __le16 buf_len; | ||
| 82 | u8 buf[DLN2_I2C_MAX_XFER_SIZE]; | ||
| 83 | } __packed *tx = dln2->buf; | ||
| 84 | unsigned len; | ||
| 85 | |||
| 86 | BUILD_BUG_ON(sizeof(*tx) > DLN2_I2C_BUF_SIZE); | ||
| 87 | |||
| 88 | tx->port = dln2->port; | ||
| 89 | tx->addr = addr; | ||
| 90 | tx->mem_addr_len = 0; | ||
| 91 | tx->mem_addr = 0; | ||
| 92 | tx->buf_len = cpu_to_le16(data_len); | ||
| 93 | memcpy(tx->buf, data, data_len); | ||
| 94 | |||
| 95 | len = sizeof(*tx) + data_len - DLN2_I2C_MAX_XFER_SIZE; | ||
| 96 | ret = dln2_transfer_tx(dln2->pdev, DLN2_I2C_WRITE, tx, len); | ||
| 97 | if (ret < 0) | ||
| 98 | return ret; | ||
| 99 | |||
| 100 | return data_len; | ||
| 101 | } | ||
| 102 | |||
| 103 | static int dln2_i2c_read(struct dln2_i2c *dln2, u16 addr, u8 *data, | ||
| 104 | u16 data_len) | ||
| 105 | { | ||
| 106 | int ret; | ||
| 107 | struct { | ||
| 108 | u8 port; | ||
| 109 | u8 addr; | ||
| 110 | u8 mem_addr_len; | ||
| 111 | __le32 mem_addr; | ||
| 112 | __le16 buf_len; | ||
| 113 | } __packed tx; | ||
| 114 | struct { | ||
| 115 | __le16 buf_len; | ||
| 116 | u8 buf[DLN2_I2C_MAX_XFER_SIZE]; | ||
| 117 | } __packed *rx = dln2->buf; | ||
| 118 | unsigned rx_len = sizeof(*rx); | ||
| 119 | |||
| 120 | BUILD_BUG_ON(sizeof(*rx) > DLN2_I2C_BUF_SIZE); | ||
| 121 | |||
| 122 | tx.port = dln2->port; | ||
| 123 | tx.addr = addr; | ||
| 124 | tx.mem_addr_len = 0; | ||
| 125 | tx.mem_addr = 0; | ||
| 126 | tx.buf_len = cpu_to_le16(data_len); | ||
| 127 | |||
| 128 | ret = dln2_transfer(dln2->pdev, DLN2_I2C_READ, &tx, sizeof(tx), | ||
| 129 | rx, &rx_len); | ||
| 130 | if (ret < 0) | ||
| 131 | return ret; | ||
| 132 | if (rx_len < sizeof(rx->buf_len) + data_len) | ||
| 133 | return -EPROTO; | ||
| 134 | if (le16_to_cpu(rx->buf_len) != data_len) | ||
| 135 | return -EPROTO; | ||
| 136 | |||
| 137 | memcpy(data, rx->buf, data_len); | ||
| 138 | |||
| 139 | return data_len; | ||
| 140 | } | ||
| 141 | |||
| 142 | static int dln2_i2c_xfer(struct i2c_adapter *adapter, | ||
| 143 | struct i2c_msg *msgs, int num) | ||
| 144 | { | ||
| 145 | struct dln2_i2c *dln2 = i2c_get_adapdata(adapter); | ||
| 146 | struct i2c_msg *pmsg; | ||
| 147 | struct device *dev = &dln2->adapter.dev; | ||
| 148 | int i; | ||
| 149 | |||
| 150 | for (i = 0; i < num; i++) { | ||
| 151 | int ret; | ||
| 152 | |||
| 153 | pmsg = &msgs[i]; | ||
| 154 | |||
| 155 | if (pmsg->len > DLN2_I2C_MAX_XFER_SIZE) { | ||
| 156 | dev_warn(dev, "maximum transfer size exceeded\n"); | ||
| 157 | return -EOPNOTSUPP; | ||
| 158 | } | ||
| 159 | |||
| 160 | if (pmsg->flags & I2C_M_RD) { | ||
| 161 | ret = dln2_i2c_read(dln2, pmsg->addr, pmsg->buf, | ||
| 162 | pmsg->len); | ||
| 163 | if (ret < 0) | ||
| 164 | return ret; | ||
| 165 | |||
| 166 | pmsg->len = ret; | ||
| 167 | } else { | ||
| 168 | ret = dln2_i2c_write(dln2, pmsg->addr, pmsg->buf, | ||
| 169 | pmsg->len); | ||
| 170 | if (ret != pmsg->len) | ||
| 171 | return -EPROTO; | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | return num; | ||
| 176 | } | ||
| 177 | |||
| 178 | static u32 dln2_i2c_func(struct i2c_adapter *a) | ||
| 179 | { | ||
| 180 | return I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | | ||
| 181 | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | | ||
| 182 | I2C_FUNC_SMBUS_I2C_BLOCK; | ||
| 183 | } | ||
| 184 | |||
| 185 | static const struct i2c_algorithm dln2_i2c_usb_algorithm = { | ||
| 186 | .master_xfer = dln2_i2c_xfer, | ||
| 187 | .functionality = dln2_i2c_func, | ||
| 188 | }; | ||
| 189 | |||
| 190 | static int dln2_i2c_probe(struct platform_device *pdev) | ||
| 191 | { | ||
| 192 | int ret; | ||
| 193 | struct dln2_i2c *dln2; | ||
| 194 | struct device *dev = &pdev->dev; | ||
| 195 | struct dln2_platform_data *pdata = dev_get_platdata(&pdev->dev); | ||
| 196 | |||
| 197 | dln2 = devm_kzalloc(dev, sizeof(*dln2), GFP_KERNEL); | ||
| 198 | if (!dln2) | ||
| 199 | return -ENOMEM; | ||
| 200 | |||
| 201 | dln2->buf = devm_kmalloc(dev, DLN2_I2C_BUF_SIZE, GFP_KERNEL); | ||
| 202 | if (!dln2->buf) | ||
| 203 | return -ENOMEM; | ||
| 204 | |||
| 205 | dln2->pdev = pdev; | ||
| 206 | dln2->port = pdata->port; | ||
| 207 | |||
| 208 | /* setup i2c adapter description */ | ||
| 209 | dln2->adapter.owner = THIS_MODULE; | ||
| 210 | dln2->adapter.class = I2C_CLASS_HWMON; | ||
| 211 | dln2->adapter.algo = &dln2_i2c_usb_algorithm; | ||
| 212 | dln2->adapter.dev.parent = dev; | ||
| 213 | i2c_set_adapdata(&dln2->adapter, dln2); | ||
| 214 | snprintf(dln2->adapter.name, sizeof(dln2->adapter.name), "%s-%s-%d", | ||
| 215 | "dln2-i2c", dev_name(pdev->dev.parent), dln2->port); | ||
| 216 | |||
| 217 | platform_set_drvdata(pdev, dln2); | ||
| 218 | |||
| 219 | /* initialize the i2c interface */ | ||
| 220 | ret = dln2_i2c_enable(dln2, true); | ||
| 221 | if (ret < 0) { | ||
| 222 | dev_err(dev, "failed to initialize adapter: %d\n", ret); | ||
| 223 | return ret; | ||
| 224 | } | ||
| 225 | |||
| 226 | /* and finally attach to i2c layer */ | ||
| 227 | ret = i2c_add_adapter(&dln2->adapter); | ||
| 228 | if (ret < 0) { | ||
| 229 | dev_err(dev, "failed to add I2C adapter: %d\n", ret); | ||
| 230 | goto out_disable; | ||
| 231 | } | ||
| 232 | |||
| 233 | return 0; | ||
| 234 | |||
| 235 | out_disable: | ||
| 236 | dln2_i2c_enable(dln2, false); | ||
| 237 | |||
| 238 | return ret; | ||
| 239 | } | ||
| 240 | |||
| 241 | static int dln2_i2c_remove(struct platform_device *pdev) | ||
| 242 | { | ||
| 243 | struct dln2_i2c *dln2 = platform_get_drvdata(pdev); | ||
| 244 | |||
| 245 | i2c_del_adapter(&dln2->adapter); | ||
| 246 | dln2_i2c_enable(dln2, false); | ||
| 247 | |||
| 248 | return 0; | ||
| 249 | } | ||
| 250 | |||
| 251 | static struct platform_driver dln2_i2c_driver = { | ||
| 252 | .driver.name = "dln2-i2c", | ||
| 253 | .probe = dln2_i2c_probe, | ||
| 254 | .remove = dln2_i2c_remove, | ||
| 255 | }; | ||
| 256 | |||
| 257 | module_platform_driver(dln2_i2c_driver); | ||
| 258 | |||
| 259 | MODULE_AUTHOR("Laurentiu Palcu <laurentiu.palcu@intel.com>"); | ||
| 260 | MODULE_DESCRIPTION("Driver for the Diolan DLN2 I2C master interface"); | ||
| 261 | MODULE_LICENSE("GPL v2"); | ||
| 262 | MODULE_ALIAS("platform:dln2-i2c"); | ||
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 88bdc8f612e2..bc4e787096e8 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig | |||
| @@ -127,6 +127,14 @@ config AT91_ADC | |||
| 127 | help | 127 | help |
| 128 | Say yes here to build support for Atmel AT91 ADC. | 128 | Say yes here to build support for Atmel AT91 ADC. |
| 129 | 129 | ||
| 130 | config AXP288_ADC | ||
| 131 | tristate "X-Powers AXP288 ADC driver" | ||
| 132 | depends on MFD_AXP20X | ||
| 133 | help | ||
| 134 | Say yes here to have support for X-Powers power management IC (PMIC) ADC | ||
| 135 | device. Depending on platform configuration, this general purpose ADC can | ||
| 136 | be used for sampling sensors such as thermal resistors. | ||
| 137 | |||
| 130 | config EXYNOS_ADC | 138 | config EXYNOS_ADC |
| 131 | tristate "Exynos ADC driver support" | 139 | tristate "Exynos ADC driver support" |
| 132 | depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST) | 140 | depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST) |
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index cb88a6a23b8f..f30093f5b67a 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile | |||
| @@ -14,6 +14,7 @@ obj-$(CONFIG_AD7793) += ad7793.o | |||
| 14 | obj-$(CONFIG_AD7887) += ad7887.o | 14 | obj-$(CONFIG_AD7887) += ad7887.o |
| 15 | obj-$(CONFIG_AD799X) += ad799x.o | 15 | obj-$(CONFIG_AD799X) += ad799x.o |
| 16 | obj-$(CONFIG_AT91_ADC) += at91_adc.o | 16 | obj-$(CONFIG_AT91_ADC) += at91_adc.o |
| 17 | obj-$(CONFIG_AXP288_ADC) += axp288_adc.o | ||
| 17 | obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o | 18 | obj-$(CONFIG_EXYNOS_ADC) += exynos_adc.o |
| 18 | obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o | 19 | obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o |
| 19 | obj-$(CONFIG_MAX1027) += max1027.o | 20 | obj-$(CONFIG_MAX1027) += max1027.o |
diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c new file mode 100644 index 000000000000..08bcfb061ca5 --- /dev/null +++ b/drivers/iio/adc/axp288_adc.c | |||
| @@ -0,0 +1,261 @@ | |||
| 1 | /* | ||
| 2 | * axp288_adc.c - X-Powers AXP288 PMIC ADC Driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2014 Intel Corporation | ||
| 5 | * | ||
| 6 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License as published by | ||
| 10 | * the Free Software Foundation; version 2 of the License. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but | ||
| 13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
| 15 | * General Public License for more details. | ||
| 16 | * | ||
| 17 | */ | ||
| 18 | |||
| 19 | #include <linux/module.h> | ||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <linux/device.h> | ||
| 22 | #include <linux/regmap.h> | ||
| 23 | #include <linux/mfd/axp20x.h> | ||
| 24 | #include <linux/platform_device.h> | ||
| 25 | |||
| 26 | #include <linux/iio/iio.h> | ||
| 27 | #include <linux/iio/machine.h> | ||
| 28 | #include <linux/iio/driver.h> | ||
| 29 | |||
| 30 | #define AXP288_ADC_EN_MASK 0xF1 | ||
| 31 | #define AXP288_ADC_TS_PIN_GPADC 0xF2 | ||
| 32 | #define AXP288_ADC_TS_PIN_ON 0xF3 | ||
| 33 | |||
| 34 | enum axp288_adc_id { | ||
| 35 | AXP288_ADC_TS, | ||
| 36 | AXP288_ADC_PMIC, | ||
| 37 | AXP288_ADC_GP, | ||
| 38 | AXP288_ADC_BATT_CHRG_I, | ||
| 39 | AXP288_ADC_BATT_DISCHRG_I, | ||
| 40 | AXP288_ADC_BATT_V, | ||
| 41 | AXP288_ADC_NR_CHAN, | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct axp288_adc_info { | ||
| 45 | int irq; | ||
| 46 | struct regmap *regmap; | ||
| 47 | }; | ||
| 48 | |||
| 49 | static const struct iio_chan_spec const axp288_adc_channels[] = { | ||
| 50 | { | ||
| 51 | .indexed = 1, | ||
| 52 | .type = IIO_TEMP, | ||
| 53 | .channel = 0, | ||
| 54 | .address = AXP288_TS_ADC_H, | ||
| 55 | .datasheet_name = "TS_PIN", | ||
| 56 | }, { | ||
| 57 | .indexed = 1, | ||
| 58 | .type = IIO_TEMP, | ||
| 59 | .channel = 1, | ||
| 60 | .address = AXP288_PMIC_ADC_H, | ||
| 61 | .datasheet_name = "PMIC_TEMP", | ||
| 62 | }, { | ||
| 63 | .indexed = 1, | ||
| 64 | .type = IIO_TEMP, | ||
| 65 | .channel = 2, | ||
| 66 | .address = AXP288_GP_ADC_H, | ||
| 67 | .datasheet_name = "GPADC", | ||
| 68 | }, { | ||
| 69 | .indexed = 1, | ||
| 70 | .type = IIO_CURRENT, | ||
| 71 | .channel = 3, | ||
| 72 | .address = AXP20X_BATT_CHRG_I_H, | ||
| 73 | .datasheet_name = "BATT_CHG_I", | ||
| 74 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | ||
| 75 | }, { | ||
| 76 | .indexed = 1, | ||
| 77 | .type = IIO_CURRENT, | ||
| 78 | .channel = 4, | ||
| 79 | .address = AXP20X_BATT_DISCHRG_I_H, | ||
| 80 | .datasheet_name = "BATT_DISCHRG_I", | ||
| 81 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | ||
| 82 | }, { | ||
| 83 | .indexed = 1, | ||
| 84 | .type = IIO_VOLTAGE, | ||
| 85 | .channel = 5, | ||
| 86 | .address = AXP20X_BATT_V_H, | ||
| 87 | .datasheet_name = "BATT_V", | ||
| 88 | .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), | ||
| 89 | }, | ||
| 90 | }; | ||
| 91 | |||
| 92 | #define AXP288_ADC_MAP(_adc_channel_label, _consumer_dev_name, \ | ||
| 93 | _consumer_channel) \ | ||
| 94 | { \ | ||
| 95 | .adc_channel_label = _adc_channel_label, \ | ||
| 96 | .consumer_dev_name = _consumer_dev_name, \ | ||
| 97 | .consumer_channel = _consumer_channel, \ | ||
| 98 | } | ||
| 99 | |||
| 100 | /* for consumer drivers */ | ||
| 101 | static struct iio_map axp288_adc_default_maps[] = { | ||
| 102 | AXP288_ADC_MAP("TS_PIN", "axp288-batt", "axp288-batt-temp"), | ||
| 103 | AXP288_ADC_MAP("PMIC_TEMP", "axp288-pmic", "axp288-pmic-temp"), | ||
| 104 | AXP288_ADC_MAP("GPADC", "axp288-gpadc", "axp288-system-temp"), | ||
| 105 | AXP288_ADC_MAP("BATT_CHG_I", "axp288-chrg", "axp288-chrg-curr"), | ||
| 106 | AXP288_ADC_MAP("BATT_DISCHRG_I", "axp288-chrg", "axp288-chrg-d-curr"), | ||
| 107 | AXP288_ADC_MAP("BATT_V", "axp288-batt", "axp288-batt-volt"), | ||
| 108 | {}, | ||
| 109 | }; | ||
| 110 | |||
| 111 | static int axp288_adc_read_channel(int *val, unsigned long address, | ||
| 112 | struct regmap *regmap) | ||
| 113 | { | ||
| 114 | u8 buf[2]; | ||
| 115 | |||
| 116 | if (regmap_bulk_read(regmap, address, buf, 2)) | ||
| 117 | return -EIO; | ||
| 118 | *val = (buf[0] << 4) + ((buf[1] >> 4) & 0x0F); | ||
| 119 | |||
| 120 | return IIO_VAL_INT; | ||
| 121 | } | ||
| 122 | |||
| 123 | static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode, | ||
| 124 | unsigned long address) | ||
| 125 | { | ||
| 126 | /* channels other than GPADC do not need to switch TS pin */ | ||
| 127 | if (address != AXP288_GP_ADC_H) | ||
| 128 | return 0; | ||
| 129 | |||
| 130 | return regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode); | ||
| 131 | } | ||
| 132 | |||
| 133 | static int axp288_adc_read_raw(struct iio_dev *indio_dev, | ||
| 134 | struct iio_chan_spec const *chan, | ||
| 135 | int *val, int *val2, long mask) | ||
| 136 | { | ||
| 137 | int ret; | ||
| 138 | struct axp288_adc_info *info = iio_priv(indio_dev); | ||
| 139 | |||
| 140 | mutex_lock(&indio_dev->mlock); | ||
| 141 | switch (mask) { | ||
| 142 | case IIO_CHAN_INFO_RAW: | ||
| 143 | if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC, | ||
| 144 | chan->address)) { | ||
| 145 | dev_err(&indio_dev->dev, "GPADC mode\n"); | ||
| 146 | ret = -EINVAL; | ||
| 147 | break; | ||
| 148 | } | ||
| 149 | ret = axp288_adc_read_channel(val, chan->address, info->regmap); | ||
| 150 | if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON, | ||
| 151 | chan->address)) | ||
| 152 | dev_err(&indio_dev->dev, "TS pin restore\n"); | ||
| 153 | break; | ||
| 154 | case IIO_CHAN_INFO_PROCESSED: | ||
| 155 | ret = axp288_adc_read_channel(val, chan->address, info->regmap); | ||
| 156 | break; | ||
| 157 | default: | ||
| 158 | ret = -EINVAL; | ||
| 159 | } | ||
| 160 | mutex_unlock(&indio_dev->mlock); | ||
| 161 | |||
| 162 | return ret; | ||
| 163 | } | ||
| 164 | |||
| 165 | static int axp288_adc_set_state(struct regmap *regmap) | ||
| 166 | { | ||
| 167 | /* ADC should be always enabled for internal FG to function */ | ||
| 168 | if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON)) | ||
| 169 | return -EIO; | ||
| 170 | |||
| 171 | return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK); | ||
| 172 | } | ||
| 173 | |||
| 174 | static const struct iio_info axp288_adc_iio_info = { | ||
| 175 | .read_raw = &axp288_adc_read_raw, | ||
| 176 | .driver_module = THIS_MODULE, | ||
| 177 | }; | ||
| 178 | |||
| 179 | static int axp288_adc_probe(struct platform_device *pdev) | ||
| 180 | { | ||
| 181 | int ret; | ||
| 182 | struct axp288_adc_info *info; | ||
| 183 | struct iio_dev *indio_dev; | ||
| 184 | struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); | ||
| 185 | |||
| 186 | indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info)); | ||
| 187 | if (!indio_dev) | ||
| 188 | return -ENOMEM; | ||
| 189 | |||
| 190 | info = iio_priv(indio_dev); | ||
| 191 | info->irq = platform_get_irq(pdev, 0); | ||
| 192 | if (info->irq < 0) { | ||
| 193 | dev_err(&pdev->dev, "no irq resource?\n"); | ||
| 194 | return info->irq; | ||
| 195 | } | ||
| 196 | platform_set_drvdata(pdev, indio_dev); | ||
| 197 | info->regmap = axp20x->regmap; | ||
| 198 | /* | ||
| 199 | * Set ADC to enabled state at all time, including system suspend. | ||
| 200 | * otherwise internal fuel gauge functionality may be affected. | ||
| 201 | */ | ||
| 202 | ret = axp288_adc_set_state(axp20x->regmap); | ||
| 203 | if (ret) { | ||
| 204 | dev_err(&pdev->dev, "unable to enable ADC device\n"); | ||
| 205 | return ret; | ||
| 206 | } | ||
| 207 | |||
| 208 | indio_dev->dev.parent = &pdev->dev; | ||
| 209 | indio_dev->name = pdev->name; | ||
| 210 | indio_dev->channels = axp288_adc_channels; | ||
| 211 | indio_dev->num_channels = ARRAY_SIZE(axp288_adc_channels); | ||
| 212 | indio_dev->info = &axp288_adc_iio_info; | ||
| 213 | indio_dev->modes = INDIO_DIRECT_MODE; | ||
| 214 | ret = iio_map_array_register(indio_dev, axp288_adc_default_maps); | ||
| 215 | if (ret < 0) | ||
| 216 | return ret; | ||
| 217 | |||
| 218 | ret = iio_device_register(indio_dev); | ||
| 219 | if (ret < 0) { | ||
| 220 | dev_err(&pdev->dev, "unable to register iio device\n"); | ||
| 221 | goto err_array_unregister; | ||
| 222 | } | ||
| 223 | return 0; | ||
| 224 | |||
| 225 | err_array_unregister: | ||
| 226 | iio_map_array_unregister(indio_dev); | ||
| 227 | |||
| 228 | return ret; | ||
| 229 | } | ||
| 230 | |||
| 231 | static int axp288_adc_remove(struct platform_device *pdev) | ||
| 232 | { | ||
| 233 | struct iio_dev *indio_dev = platform_get_drvdata(pdev); | ||
| 234 | |||
| 235 | iio_device_unregister(indio_dev); | ||
| 236 | iio_map_array_unregister(indio_dev); | ||
| 237 | |||
| 238 | return 0; | ||
| 239 | } | ||
| 240 | |||
| 241 | static struct platform_device_id axp288_adc_id_table[] = { | ||
| 242 | { .name = "axp288_adc" }, | ||
| 243 | {}, | ||
| 244 | }; | ||
| 245 | |||
| 246 | static struct platform_driver axp288_adc_driver = { | ||
| 247 | .probe = axp288_adc_probe, | ||
| 248 | .remove = axp288_adc_remove, | ||
| 249 | .id_table = axp288_adc_id_table, | ||
| 250 | .driver = { | ||
| 251 | .name = "axp288_adc", | ||
| 252 | }, | ||
| 253 | }; | ||
| 254 | |||
| 255 | MODULE_DEVICE_TABLE(platform, axp288_adc_id_table); | ||
| 256 | |||
| 257 | module_platform_driver(axp288_adc_driver); | ||
| 258 | |||
| 259 | MODULE_AUTHOR("Jacob Pan <jacob.jun.pan@linux.intel.com>"); | ||
| 260 | MODULE_DESCRIPTION("X-Powers AXP288 ADC Driver"); | ||
| 261 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 1456ea70bbc7..fdbb40181834 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig | |||
| @@ -74,7 +74,8 @@ config MFD_AXP20X | |||
| 74 | select REGMAP_IRQ | 74 | select REGMAP_IRQ |
| 75 | depends on I2C=y | 75 | depends on I2C=y |
| 76 | help | 76 | help |
| 77 | If you say Y here you get support for the X-Powers AXP202 and AXP209. | 77 | If you say Y here you get support for the X-Powers AXP202, AXP209 and |
| 78 | AXP288 power management IC (PMIC). | ||
| 78 | This driver include only the core APIs. You have to select individual | 79 | This driver include only the core APIs. You have to select individual |
| 79 | components like regulators or the PEK (Power Enable Key) under the | 80 | components like regulators or the PEK (Power Enable Key) under the |
| 80 | corresponding menus. | 81 | corresponding menus. |
| @@ -183,6 +184,16 @@ config MFD_DA9063 | |||
| 183 | Additional drivers must be enabled in order to use the functionality | 184 | Additional drivers must be enabled in order to use the functionality |
| 184 | of the device. | 185 | of the device. |
| 185 | 186 | ||
| 187 | config MFD_DLN2 | ||
| 188 | tristate "Diolan DLN2 support" | ||
| 189 | select MFD_CORE | ||
| 190 | depends on USB | ||
| 191 | help | ||
| 192 | This adds support for Diolan USB-I2C/SPI/GPIO Master Adapter | ||
| 193 | DLN-2. Additional drivers such as I2C_DLN2, GPIO_DLN2, | ||
| 194 | etc. must be enabled in order to use the functionality of | ||
| 195 | the device. | ||
| 196 | |||
| 186 | config MFD_MC13XXX | 197 | config MFD_MC13XXX |
| 187 | tristate | 198 | tristate |
| 188 | depends on (SPI_MASTER || I2C) | 199 | depends on (SPI_MASTER || I2C) |
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 8bd54b1253af..10bbd0b9096b 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile | |||
| @@ -174,6 +174,7 @@ obj-$(CONFIG_MFD_STW481X) += stw481x.o | |||
| 174 | obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o | 174 | obj-$(CONFIG_MFD_IPAQ_MICRO) += ipaq-micro.o |
| 175 | obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o | 175 | obj-$(CONFIG_MFD_MENF21BMC) += menf21bmc.o |
| 176 | obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o | 176 | obj-$(CONFIG_MFD_HI6421_PMIC) += hi6421-pmic-core.o |
| 177 | obj-$(CONFIG_MFD_DLN2) += dln2.o | ||
| 177 | 178 | ||
| 178 | intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o | 179 | intel-soc-pmic-objs := intel_soc_pmic_core.o intel_soc_pmic_crc.o |
| 179 | obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o | 180 | obj-$(CONFIG_INTEL_SOC_PMIC) += intel-soc-pmic.o |
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index 6231adbb295d..971b0eb8d821 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * axp20x.c - MFD core driver for the X-Powers AXP202 and AXP209 | 2 | * axp20x.c - MFD core driver for the X-Powers' Power Management ICs |
| 3 | * | 3 | * |
| 4 | * AXP20x comprises an adaptive USB-Compatible PWM charger, 2 BUCK DC-DC | 4 | * AXP20x typically comprises an adaptive USB-Compatible PWM charger, BUCK DC-DC |
| 5 | * converters, 5 LDOs, multiple 12-bit ADCs of voltage, current and temperature | 5 | * converters, LDOs, multiple 12-bit ADCs of voltage, current and temperature |
| 6 | * as well as 4 configurable GPIOs. | 6 | * as well as configurable GPIOs. |
| 7 | * | 7 | * |
| 8 | * Author: Carlo Caione <carlo@caione.org> | 8 | * Author: Carlo Caione <carlo@caione.org> |
| 9 | * | 9 | * |
| @@ -25,9 +25,16 @@ | |||
| 25 | #include <linux/mfd/core.h> | 25 | #include <linux/mfd/core.h> |
| 26 | #include <linux/of_device.h> | 26 | #include <linux/of_device.h> |
| 27 | #include <linux/of_irq.h> | 27 | #include <linux/of_irq.h> |
| 28 | #include <linux/acpi.h> | ||
| 28 | 29 | ||
| 29 | #define AXP20X_OFF 0x80 | 30 | #define AXP20X_OFF 0x80 |
| 30 | 31 | ||
| 32 | static const char const *axp20x_model_names[] = { | ||
| 33 | "AXP202", | ||
| 34 | "AXP209", | ||
| 35 | "AXP288", | ||
| 36 | }; | ||
| 37 | |||
| 31 | static const struct regmap_range axp20x_writeable_ranges[] = { | 38 | static const struct regmap_range axp20x_writeable_ranges[] = { |
| 32 | regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE), | 39 | regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE), |
| 33 | regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES), | 40 | regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES), |
| @@ -47,6 +54,25 @@ static const struct regmap_access_table axp20x_volatile_table = { | |||
| 47 | .n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges), | 54 | .n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges), |
| 48 | }; | 55 | }; |
| 49 | 56 | ||
| 57 | static const struct regmap_range axp288_writeable_ranges[] = { | ||
| 58 | regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ6_STATE), | ||
| 59 | regmap_reg_range(AXP20X_DCDC_MODE, AXP288_FG_TUNE5), | ||
| 60 | }; | ||
| 61 | |||
| 62 | static const struct regmap_range axp288_volatile_ranges[] = { | ||
| 63 | regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IPSOUT_V_HIGH_L), | ||
| 64 | }; | ||
| 65 | |||
| 66 | static const struct regmap_access_table axp288_writeable_table = { | ||
| 67 | .yes_ranges = axp288_writeable_ranges, | ||
| 68 | .n_yes_ranges = ARRAY_SIZE(axp288_writeable_ranges), | ||
| 69 | }; | ||
| 70 | |||
| 71 | static const struct regmap_access_table axp288_volatile_table = { | ||
| 72 | .yes_ranges = axp288_volatile_ranges, | ||
| 73 | .n_yes_ranges = ARRAY_SIZE(axp288_volatile_ranges), | ||
| 74 | }; | ||
| 75 | |||
| 50 | static struct resource axp20x_pek_resources[] = { | 76 | static struct resource axp20x_pek_resources[] = { |
| 51 | { | 77 | { |
| 52 | .name = "PEK_DBR", | 78 | .name = "PEK_DBR", |
| @@ -61,6 +87,39 @@ static struct resource axp20x_pek_resources[] = { | |||
| 61 | }, | 87 | }, |
| 62 | }; | 88 | }; |
| 63 | 89 | ||
| 90 | static struct resource axp288_battery_resources[] = { | ||
| 91 | { | ||
| 92 | .start = AXP288_IRQ_QWBTU, | ||
| 93 | .end = AXP288_IRQ_QWBTU, | ||
| 94 | .flags = IORESOURCE_IRQ, | ||
| 95 | }, | ||
| 96 | { | ||
| 97 | .start = AXP288_IRQ_WBTU, | ||
| 98 | .end = AXP288_IRQ_WBTU, | ||
| 99 | .flags = IORESOURCE_IRQ, | ||
| 100 | }, | ||
| 101 | { | ||
| 102 | .start = AXP288_IRQ_QWBTO, | ||
| 103 | .end = AXP288_IRQ_QWBTO, | ||
| 104 | .flags = IORESOURCE_IRQ, | ||
| 105 | }, | ||
| 106 | { | ||
| 107 | .start = AXP288_IRQ_WBTO, | ||
| 108 | .end = AXP288_IRQ_WBTO, | ||
| 109 | .flags = IORESOURCE_IRQ, | ||
| 110 | }, | ||
| 111 | { | ||
| 112 | .start = AXP288_IRQ_WL2, | ||
| 113 | .end = AXP288_IRQ_WL2, | ||
| 114 | .flags = IORESOURCE_IRQ, | ||
| 115 | }, | ||
| 116 | { | ||
| 117 | .start = AXP288_IRQ_WL1, | ||
| 118 | .end = AXP288_IRQ_WL1, | ||
| 119 | .flags = IORESOURCE_IRQ, | ||
| 120 | }, | ||
| 121 | }; | ||
| 122 | |||
| 64 | static const struct regmap_config axp20x_regmap_config = { | 123 | static const struct regmap_config axp20x_regmap_config = { |
| 65 | .reg_bits = 8, | 124 | .reg_bits = 8, |
| 66 | .val_bits = 8, | 125 | .val_bits = 8, |
| @@ -70,47 +129,96 @@ static const struct regmap_config axp20x_regmap_config = { | |||
| 70 | .cache_type = REGCACHE_RBTREE, | 129 | .cache_type = REGCACHE_RBTREE, |
| 71 | }; | 130 | }; |
| 72 | 131 | ||
| 73 | #define AXP20X_IRQ(_irq, _off, _mask) \ | 132 | static const struct regmap_config axp288_regmap_config = { |
| 74 | [AXP20X_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) } | 133 | .reg_bits = 8, |
| 134 | .val_bits = 8, | ||
| 135 | .wr_table = &axp288_writeable_table, | ||
| 136 | .volatile_table = &axp288_volatile_table, | ||
| 137 | .max_register = AXP288_FG_TUNE5, | ||
| 138 | .cache_type = REGCACHE_RBTREE, | ||
| 139 | }; | ||
| 140 | |||
| 141 | #define INIT_REGMAP_IRQ(_variant, _irq, _off, _mask) \ | ||
| 142 | [_variant##_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) } | ||
| 75 | 143 | ||
| 76 | static const struct regmap_irq axp20x_regmap_irqs[] = { | 144 | static const struct regmap_irq axp20x_regmap_irqs[] = { |
| 77 | AXP20X_IRQ(ACIN_OVER_V, 0, 7), | 145 | INIT_REGMAP_IRQ(AXP20X, ACIN_OVER_V, 0, 7), |
| 78 | AXP20X_IRQ(ACIN_PLUGIN, 0, 6), | 146 | INIT_REGMAP_IRQ(AXP20X, ACIN_PLUGIN, 0, 6), |
| 79 | AXP20X_IRQ(ACIN_REMOVAL, 0, 5), | 147 | INIT_REGMAP_IRQ(AXP20X, ACIN_REMOVAL, 0, 5), |
| 80 | AXP20X_IRQ(VBUS_OVER_V, 0, 4), | 148 | INIT_REGMAP_IRQ(AXP20X, VBUS_OVER_V, 0, 4), |
| 81 | AXP20X_IRQ(VBUS_PLUGIN, 0, 3), | 149 | INIT_REGMAP_IRQ(AXP20X, VBUS_PLUGIN, 0, 3), |
| 82 | AXP20X_IRQ(VBUS_REMOVAL, 0, 2), | 150 | INIT_REGMAP_IRQ(AXP20X, VBUS_REMOVAL, 0, 2), |
| 83 | AXP20X_IRQ(VBUS_V_LOW, 0, 1), | 151 | INIT_REGMAP_IRQ(AXP20X, VBUS_V_LOW, 0, 1), |
| 84 | AXP20X_IRQ(BATT_PLUGIN, 1, 7), | 152 | INIT_REGMAP_IRQ(AXP20X, BATT_PLUGIN, 1, 7), |
| 85 | AXP20X_IRQ(BATT_REMOVAL, 1, 6), | 153 | INIT_REGMAP_IRQ(AXP20X, BATT_REMOVAL, 1, 6), |
| 86 | AXP20X_IRQ(BATT_ENT_ACT_MODE, 1, 5), | 154 | INIT_REGMAP_IRQ(AXP20X, BATT_ENT_ACT_MODE, 1, 5), |
| 87 | AXP20X_IRQ(BATT_EXIT_ACT_MODE, 1, 4), | 155 | INIT_REGMAP_IRQ(AXP20X, BATT_EXIT_ACT_MODE, 1, 4), |
| 88 | AXP20X_IRQ(CHARG, 1, 3), | 156 | INIT_REGMAP_IRQ(AXP20X, CHARG, 1, 3), |
| 89 | AXP20X_IRQ(CHARG_DONE, 1, 2), | 157 | INIT_REGMAP_IRQ(AXP20X, CHARG_DONE, 1, 2), |
| 90 | AXP20X_IRQ(BATT_TEMP_HIGH, 1, 1), | 158 | INIT_REGMAP_IRQ(AXP20X, BATT_TEMP_HIGH, 1, 1), |
| 91 | AXP20X_IRQ(BATT_TEMP_LOW, 1, 0), | 159 | INIT_REGMAP_IRQ(AXP20X, BATT_TEMP_LOW, 1, 0), |
| 92 | AXP20X_IRQ(DIE_TEMP_HIGH, 2, 7), | 160 | INIT_REGMAP_IRQ(AXP20X, DIE_TEMP_HIGH, 2, 7), |
| 93 | AXP20X_IRQ(CHARG_I_LOW, 2, 6), | 161 | INIT_REGMAP_IRQ(AXP20X, CHARG_I_LOW, 2, 6), |
| 94 | AXP20X_IRQ(DCDC1_V_LONG, 2, 5), | 162 | INIT_REGMAP_IRQ(AXP20X, DCDC1_V_LONG, 2, 5), |
| 95 | AXP20X_IRQ(DCDC2_V_LONG, 2, 4), | 163 | INIT_REGMAP_IRQ(AXP20X, DCDC2_V_LONG, 2, 4), |
| 96 | AXP20X_IRQ(DCDC3_V_LONG, 2, 3), | 164 | INIT_REGMAP_IRQ(AXP20X, DCDC3_V_LONG, 2, 3), |
| 97 | AXP20X_IRQ(PEK_SHORT, 2, 1), | 165 | INIT_REGMAP_IRQ(AXP20X, PEK_SHORT, 2, 1), |
| 98 | AXP20X_IRQ(PEK_LONG, 2, 0), | 166 | INIT_REGMAP_IRQ(AXP20X, PEK_LONG, 2, 0), |
| 99 | AXP20X_IRQ(N_OE_PWR_ON, 3, 7), | 167 | INIT_REGMAP_IRQ(AXP20X, N_OE_PWR_ON, 3, 7), |
| 100 | AXP20X_IRQ(N_OE_PWR_OFF, 3, 6), | 168 | INIT_REGMAP_IRQ(AXP20X, N_OE_PWR_OFF, 3, 6), |
| 101 | AXP20X_IRQ(VBUS_VALID, 3, 5), | 169 | INIT_REGMAP_IRQ(AXP20X, VBUS_VALID, 3, 5), |
| 102 | AXP20X_IRQ(VBUS_NOT_VALID, 3, 4), | 170 | INIT_REGMAP_IRQ(AXP20X, VBUS_NOT_VALID, 3, 4), |
| 103 | AXP20X_IRQ(VBUS_SESS_VALID, 3, 3), | 171 | INIT_REGMAP_IRQ(AXP20X, VBUS_SESS_VALID, 3, 3), |
| 104 | AXP20X_IRQ(VBUS_SESS_END, 3, 2), | 172 | INIT_REGMAP_IRQ(AXP20X, VBUS_SESS_END, 3, 2), |
| 105 | AXP20X_IRQ(LOW_PWR_LVL1, 3, 1), | 173 | INIT_REGMAP_IRQ(AXP20X, LOW_PWR_LVL1, 3, 1), |
| 106 | AXP20X_IRQ(LOW_PWR_LVL2, 3, 0), | 174 | INIT_REGMAP_IRQ(AXP20X, LOW_PWR_LVL2, 3, 0), |
| 107 | AXP20X_IRQ(TIMER, 4, 7), | 175 | INIT_REGMAP_IRQ(AXP20X, TIMER, 4, 7), |
| 108 | AXP20X_IRQ(PEK_RIS_EDGE, 4, 6), | 176 | INIT_REGMAP_IRQ(AXP20X, PEK_RIS_EDGE, 4, 6), |
| 109 | AXP20X_IRQ(PEK_FAL_EDGE, 4, 5), | 177 | INIT_REGMAP_IRQ(AXP20X, PEK_FAL_EDGE, 4, 5), |
| 110 | AXP20X_IRQ(GPIO3_INPUT, 4, 3), | 178 | INIT_REGMAP_IRQ(AXP20X, GPIO3_INPUT, 4, 3), |
| 111 | AXP20X_IRQ(GPIO2_INPUT, 4, 2), | 179 | INIT_REGMAP_IRQ(AXP20X, GPIO2_INPUT, 4, 2), |
| 112 | AXP20X_IRQ(GPIO1_INPUT, 4, 1), | 180 | INIT_REGMAP_IRQ(AXP20X, GPIO1_INPUT, 4, 1), |
| 113 | AXP20X_IRQ(GPIO0_INPUT, 4, 0), | 181 | INIT_REGMAP_IRQ(AXP20X, GPIO0_INPUT, 4, 0), |
| 182 | }; | ||
| 183 | |||
| 184 | /* some IRQs are compatible with axp20x models */ | ||
| 185 | static const struct regmap_irq axp288_regmap_irqs[] = { | ||
| 186 | INIT_REGMAP_IRQ(AXP288, VBUS_FALL, 0, 2), | ||
| 187 | INIT_REGMAP_IRQ(AXP288, VBUS_RISE, 0, 3), | ||
| 188 | INIT_REGMAP_IRQ(AXP288, OV, 0, 4), | ||
| 189 | |||
| 190 | INIT_REGMAP_IRQ(AXP288, DONE, 1, 2), | ||
| 191 | INIT_REGMAP_IRQ(AXP288, CHARGING, 1, 3), | ||
| 192 | INIT_REGMAP_IRQ(AXP288, SAFE_QUIT, 1, 4), | ||
| 193 | INIT_REGMAP_IRQ(AXP288, SAFE_ENTER, 1, 5), | ||
| 194 | INIT_REGMAP_IRQ(AXP288, ABSENT, 1, 6), | ||
| 195 | INIT_REGMAP_IRQ(AXP288, APPEND, 1, 7), | ||
| 196 | |||
| 197 | INIT_REGMAP_IRQ(AXP288, QWBTU, 2, 0), | ||
| 198 | INIT_REGMAP_IRQ(AXP288, WBTU, 2, 1), | ||
| 199 | INIT_REGMAP_IRQ(AXP288, QWBTO, 2, 2), | ||
| 200 | INIT_REGMAP_IRQ(AXP288, WBTO, 2, 3), | ||
| 201 | INIT_REGMAP_IRQ(AXP288, QCBTU, 2, 4), | ||
| 202 | INIT_REGMAP_IRQ(AXP288, CBTU, 2, 5), | ||
| 203 | INIT_REGMAP_IRQ(AXP288, QCBTO, 2, 6), | ||
| 204 | INIT_REGMAP_IRQ(AXP288, CBTO, 2, 7), | ||
| 205 | |||
| 206 | INIT_REGMAP_IRQ(AXP288, WL2, 3, 0), | ||
| 207 | INIT_REGMAP_IRQ(AXP288, WL1, 3, 1), | ||
| 208 | INIT_REGMAP_IRQ(AXP288, GPADC, 3, 2), | ||
| 209 | INIT_REGMAP_IRQ(AXP288, OT, 3, 7), | ||
| 210 | |||
| 211 | INIT_REGMAP_IRQ(AXP288, GPIO0, 4, 0), | ||
| 212 | INIT_REGMAP_IRQ(AXP288, GPIO1, 4, 1), | ||
| 213 | INIT_REGMAP_IRQ(AXP288, POKO, 4, 2), | ||
| 214 | INIT_REGMAP_IRQ(AXP288, POKL, 4, 3), | ||
| 215 | INIT_REGMAP_IRQ(AXP288, POKS, 4, 4), | ||
| 216 | INIT_REGMAP_IRQ(AXP288, POKN, 4, 5), | ||
| 217 | INIT_REGMAP_IRQ(AXP288, POKP, 4, 6), | ||
| 218 | INIT_REGMAP_IRQ(AXP288, TIMER, 4, 7), | ||
| 219 | |||
| 220 | INIT_REGMAP_IRQ(AXP288, MV_CHNG, 5, 0), | ||
| 221 | INIT_REGMAP_IRQ(AXP288, BC_USB_CHNG, 5, 1), | ||
| 114 | }; | 222 | }; |
| 115 | 223 | ||
| 116 | static const struct of_device_id axp20x_of_match[] = { | 224 | static const struct of_device_id axp20x_of_match[] = { |
| @@ -128,16 +236,39 @@ static const struct i2c_device_id axp20x_i2c_id[] = { | |||
| 128 | }; | 236 | }; |
| 129 | MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id); | 237 | MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id); |
| 130 | 238 | ||
| 239 | static struct acpi_device_id axp20x_acpi_match[] = { | ||
| 240 | { | ||
| 241 | .id = "INT33F4", | ||
| 242 | .driver_data = AXP288_ID, | ||
| 243 | }, | ||
| 244 | { }, | ||
| 245 | }; | ||
| 246 | MODULE_DEVICE_TABLE(acpi, axp20x_acpi_match); | ||
| 247 | |||
| 131 | static const struct regmap_irq_chip axp20x_regmap_irq_chip = { | 248 | static const struct regmap_irq_chip axp20x_regmap_irq_chip = { |
| 132 | .name = "axp20x_irq_chip", | 249 | .name = "axp20x_irq_chip", |
| 133 | .status_base = AXP20X_IRQ1_STATE, | 250 | .status_base = AXP20X_IRQ1_STATE, |
| 134 | .ack_base = AXP20X_IRQ1_STATE, | 251 | .ack_base = AXP20X_IRQ1_STATE, |
| 135 | .mask_base = AXP20X_IRQ1_EN, | 252 | .mask_base = AXP20X_IRQ1_EN, |
| 136 | .num_regs = 5, | 253 | .mask_invert = true, |
| 254 | .init_ack_masked = true, | ||
| 137 | .irqs = axp20x_regmap_irqs, | 255 | .irqs = axp20x_regmap_irqs, |
| 138 | .num_irqs = ARRAY_SIZE(axp20x_regmap_irqs), | 256 | .num_irqs = ARRAY_SIZE(axp20x_regmap_irqs), |
| 257 | .num_regs = 5, | ||
| 258 | |||
| 259 | }; | ||
| 260 | |||
| 261 | static const struct regmap_irq_chip axp288_regmap_irq_chip = { | ||
| 262 | .name = "axp288_irq_chip", | ||
| 263 | .status_base = AXP20X_IRQ1_STATE, | ||
| 264 | .ack_base = AXP20X_IRQ1_STATE, | ||
| 265 | .mask_base = AXP20X_IRQ1_EN, | ||
| 139 | .mask_invert = true, | 266 | .mask_invert = true, |
| 140 | .init_ack_masked = true, | 267 | .init_ack_masked = true, |
| 268 | .irqs = axp288_regmap_irqs, | ||
| 269 | .num_irqs = ARRAY_SIZE(axp288_regmap_irqs), | ||
| 270 | .num_regs = 6, | ||
| 271 | |||
| 141 | }; | 272 | }; |
| 142 | 273 | ||
| 143 | static struct mfd_cell axp20x_cells[] = { | 274 | static struct mfd_cell axp20x_cells[] = { |
| @@ -150,36 +281,155 @@ static struct mfd_cell axp20x_cells[] = { | |||
| 150 | }, | 281 | }, |
| 151 | }; | 282 | }; |
| 152 | 283 | ||
| 284 | static struct resource axp288_adc_resources[] = { | ||
| 285 | { | ||
| 286 | .name = "GPADC", | ||
| 287 | .start = AXP288_IRQ_GPADC, | ||
| 288 | .end = AXP288_IRQ_GPADC, | ||
| 289 | .flags = IORESOURCE_IRQ, | ||
| 290 | }, | ||
| 291 | }; | ||
| 292 | |||
| 293 | static struct resource axp288_charger_resources[] = { | ||
| 294 | { | ||
| 295 | .start = AXP288_IRQ_OV, | ||
| 296 | .end = AXP288_IRQ_OV, | ||
| 297 | .flags = IORESOURCE_IRQ, | ||
| 298 | }, | ||
| 299 | { | ||
| 300 | .start = AXP288_IRQ_DONE, | ||
| 301 | .end = AXP288_IRQ_DONE, | ||
| 302 | .flags = IORESOURCE_IRQ, | ||
| 303 | }, | ||
| 304 | { | ||
| 305 | .start = AXP288_IRQ_CHARGING, | ||
| 306 | .end = AXP288_IRQ_CHARGING, | ||
| 307 | .flags = IORESOURCE_IRQ, | ||
| 308 | }, | ||
| 309 | { | ||
| 310 | .start = AXP288_IRQ_SAFE_QUIT, | ||
| 311 | .end = AXP288_IRQ_SAFE_QUIT, | ||
| 312 | .flags = IORESOURCE_IRQ, | ||
| 313 | }, | ||
| 314 | { | ||
| 315 | .start = AXP288_IRQ_SAFE_ENTER, | ||
| 316 | .end = AXP288_IRQ_SAFE_ENTER, | ||
| 317 | .flags = IORESOURCE_IRQ, | ||
| 318 | }, | ||
| 319 | { | ||
| 320 | .start = AXP288_IRQ_QCBTU, | ||
| 321 | .end = AXP288_IRQ_QCBTU, | ||
| 322 | .flags = IORESOURCE_IRQ, | ||
| 323 | }, | ||
| 324 | { | ||
| 325 | .start = AXP288_IRQ_CBTU, | ||
| 326 | .end = AXP288_IRQ_CBTU, | ||
| 327 | .flags = IORESOURCE_IRQ, | ||
| 328 | }, | ||
| 329 | { | ||
| 330 | .start = AXP288_IRQ_QCBTO, | ||
| 331 | .end = AXP288_IRQ_QCBTO, | ||
| 332 | .flags = IORESOURCE_IRQ, | ||
| 333 | }, | ||
| 334 | { | ||
| 335 | .start = AXP288_IRQ_CBTO, | ||
| 336 | .end = AXP288_IRQ_CBTO, | ||
| 337 | .flags = IORESOURCE_IRQ, | ||
| 338 | }, | ||
| 339 | }; | ||
| 340 | |||
| 341 | static struct mfd_cell axp288_cells[] = { | ||
| 342 | { | ||
| 343 | .name = "axp288_adc", | ||
| 344 | .num_resources = ARRAY_SIZE(axp288_adc_resources), | ||
| 345 | .resources = axp288_adc_resources, | ||
| 346 | }, | ||
| 347 | { | ||
| 348 | .name = "axp288_charger", | ||
| 349 | .num_resources = ARRAY_SIZE(axp288_charger_resources), | ||
| 350 | .resources = axp288_charger_resources, | ||
| 351 | }, | ||
| 352 | { | ||
| 353 | .name = "axp288_battery", | ||
| 354 | .num_resources = ARRAY_SIZE(axp288_battery_resources), | ||
| 355 | .resources = axp288_battery_resources, | ||
| 356 | }, | ||
| 357 | }; | ||
| 358 | |||
| 153 | static struct axp20x_dev *axp20x_pm_power_off; | 359 | static struct axp20x_dev *axp20x_pm_power_off; |
| 154 | static void axp20x_power_off(void) | 360 | static void axp20x_power_off(void) |
| 155 | { | 361 | { |
| 362 | if (axp20x_pm_power_off->variant == AXP288_ID) | ||
| 363 | return; | ||
| 364 | |||
| 156 | regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL, | 365 | regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL, |
| 157 | AXP20X_OFF); | 366 | AXP20X_OFF); |
| 158 | } | 367 | } |
| 159 | 368 | ||
| 369 | static int axp20x_match_device(struct axp20x_dev *axp20x, struct device *dev) | ||
| 370 | { | ||
| 371 | const struct acpi_device_id *acpi_id; | ||
| 372 | const struct of_device_id *of_id; | ||
| 373 | |||
| 374 | if (dev->of_node) { | ||
| 375 | of_id = of_match_device(axp20x_of_match, dev); | ||
| 376 | if (!of_id) { | ||
| 377 | dev_err(dev, "Unable to match OF ID\n"); | ||
| 378 | return -ENODEV; | ||
| 379 | } | ||
| 380 | axp20x->variant = (long) of_id->data; | ||
| 381 | } else { | ||
| 382 | acpi_id = acpi_match_device(dev->driver->acpi_match_table, dev); | ||
| 383 | if (!acpi_id || !acpi_id->driver_data) { | ||
| 384 | dev_err(dev, "Unable to match ACPI ID and data\n"); | ||
| 385 | return -ENODEV; | ||
| 386 | } | ||
| 387 | axp20x->variant = (long) acpi_id->driver_data; | ||
| 388 | } | ||
| 389 | |||
| 390 | switch (axp20x->variant) { | ||
| 391 | case AXP202_ID: | ||
| 392 | case AXP209_ID: | ||
| 393 | axp20x->nr_cells = ARRAY_SIZE(axp20x_cells); | ||
| 394 | axp20x->cells = axp20x_cells; | ||
| 395 | axp20x->regmap_cfg = &axp20x_regmap_config; | ||
| 396 | axp20x->regmap_irq_chip = &axp20x_regmap_irq_chip; | ||
| 397 | break; | ||
| 398 | case AXP288_ID: | ||
| 399 | axp20x->cells = axp288_cells; | ||
| 400 | axp20x->nr_cells = ARRAY_SIZE(axp288_cells); | ||
| 401 | axp20x->regmap_cfg = &axp288_regmap_config; | ||
| 402 | axp20x->regmap_irq_chip = &axp288_regmap_irq_chip; | ||
| 403 | break; | ||
| 404 | default: | ||
| 405 | dev_err(dev, "unsupported AXP20X ID %lu\n", axp20x->variant); | ||
| 406 | return -EINVAL; | ||
| 407 | } | ||
| 408 | dev_info(dev, "AXP20x variant %s found\n", | ||
| 409 | axp20x_model_names[axp20x->variant]); | ||
| 410 | |||
| 411 | return 0; | ||
| 412 | } | ||
| 413 | |||
| 160 | static int axp20x_i2c_probe(struct i2c_client *i2c, | 414 | static int axp20x_i2c_probe(struct i2c_client *i2c, |
| 161 | const struct i2c_device_id *id) | 415 | const struct i2c_device_id *id) |
| 162 | { | 416 | { |
| 163 | struct axp20x_dev *axp20x; | 417 | struct axp20x_dev *axp20x; |
| 164 | const struct of_device_id *of_id; | ||
| 165 | int ret; | 418 | int ret; |
| 166 | 419 | ||
| 167 | axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL); | 420 | axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL); |
| 168 | if (!axp20x) | 421 | if (!axp20x) |
| 169 | return -ENOMEM; | 422 | return -ENOMEM; |
| 170 | 423 | ||
| 171 | of_id = of_match_device(axp20x_of_match, &i2c->dev); | 424 | ret = axp20x_match_device(axp20x, &i2c->dev); |
| 172 | if (!of_id) { | 425 | if (ret) |
| 173 | dev_err(&i2c->dev, "Unable to setup AXP20X data\n"); | 426 | return ret; |
| 174 | return -ENODEV; | ||
| 175 | } | ||
| 176 | axp20x->variant = (long) of_id->data; | ||
| 177 | 427 | ||
| 178 | axp20x->i2c_client = i2c; | 428 | axp20x->i2c_client = i2c; |
| 179 | axp20x->dev = &i2c->dev; | 429 | axp20x->dev = &i2c->dev; |
| 180 | dev_set_drvdata(axp20x->dev, axp20x); | 430 | dev_set_drvdata(axp20x->dev, axp20x); |
| 181 | 431 | ||
| 182 | axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config); | 432 | axp20x->regmap = devm_regmap_init_i2c(i2c, axp20x->regmap_cfg); |
| 183 | if (IS_ERR(axp20x->regmap)) { | 433 | if (IS_ERR(axp20x->regmap)) { |
| 184 | ret = PTR_ERR(axp20x->regmap); | 434 | ret = PTR_ERR(axp20x->regmap); |
| 185 | dev_err(&i2c->dev, "regmap init failed: %d\n", ret); | 435 | dev_err(&i2c->dev, "regmap init failed: %d\n", ret); |
| @@ -188,15 +438,15 @@ static int axp20x_i2c_probe(struct i2c_client *i2c, | |||
| 188 | 438 | ||
| 189 | ret = regmap_add_irq_chip(axp20x->regmap, i2c->irq, | 439 | ret = regmap_add_irq_chip(axp20x->regmap, i2c->irq, |
| 190 | IRQF_ONESHOT | IRQF_SHARED, -1, | 440 | IRQF_ONESHOT | IRQF_SHARED, -1, |
| 191 | &axp20x_regmap_irq_chip, | 441 | axp20x->regmap_irq_chip, |
| 192 | &axp20x->regmap_irqc); | 442 | &axp20x->regmap_irqc); |
| 193 | if (ret) { | 443 | if (ret) { |
| 194 | dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret); | 444 | dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret); |
| 195 | return ret; | 445 | return ret; |
| 196 | } | 446 | } |
| 197 | 447 | ||
| 198 | ret = mfd_add_devices(axp20x->dev, -1, axp20x_cells, | 448 | ret = mfd_add_devices(axp20x->dev, -1, axp20x->cells, |
| 199 | ARRAY_SIZE(axp20x_cells), NULL, 0, NULL); | 449 | axp20x->nr_cells, NULL, 0, NULL); |
| 200 | 450 | ||
| 201 | if (ret) { | 451 | if (ret) { |
| 202 | dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret); | 452 | dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret); |
| @@ -234,6 +484,7 @@ static struct i2c_driver axp20x_i2c_driver = { | |||
| 234 | .name = "axp20x", | 484 | .name = "axp20x", |
| 235 | .owner = THIS_MODULE, | 485 | .owner = THIS_MODULE, |
| 236 | .of_match_table = of_match_ptr(axp20x_of_match), | 486 | .of_match_table = of_match_ptr(axp20x_of_match), |
| 487 | .acpi_match_table = ACPI_PTR(axp20x_acpi_match), | ||
| 237 | }, | 488 | }, |
| 238 | .probe = axp20x_i2c_probe, | 489 | .probe = axp20x_i2c_probe, |
| 239 | .remove = axp20x_i2c_remove, | 490 | .remove = axp20x_i2c_remove, |
diff --git a/drivers/mfd/dln2.c b/drivers/mfd/dln2.c new file mode 100644 index 000000000000..559e6cc3e022 --- /dev/null +++ b/drivers/mfd/dln2.c | |||
| @@ -0,0 +1,769 @@ | |||
| 1 | /* | ||
| 2 | * Driver for the Diolan DLN-2 USB adapter | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Intel Corporation | ||
| 5 | * | ||
| 6 | * Derived from: | ||
| 7 | * i2c-diolan-u2c.c | ||
| 8 | * Copyright (c) 2010-2011 Ericsson AB | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or | ||
| 11 | * modify it under the terms of the GNU General Public License as | ||
| 12 | * published by the Free Software Foundation, version 2. | ||
| 13 | */ | ||
| 14 | |||
| 15 | #include <linux/kernel.h> | ||
| 16 | #include <linux/module.h> | ||
| 17 | #include <linux/types.h> | ||
| 18 | #include <linux/slab.h> | ||
| 19 | #include <linux/usb.h> | ||
| 20 | #include <linux/i2c.h> | ||
| 21 | #include <linux/mutex.h> | ||
| 22 | #include <linux/platform_device.h> | ||
| 23 | #include <linux/mfd/core.h> | ||
| 24 | #include <linux/mfd/dln2.h> | ||
| 25 | #include <linux/rculist.h> | ||
| 26 | |||
| 27 | struct dln2_header { | ||
| 28 | __le16 size; | ||
| 29 | __le16 id; | ||
| 30 | __le16 echo; | ||
| 31 | __le16 handle; | ||
| 32 | }; | ||
| 33 | |||
| 34 | struct dln2_response { | ||
| 35 | struct dln2_header hdr; | ||
| 36 | __le16 result; | ||
| 37 | }; | ||
| 38 | |||
| 39 | #define DLN2_GENERIC_MODULE_ID 0x00 | ||
| 40 | #define DLN2_GENERIC_CMD(cmd) DLN2_CMD(cmd, DLN2_GENERIC_MODULE_ID) | ||
| 41 | #define CMD_GET_DEVICE_VER DLN2_GENERIC_CMD(0x30) | ||
| 42 | #define CMD_GET_DEVICE_SN DLN2_GENERIC_CMD(0x31) | ||
| 43 | |||
| 44 | #define DLN2_HW_ID 0x200 | ||
| 45 | #define DLN2_USB_TIMEOUT 200 /* in ms */ | ||
| 46 | #define DLN2_MAX_RX_SLOTS 16 | ||
| 47 | #define DLN2_MAX_URBS 16 | ||
| 48 | #define DLN2_RX_BUF_SIZE 512 | ||
| 49 | |||
| 50 | enum dln2_handle { | ||
| 51 | DLN2_HANDLE_EVENT = 0, /* don't change, hardware defined */ | ||
| 52 | DLN2_HANDLE_CTRL, | ||
| 53 | DLN2_HANDLE_GPIO, | ||
| 54 | DLN2_HANDLE_I2C, | ||
| 55 | DLN2_HANDLES | ||
| 56 | }; | ||
| 57 | |||
| 58 | /* | ||
| 59 | * Receive context used between the receive demultiplexer and the transfer | ||
| 60 | * routine. While sending a request the transfer routine will look for a free | ||
| 61 | * receive context and use it to wait for a response and to receive the URB and | ||
| 62 | * thus the response data. | ||
| 63 | */ | ||
| 64 | struct dln2_rx_context { | ||
| 65 | /* completion used to wait for a response */ | ||
| 66 | struct completion done; | ||
| 67 | |||
| 68 | /* if non-NULL the URB contains the response */ | ||
| 69 | struct urb *urb; | ||
| 70 | |||
| 71 | /* if true then this context is used to wait for a response */ | ||
| 72 | bool in_use; | ||
| 73 | }; | ||
| 74 | |||
| 75 | /* | ||
| 76 | * Receive contexts for a particular DLN2 module (i2c, gpio, etc.). We use the | ||
| 77 | * handle header field to identify the module in dln2_dev.mod_rx_slots and then | ||
| 78 | * the echo header field to index the slots field and find the receive context | ||
| 79 | * for a particular request. | ||
| 80 | */ | ||
| 81 | struct dln2_mod_rx_slots { | ||
| 82 | /* RX slots bitmap */ | ||
| 83 | DECLARE_BITMAP(bmap, DLN2_MAX_RX_SLOTS); | ||
| 84 | |||
| 85 | /* used to wait for a free RX slot */ | ||
| 86 | wait_queue_head_t wq; | ||
| 87 | |||
| 88 | /* used to wait for an RX operation to complete */ | ||
| 89 | struct dln2_rx_context slots[DLN2_MAX_RX_SLOTS]; | ||
| 90 | |||
| 91 | /* avoid races between alloc/free_rx_slot and dln2_rx_transfer */ | ||
| 92 | spinlock_t lock; | ||
| 93 | }; | ||
| 94 | |||
| 95 | struct dln2_dev { | ||
| 96 | struct usb_device *usb_dev; | ||
| 97 | struct usb_interface *interface; | ||
| 98 | u8 ep_in; | ||
| 99 | u8 ep_out; | ||
| 100 | |||
| 101 | struct urb *rx_urb[DLN2_MAX_URBS]; | ||
| 102 | void *rx_buf[DLN2_MAX_URBS]; | ||
| 103 | |||
| 104 | struct dln2_mod_rx_slots mod_rx_slots[DLN2_HANDLES]; | ||
| 105 | |||
| 106 | struct list_head event_cb_list; | ||
| 107 | spinlock_t event_cb_lock; | ||
| 108 | |||
| 109 | bool disconnect; | ||
| 110 | int active_transfers; | ||
| 111 | wait_queue_head_t disconnect_wq; | ||
| 112 | spinlock_t disconnect_lock; | ||
| 113 | }; | ||
| 114 | |||
| 115 | struct dln2_event_cb_entry { | ||
| 116 | struct list_head list; | ||
| 117 | u16 id; | ||
| 118 | struct platform_device *pdev; | ||
| 119 | dln2_event_cb_t callback; | ||
| 120 | }; | ||
| 121 | |||
| 122 | int dln2_register_event_cb(struct platform_device *pdev, u16 id, | ||
| 123 | dln2_event_cb_t event_cb) | ||
| 124 | { | ||
| 125 | struct dln2_dev *dln2 = dev_get_drvdata(pdev->dev.parent); | ||
| 126 | struct dln2_event_cb_entry *i, *entry; | ||
| 127 | unsigned long flags; | ||
| 128 | int ret = 0; | ||
| 129 | |||
| 130 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); | ||
| 131 | if (!entry) | ||
| 132 | return -ENOMEM; | ||
| 133 | |||
| 134 | entry->id = id; | ||
| 135 | entry->callback = event_cb; | ||
| 136 | entry->pdev = pdev; | ||
| 137 | |||
| 138 | spin_lock_irqsave(&dln2->event_cb_lock, flags); | ||
| 139 | |||
| 140 | list_for_each_entry(i, &dln2->event_cb_list, list) { | ||
| 141 | if (i->id == id) { | ||
| 142 | ret = -EBUSY; | ||
| 143 | break; | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | if (!ret) | ||
| 148 | list_add_rcu(&entry->list, &dln2->event_cb_list); | ||
| 149 | |||
| 150 | spin_unlock_irqrestore(&dln2->event_cb_lock, flags); | ||
| 151 | |||
| 152 | if (ret) | ||
| 153 | kfree(entry); | ||
| 154 | |||
| 155 | return ret; | ||
| 156 | } | ||
| 157 | EXPORT_SYMBOL(dln2_register_event_cb); | ||
| 158 | |||
| 159 | void dln2_unregister_event_cb(struct platform_device *pdev, u16 id) | ||
| 160 | { | ||
| 161 | struct dln2_dev *dln2 = dev_get_drvdata(pdev->dev.parent); | ||
| 162 | struct dln2_event_cb_entry *i; | ||
| 163 | unsigned long flags; | ||
| 164 | bool found = false; | ||
| 165 | |||
| 166 | spin_lock_irqsave(&dln2->event_cb_lock, flags); | ||
| 167 | |||
| 168 | list_for_each_entry(i, &dln2->event_cb_list, list) { | ||
| 169 | if (i->id == id) { | ||
| 170 | list_del_rcu(&i->list); | ||
| 171 | found = true; | ||
| 172 | break; | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | spin_unlock_irqrestore(&dln2->event_cb_lock, flags); | ||
| 177 | |||
| 178 | if (found) { | ||
| 179 | synchronize_rcu(); | ||
| 180 | kfree(i); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | EXPORT_SYMBOL(dln2_unregister_event_cb); | ||
| 184 | |||
| 185 | /* | ||
| 186 | * Returns true if a valid transfer slot is found. In this case the URB must not | ||
| 187 | * be resubmitted immediately in dln2_rx as we need the data when dln2_transfer | ||
| 188 | * is woke up. It will be resubmitted there. | ||
| 189 | */ | ||
| 190 | static bool dln2_transfer_complete(struct dln2_dev *dln2, struct urb *urb, | ||
| 191 | u16 handle, u16 rx_slot) | ||
| 192 | { | ||
| 193 | struct device *dev = &dln2->interface->dev; | ||
| 194 | struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle]; | ||
| 195 | struct dln2_rx_context *rxc; | ||
| 196 | bool valid_slot = false; | ||
| 197 | |||
| 198 | if (rx_slot >= DLN2_MAX_RX_SLOTS) | ||
| 199 | goto out; | ||
| 200 | |||
| 201 | rxc = &rxs->slots[rx_slot]; | ||
| 202 | |||
| 203 | /* | ||
| 204 | * No need to disable interrupts as this lock is not taken in interrupt | ||
| 205 | * context elsewhere in this driver. This function (or its callers) are | ||
| 206 | * also not exported to other modules. | ||
| 207 | */ | ||
| 208 | spin_lock(&rxs->lock); | ||
| 209 | if (rxc->in_use && !rxc->urb) { | ||
| 210 | rxc->urb = urb; | ||
| 211 | complete(&rxc->done); | ||
| 212 | valid_slot = true; | ||
| 213 | } | ||
| 214 | spin_unlock(&rxs->lock); | ||
| 215 | |||
| 216 | out: | ||
| 217 | if (!valid_slot) | ||
| 218 | dev_warn(dev, "bad/late response %d/%d\n", handle, rx_slot); | ||
| 219 | |||
| 220 | return valid_slot; | ||
| 221 | } | ||
| 222 | |||
| 223 | static void dln2_run_event_callbacks(struct dln2_dev *dln2, u16 id, u16 echo, | ||
| 224 | void *data, int len) | ||
| 225 | { | ||
| 226 | struct dln2_event_cb_entry *i; | ||
| 227 | |||
| 228 | rcu_read_lock(); | ||
| 229 | |||
| 230 | list_for_each_entry_rcu(i, &dln2->event_cb_list, list) { | ||
| 231 | if (i->id == id) { | ||
| 232 | i->callback(i->pdev, echo, data, len); | ||
| 233 | break; | ||
| 234 | } | ||
| 235 | } | ||
| 236 | |||
| 237 | rcu_read_unlock(); | ||
| 238 | } | ||
| 239 | |||
| 240 | static void dln2_rx(struct urb *urb) | ||
| 241 | { | ||
| 242 | struct dln2_dev *dln2 = urb->context; | ||
| 243 | struct dln2_header *hdr = urb->transfer_buffer; | ||
| 244 | struct device *dev = &dln2->interface->dev; | ||
| 245 | u16 id, echo, handle, size; | ||
| 246 | u8 *data; | ||
| 247 | int len; | ||
| 248 | int err; | ||
| 249 | |||
| 250 | switch (urb->status) { | ||
| 251 | case 0: | ||
| 252 | /* success */ | ||
| 253 | break; | ||
| 254 | case -ECONNRESET: | ||
| 255 | case -ENOENT: | ||
| 256 | case -ESHUTDOWN: | ||
| 257 | case -EPIPE: | ||
| 258 | /* this urb is terminated, clean up */ | ||
| 259 | dev_dbg(dev, "urb shutting down with status %d\n", urb->status); | ||
| 260 | return; | ||
| 261 | default: | ||
| 262 | dev_dbg(dev, "nonzero urb status received %d\n", urb->status); | ||
| 263 | goto out; | ||
| 264 | } | ||
| 265 | |||
| 266 | if (urb->actual_length < sizeof(struct dln2_header)) { | ||
| 267 | dev_err(dev, "short response: %d\n", urb->actual_length); | ||
| 268 | goto out; | ||
| 269 | } | ||
| 270 | |||
| 271 | handle = le16_to_cpu(hdr->handle); | ||
| 272 | id = le16_to_cpu(hdr->id); | ||
| 273 | echo = le16_to_cpu(hdr->echo); | ||
| 274 | size = le16_to_cpu(hdr->size); | ||
| 275 | |||
| 276 | if (size != urb->actual_length) { | ||
| 277 | dev_err(dev, "size mismatch: handle %x cmd %x echo %x size %d actual %d\n", | ||
| 278 | handle, id, echo, size, urb->actual_length); | ||
| 279 | goto out; | ||
| 280 | } | ||
| 281 | |||
| 282 | if (handle >= DLN2_HANDLES) { | ||
| 283 | dev_warn(dev, "invalid handle %d\n", handle); | ||
| 284 | goto out; | ||
| 285 | } | ||
| 286 | |||
| 287 | data = urb->transfer_buffer + sizeof(struct dln2_header); | ||
| 288 | len = urb->actual_length - sizeof(struct dln2_header); | ||
| 289 | |||
| 290 | if (handle == DLN2_HANDLE_EVENT) { | ||
| 291 | dln2_run_event_callbacks(dln2, id, echo, data, len); | ||
| 292 | } else { | ||
| 293 | /* URB will be re-submitted in _dln2_transfer (free_rx_slot) */ | ||
| 294 | if (dln2_transfer_complete(dln2, urb, handle, echo)) | ||
| 295 | return; | ||
| 296 | } | ||
| 297 | |||
| 298 | out: | ||
| 299 | err = usb_submit_urb(urb, GFP_ATOMIC); | ||
| 300 | if (err < 0) | ||
| 301 | dev_err(dev, "failed to resubmit RX URB: %d\n", err); | ||
| 302 | } | ||
| 303 | |||
| 304 | static void *dln2_prep_buf(u16 handle, u16 cmd, u16 echo, const void *obuf, | ||
| 305 | int *obuf_len, gfp_t gfp) | ||
| 306 | { | ||
| 307 | int len; | ||
| 308 | void *buf; | ||
| 309 | struct dln2_header *hdr; | ||
| 310 | |||
| 311 | len = *obuf_len + sizeof(*hdr); | ||
| 312 | buf = kmalloc(len, gfp); | ||
| 313 | if (!buf) | ||
| 314 | return NULL; | ||
| 315 | |||
| 316 | hdr = (struct dln2_header *)buf; | ||
| 317 | hdr->id = cpu_to_le16(cmd); | ||
| 318 | hdr->size = cpu_to_le16(len); | ||
| 319 | hdr->echo = cpu_to_le16(echo); | ||
| 320 | hdr->handle = cpu_to_le16(handle); | ||
| 321 | |||
| 322 | memcpy(buf + sizeof(*hdr), obuf, *obuf_len); | ||
| 323 | |||
| 324 | *obuf_len = len; | ||
| 325 | |||
| 326 | return buf; | ||
| 327 | } | ||
| 328 | |||
| 329 | static int dln2_send_wait(struct dln2_dev *dln2, u16 handle, u16 cmd, u16 echo, | ||
| 330 | const void *obuf, int obuf_len) | ||
| 331 | { | ||
| 332 | int ret = 0; | ||
| 333 | int len = obuf_len; | ||
| 334 | void *buf; | ||
| 335 | int actual; | ||
| 336 | |||
| 337 | buf = dln2_prep_buf(handle, cmd, echo, obuf, &len, GFP_KERNEL); | ||
| 338 | if (!buf) | ||
| 339 | return -ENOMEM; | ||
| 340 | |||
| 341 | ret = usb_bulk_msg(dln2->usb_dev, | ||
| 342 | usb_sndbulkpipe(dln2->usb_dev, dln2->ep_out), | ||
| 343 | buf, len, &actual, DLN2_USB_TIMEOUT); | ||
| 344 | |||
| 345 | kfree(buf); | ||
| 346 | |||
| 347 | return ret; | ||
| 348 | } | ||
| 349 | |||
| 350 | static bool find_free_slot(struct dln2_dev *dln2, u16 handle, int *slot) | ||
| 351 | { | ||
| 352 | struct dln2_mod_rx_slots *rxs; | ||
| 353 | unsigned long flags; | ||
| 354 | |||
| 355 | if (dln2->disconnect) { | ||
| 356 | *slot = -ENODEV; | ||
| 357 | return true; | ||
| 358 | } | ||
| 359 | |||
| 360 | rxs = &dln2->mod_rx_slots[handle]; | ||
| 361 | |||
| 362 | spin_lock_irqsave(&rxs->lock, flags); | ||
| 363 | |||
| 364 | *slot = find_first_zero_bit(rxs->bmap, DLN2_MAX_RX_SLOTS); | ||
| 365 | |||
| 366 | if (*slot < DLN2_MAX_RX_SLOTS) { | ||
| 367 | struct dln2_rx_context *rxc = &rxs->slots[*slot]; | ||
| 368 | |||
| 369 | set_bit(*slot, rxs->bmap); | ||
| 370 | rxc->in_use = true; | ||
| 371 | } | ||
| 372 | |||
| 373 | spin_unlock_irqrestore(&rxs->lock, flags); | ||
| 374 | |||
| 375 | return *slot < DLN2_MAX_RX_SLOTS; | ||
| 376 | } | ||
| 377 | |||
| 378 | static int alloc_rx_slot(struct dln2_dev *dln2, u16 handle) | ||
| 379 | { | ||
| 380 | int ret; | ||
| 381 | int slot; | ||
| 382 | |||
| 383 | /* | ||
| 384 | * No need to timeout here, the wait is bounded by the timeout in | ||
| 385 | * _dln2_transfer. | ||
| 386 | */ | ||
| 387 | ret = wait_event_interruptible(dln2->mod_rx_slots[handle].wq, | ||
| 388 | find_free_slot(dln2, handle, &slot)); | ||
| 389 | if (ret < 0) | ||
| 390 | return ret; | ||
| 391 | |||
| 392 | return slot; | ||
| 393 | } | ||
| 394 | |||
| 395 | static void free_rx_slot(struct dln2_dev *dln2, u16 handle, int slot) | ||
| 396 | { | ||
| 397 | struct dln2_mod_rx_slots *rxs; | ||
| 398 | struct urb *urb = NULL; | ||
| 399 | unsigned long flags; | ||
| 400 | struct dln2_rx_context *rxc; | ||
| 401 | |||
| 402 | rxs = &dln2->mod_rx_slots[handle]; | ||
| 403 | |||
| 404 | spin_lock_irqsave(&rxs->lock, flags); | ||
| 405 | |||
| 406 | clear_bit(slot, rxs->bmap); | ||
| 407 | |||
| 408 | rxc = &rxs->slots[slot]; | ||
| 409 | rxc->in_use = false; | ||
| 410 | urb = rxc->urb; | ||
| 411 | rxc->urb = NULL; | ||
| 412 | reinit_completion(&rxc->done); | ||
| 413 | |||
| 414 | spin_unlock_irqrestore(&rxs->lock, flags); | ||
| 415 | |||
| 416 | if (urb) { | ||
| 417 | int err; | ||
| 418 | struct device *dev = &dln2->interface->dev; | ||
| 419 | |||
| 420 | err = usb_submit_urb(urb, GFP_KERNEL); | ||
| 421 | if (err < 0) | ||
| 422 | dev_err(dev, "failed to resubmit RX URB: %d\n", err); | ||
| 423 | } | ||
| 424 | |||
| 425 | wake_up_interruptible(&rxs->wq); | ||
| 426 | } | ||
| 427 | |||
| 428 | static int _dln2_transfer(struct dln2_dev *dln2, u16 handle, u16 cmd, | ||
| 429 | const void *obuf, unsigned obuf_len, | ||
| 430 | void *ibuf, unsigned *ibuf_len) | ||
| 431 | { | ||
| 432 | int ret = 0; | ||
| 433 | int rx_slot; | ||
| 434 | struct dln2_response *rsp; | ||
| 435 | struct dln2_rx_context *rxc; | ||
| 436 | struct device *dev = &dln2->interface->dev; | ||
| 437 | const unsigned long timeout = DLN2_USB_TIMEOUT * HZ / 1000; | ||
| 438 | struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[handle]; | ||
| 439 | int size; | ||
| 440 | |||
| 441 | spin_lock(&dln2->disconnect_lock); | ||
| 442 | if (!dln2->disconnect) | ||
| 443 | dln2->active_transfers++; | ||
| 444 | else | ||
| 445 | ret = -ENODEV; | ||
| 446 | spin_unlock(&dln2->disconnect_lock); | ||
| 447 | |||
| 448 | if (ret) | ||
| 449 | return ret; | ||
| 450 | |||
| 451 | rx_slot = alloc_rx_slot(dln2, handle); | ||
| 452 | if (rx_slot < 0) { | ||
| 453 | ret = rx_slot; | ||
| 454 | goto out_decr; | ||
| 455 | } | ||
| 456 | |||
| 457 | ret = dln2_send_wait(dln2, handle, cmd, rx_slot, obuf, obuf_len); | ||
| 458 | if (ret < 0) { | ||
| 459 | dev_err(dev, "USB write failed: %d\n", ret); | ||
| 460 | goto out_free_rx_slot; | ||
| 461 | } | ||
| 462 | |||
| 463 | rxc = &rxs->slots[rx_slot]; | ||
| 464 | |||
| 465 | ret = wait_for_completion_interruptible_timeout(&rxc->done, timeout); | ||
| 466 | if (ret <= 0) { | ||
| 467 | if (!ret) | ||
| 468 | ret = -ETIMEDOUT; | ||
| 469 | goto out_free_rx_slot; | ||
| 470 | } else { | ||
| 471 | ret = 0; | ||
| 472 | } | ||
| 473 | |||
| 474 | if (dln2->disconnect) { | ||
| 475 | ret = -ENODEV; | ||
| 476 | goto out_free_rx_slot; | ||
| 477 | } | ||
| 478 | |||
| 479 | /* if we got here we know that the response header has been checked */ | ||
| 480 | rsp = rxc->urb->transfer_buffer; | ||
| 481 | size = le16_to_cpu(rsp->hdr.size); | ||
| 482 | |||
| 483 | if (size < sizeof(*rsp)) { | ||
| 484 | ret = -EPROTO; | ||
| 485 | goto out_free_rx_slot; | ||
| 486 | } | ||
| 487 | |||
| 488 | if (le16_to_cpu(rsp->result) > 0x80) { | ||
| 489 | dev_dbg(dev, "%d received response with error %d\n", | ||
| 490 | handle, le16_to_cpu(rsp->result)); | ||
| 491 | ret = -EREMOTEIO; | ||
| 492 | goto out_free_rx_slot; | ||
| 493 | } | ||
| 494 | |||
| 495 | if (!ibuf) | ||
| 496 | goto out_free_rx_slot; | ||
| 497 | |||
| 498 | if (*ibuf_len > size - sizeof(*rsp)) | ||
| 499 | *ibuf_len = size - sizeof(*rsp); | ||
| 500 | |||
| 501 | memcpy(ibuf, rsp + 1, *ibuf_len); | ||
| 502 | |||
| 503 | out_free_rx_slot: | ||
| 504 | free_rx_slot(dln2, handle, rx_slot); | ||
| 505 | out_decr: | ||
| 506 | spin_lock(&dln2->disconnect_lock); | ||
| 507 | dln2->active_transfers--; | ||
| 508 | spin_unlock(&dln2->disconnect_lock); | ||
| 509 | if (dln2->disconnect) | ||
| 510 | wake_up(&dln2->disconnect_wq); | ||
| 511 | |||
| 512 | return ret; | ||
| 513 | } | ||
| 514 | |||
| 515 | int dln2_transfer(struct platform_device *pdev, u16 cmd, | ||
| 516 | const void *obuf, unsigned obuf_len, | ||
| 517 | void *ibuf, unsigned *ibuf_len) | ||
| 518 | { | ||
| 519 | struct dln2_platform_data *dln2_pdata; | ||
| 520 | struct dln2_dev *dln2; | ||
| 521 | u16 handle; | ||
| 522 | |||
| 523 | dln2 = dev_get_drvdata(pdev->dev.parent); | ||
| 524 | dln2_pdata = dev_get_platdata(&pdev->dev); | ||
| 525 | handle = dln2_pdata->handle; | ||
| 526 | |||
| 527 | return _dln2_transfer(dln2, handle, cmd, obuf, obuf_len, ibuf, | ||
| 528 | ibuf_len); | ||
| 529 | } | ||
| 530 | EXPORT_SYMBOL(dln2_transfer); | ||
| 531 | |||
| 532 | static int dln2_check_hw(struct dln2_dev *dln2) | ||
| 533 | { | ||
| 534 | int ret; | ||
| 535 | __le32 hw_type; | ||
| 536 | int len = sizeof(hw_type); | ||
| 537 | |||
| 538 | ret = _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_GET_DEVICE_VER, | ||
| 539 | NULL, 0, &hw_type, &len); | ||
| 540 | if (ret < 0) | ||
| 541 | return ret; | ||
| 542 | if (len < sizeof(hw_type)) | ||
| 543 | return -EREMOTEIO; | ||
| 544 | |||
| 545 | if (le32_to_cpu(hw_type) != DLN2_HW_ID) { | ||
| 546 | dev_err(&dln2->interface->dev, "Device ID 0x%x not supported\n", | ||
| 547 | le32_to_cpu(hw_type)); | ||
| 548 | return -ENODEV; | ||
| 549 | } | ||
| 550 | |||
| 551 | return 0; | ||
| 552 | } | ||
| 553 | |||
| 554 | static int dln2_print_serialno(struct dln2_dev *dln2) | ||
| 555 | { | ||
| 556 | int ret; | ||
| 557 | __le32 serial_no; | ||
| 558 | int len = sizeof(serial_no); | ||
| 559 | struct device *dev = &dln2->interface->dev; | ||
| 560 | |||
| 561 | ret = _dln2_transfer(dln2, DLN2_HANDLE_CTRL, CMD_GET_DEVICE_SN, NULL, 0, | ||
| 562 | &serial_no, &len); | ||
| 563 | if (ret < 0) | ||
| 564 | return ret; | ||
| 565 | if (len < sizeof(serial_no)) | ||
| 566 | return -EREMOTEIO; | ||
| 567 | |||
| 568 | dev_info(dev, "Diolan DLN2 serial %u\n", le32_to_cpu(serial_no)); | ||
| 569 | |||
| 570 | return 0; | ||
| 571 | } | ||
| 572 | |||
| 573 | static int dln2_hw_init(struct dln2_dev *dln2) | ||
| 574 | { | ||
| 575 | int ret; | ||
| 576 | |||
| 577 | ret = dln2_check_hw(dln2); | ||
| 578 | if (ret < 0) | ||
| 579 | return ret; | ||
| 580 | |||
| 581 | return dln2_print_serialno(dln2); | ||
| 582 | } | ||
| 583 | |||
| 584 | static void dln2_free_rx_urbs(struct dln2_dev *dln2) | ||
| 585 | { | ||
| 586 | int i; | ||
| 587 | |||
| 588 | for (i = 0; i < DLN2_MAX_URBS; i++) { | ||
| 589 | usb_kill_urb(dln2->rx_urb[i]); | ||
| 590 | usb_free_urb(dln2->rx_urb[i]); | ||
| 591 | kfree(dln2->rx_buf[i]); | ||
| 592 | } | ||
| 593 | } | ||
| 594 | |||
| 595 | static void dln2_free(struct dln2_dev *dln2) | ||
| 596 | { | ||
| 597 | dln2_free_rx_urbs(dln2); | ||
| 598 | usb_put_dev(dln2->usb_dev); | ||
| 599 | kfree(dln2); | ||
| 600 | } | ||
| 601 | |||
| 602 | static int dln2_setup_rx_urbs(struct dln2_dev *dln2, | ||
| 603 | struct usb_host_interface *hostif) | ||
| 604 | { | ||
| 605 | int i; | ||
| 606 | int ret; | ||
| 607 | const int rx_max_size = DLN2_RX_BUF_SIZE; | ||
| 608 | struct device *dev = &dln2->interface->dev; | ||
| 609 | |||
| 610 | for (i = 0; i < DLN2_MAX_URBS; i++) { | ||
| 611 | dln2->rx_buf[i] = kmalloc(rx_max_size, GFP_KERNEL); | ||
| 612 | if (!dln2->rx_buf[i]) | ||
| 613 | return -ENOMEM; | ||
| 614 | |||
| 615 | dln2->rx_urb[i] = usb_alloc_urb(0, GFP_KERNEL); | ||
| 616 | if (!dln2->rx_urb[i]) | ||
| 617 | return -ENOMEM; | ||
| 618 | |||
| 619 | usb_fill_bulk_urb(dln2->rx_urb[i], dln2->usb_dev, | ||
| 620 | usb_rcvbulkpipe(dln2->usb_dev, dln2->ep_in), | ||
| 621 | dln2->rx_buf[i], rx_max_size, dln2_rx, dln2); | ||
| 622 | |||
| 623 | ret = usb_submit_urb(dln2->rx_urb[i], GFP_KERNEL); | ||
| 624 | if (ret < 0) { | ||
| 625 | dev_err(dev, "failed to submit RX URB: %d\n", ret); | ||
| 626 | return ret; | ||
| 627 | } | ||
| 628 | } | ||
| 629 | |||
| 630 | return 0; | ||
| 631 | } | ||
| 632 | |||
| 633 | static struct dln2_platform_data dln2_pdata_gpio = { | ||
| 634 | .handle = DLN2_HANDLE_GPIO, | ||
| 635 | }; | ||
| 636 | |||
| 637 | /* Only one I2C port seems to be supported on current hardware */ | ||
| 638 | static struct dln2_platform_data dln2_pdata_i2c = { | ||
| 639 | .handle = DLN2_HANDLE_I2C, | ||
| 640 | .port = 0, | ||
| 641 | }; | ||
| 642 | |||
| 643 | static const struct mfd_cell dln2_devs[] = { | ||
| 644 | { | ||
| 645 | .name = "dln2-gpio", | ||
| 646 | .platform_data = &dln2_pdata_gpio, | ||
| 647 | .pdata_size = sizeof(struct dln2_platform_data), | ||
| 648 | }, | ||
| 649 | { | ||
| 650 | .name = "dln2-i2c", | ||
| 651 | .platform_data = &dln2_pdata_i2c, | ||
| 652 | .pdata_size = sizeof(struct dln2_platform_data), | ||
| 653 | }, | ||
| 654 | }; | ||
| 655 | |||
| 656 | static void dln2_disconnect(struct usb_interface *interface) | ||
| 657 | { | ||
| 658 | struct dln2_dev *dln2 = usb_get_intfdata(interface); | ||
| 659 | int i, j; | ||
| 660 | |||
| 661 | /* don't allow starting new transfers */ | ||
| 662 | spin_lock(&dln2->disconnect_lock); | ||
| 663 | dln2->disconnect = true; | ||
| 664 | spin_unlock(&dln2->disconnect_lock); | ||
| 665 | |||
| 666 | /* cancel in progress transfers */ | ||
| 667 | for (i = 0; i < DLN2_HANDLES; i++) { | ||
| 668 | struct dln2_mod_rx_slots *rxs = &dln2->mod_rx_slots[i]; | ||
| 669 | unsigned long flags; | ||
| 670 | |||
| 671 | spin_lock_irqsave(&rxs->lock, flags); | ||
| 672 | |||
| 673 | /* cancel all response waiters */ | ||
| 674 | for (j = 0; j < DLN2_MAX_RX_SLOTS; j++) { | ||
| 675 | struct dln2_rx_context *rxc = &rxs->slots[j]; | ||
| 676 | |||
| 677 | if (rxc->in_use) | ||
| 678 | complete(&rxc->done); | ||
| 679 | } | ||
| 680 | |||
| 681 | spin_unlock_irqrestore(&rxs->lock, flags); | ||
| 682 | } | ||
| 683 | |||
| 684 | /* wait for transfers to end */ | ||
| 685 | wait_event(dln2->disconnect_wq, !dln2->active_transfers); | ||
| 686 | |||
| 687 | mfd_remove_devices(&interface->dev); | ||
| 688 | |||
| 689 | dln2_free(dln2); | ||
| 690 | } | ||
| 691 | |||
| 692 | static int dln2_probe(struct usb_interface *interface, | ||
| 693 | const struct usb_device_id *usb_id) | ||
| 694 | { | ||
| 695 | struct usb_host_interface *hostif = interface->cur_altsetting; | ||
| 696 | struct device *dev = &interface->dev; | ||
| 697 | struct dln2_dev *dln2; | ||
| 698 | int ret; | ||
| 699 | int i, j; | ||
| 700 | |||
| 701 | if (hostif->desc.bInterfaceNumber != 0 || | ||
| 702 | hostif->desc.bNumEndpoints < 2) | ||
| 703 | return -ENODEV; | ||
| 704 | |||
| 705 | dln2 = kzalloc(sizeof(*dln2), GFP_KERNEL); | ||
| 706 | if (!dln2) | ||
| 707 | return -ENOMEM; | ||
| 708 | |||
| 709 | dln2->ep_out = hostif->endpoint[0].desc.bEndpointAddress; | ||
| 710 | dln2->ep_in = hostif->endpoint[1].desc.bEndpointAddress; | ||
| 711 | dln2->usb_dev = usb_get_dev(interface_to_usbdev(interface)); | ||
| 712 | dln2->interface = interface; | ||
| 713 | usb_set_intfdata(interface, dln2); | ||
| 714 | init_waitqueue_head(&dln2->disconnect_wq); | ||
| 715 | |||
| 716 | for (i = 0; i < DLN2_HANDLES; i++) { | ||
| 717 | init_waitqueue_head(&dln2->mod_rx_slots[i].wq); | ||
| 718 | spin_lock_init(&dln2->mod_rx_slots[i].lock); | ||
| 719 | for (j = 0; j < DLN2_MAX_RX_SLOTS; j++) | ||
| 720 | init_completion(&dln2->mod_rx_slots[i].slots[j].done); | ||
| 721 | } | ||
| 722 | |||
| 723 | spin_lock_init(&dln2->event_cb_lock); | ||
| 724 | spin_lock_init(&dln2->disconnect_lock); | ||
| 725 | INIT_LIST_HEAD(&dln2->event_cb_list); | ||
| 726 | |||
| 727 | ret = dln2_setup_rx_urbs(dln2, hostif); | ||
| 728 | if (ret) | ||
| 729 | goto out_cleanup; | ||
| 730 | |||
| 731 | ret = dln2_hw_init(dln2); | ||
| 732 | if (ret < 0) { | ||
| 733 | dev_err(dev, "failed to initialize hardware\n"); | ||
| 734 | goto out_cleanup; | ||
| 735 | } | ||
| 736 | |||
| 737 | ret = mfd_add_hotplug_devices(dev, dln2_devs, ARRAY_SIZE(dln2_devs)); | ||
| 738 | if (ret != 0) { | ||
| 739 | dev_err(dev, "failed to add mfd devices to core\n"); | ||
| 740 | goto out_cleanup; | ||
| 741 | } | ||
| 742 | |||
| 743 | return 0; | ||
| 744 | |||
| 745 | out_cleanup: | ||
| 746 | dln2_free(dln2); | ||
| 747 | |||
| 748 | return ret; | ||
| 749 | } | ||
| 750 | |||
| 751 | static const struct usb_device_id dln2_table[] = { | ||
| 752 | { USB_DEVICE(0xa257, 0x2013) }, | ||
| 753 | { } | ||
| 754 | }; | ||
| 755 | |||
| 756 | MODULE_DEVICE_TABLE(usb, dln2_table); | ||
| 757 | |||
| 758 | static struct usb_driver dln2_driver = { | ||
| 759 | .name = "dln2", | ||
| 760 | .probe = dln2_probe, | ||
| 761 | .disconnect = dln2_disconnect, | ||
| 762 | .id_table = dln2_table, | ||
| 763 | }; | ||
| 764 | |||
| 765 | module_usb_driver(dln2_driver); | ||
| 766 | |||
| 767 | MODULE_AUTHOR("Octavian Purdila <octavian.purdila@intel.com>"); | ||
| 768 | MODULE_DESCRIPTION("Core driver for the Diolan DLN2 interface adapter"); | ||
| 769 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/mfd/sec-core.c b/drivers/mfd/sec-core.c index dba7e2b6f8e9..b39960532f76 100644 --- a/drivers/mfd/sec-core.c +++ b/drivers/mfd/sec-core.c | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include <linux/mfd/samsung/irq.h> | 27 | #include <linux/mfd/samsung/irq.h> |
| 28 | #include <linux/mfd/samsung/s2mpa01.h> | 28 | #include <linux/mfd/samsung/s2mpa01.h> |
| 29 | #include <linux/mfd/samsung/s2mps11.h> | 29 | #include <linux/mfd/samsung/s2mps11.h> |
| 30 | #include <linux/mfd/samsung/s2mps13.h> | ||
| 30 | #include <linux/mfd/samsung/s2mps14.h> | 31 | #include <linux/mfd/samsung/s2mps14.h> |
| 31 | #include <linux/mfd/samsung/s2mpu02.h> | 32 | #include <linux/mfd/samsung/s2mpu02.h> |
| 32 | #include <linux/mfd/samsung/s5m8763.h> | 33 | #include <linux/mfd/samsung/s5m8763.h> |
| @@ -74,6 +75,15 @@ static const struct mfd_cell s2mps11_devs[] = { | |||
| 74 | } | 75 | } |
| 75 | }; | 76 | }; |
| 76 | 77 | ||
| 78 | static const struct mfd_cell s2mps13_devs[] = { | ||
| 79 | { .name = "s2mps13-pmic", }, | ||
| 80 | { .name = "s2mps13-rtc", }, | ||
| 81 | { | ||
| 82 | .name = "s2mps13-clk", | ||
| 83 | .of_compatible = "samsung,s2mps13-clk", | ||
| 84 | }, | ||
| 85 | }; | ||
| 86 | |||
| 77 | static const struct mfd_cell s2mps14_devs[] = { | 87 | static const struct mfd_cell s2mps14_devs[] = { |
| 78 | { | 88 | { |
| 79 | .name = "s2mps14-pmic", | 89 | .name = "s2mps14-pmic", |
| @@ -108,6 +118,9 @@ static const struct of_device_id sec_dt_match[] = { | |||
| 108 | .compatible = "samsung,s2mps11-pmic", | 118 | .compatible = "samsung,s2mps11-pmic", |
| 109 | .data = (void *)S2MPS11X, | 119 | .data = (void *)S2MPS11X, |
| 110 | }, { | 120 | }, { |
| 121 | .compatible = "samsung,s2mps13-pmic", | ||
| 122 | .data = (void *)S2MPS13X, | ||
| 123 | }, { | ||
| 111 | .compatible = "samsung,s2mps14-pmic", | 124 | .compatible = "samsung,s2mps14-pmic", |
| 112 | .data = (void *)S2MPS14X, | 125 | .data = (void *)S2MPS14X, |
| 113 | }, { | 126 | }, { |
| @@ -194,6 +207,15 @@ static const struct regmap_config s2mps11_regmap_config = { | |||
| 194 | .cache_type = REGCACHE_FLAT, | 207 | .cache_type = REGCACHE_FLAT, |
| 195 | }; | 208 | }; |
| 196 | 209 | ||
| 210 | static const struct regmap_config s2mps13_regmap_config = { | ||
| 211 | .reg_bits = 8, | ||
| 212 | .val_bits = 8, | ||
| 213 | |||
| 214 | .max_register = S2MPS13_REG_LDODSCH5, | ||
| 215 | .volatile_reg = s2mps11_volatile, | ||
| 216 | .cache_type = REGCACHE_FLAT, | ||
| 217 | }; | ||
| 218 | |||
| 197 | static const struct regmap_config s2mps14_regmap_config = { | 219 | static const struct regmap_config s2mps14_regmap_config = { |
| 198 | .reg_bits = 8, | 220 | .reg_bits = 8, |
| 199 | .val_bits = 8, | 221 | .val_bits = 8, |
| @@ -325,6 +347,9 @@ static int sec_pmic_probe(struct i2c_client *i2c, | |||
| 325 | case S2MPS11X: | 347 | case S2MPS11X: |
| 326 | regmap = &s2mps11_regmap_config; | 348 | regmap = &s2mps11_regmap_config; |
| 327 | break; | 349 | break; |
| 350 | case S2MPS13X: | ||
| 351 | regmap = &s2mps13_regmap_config; | ||
| 352 | break; | ||
| 328 | case S2MPS14X: | 353 | case S2MPS14X: |
| 329 | regmap = &s2mps14_regmap_config; | 354 | regmap = &s2mps14_regmap_config; |
| 330 | break; | 355 | break; |
| @@ -378,6 +403,10 @@ static int sec_pmic_probe(struct i2c_client *i2c, | |||
| 378 | sec_devs = s2mps11_devs; | 403 | sec_devs = s2mps11_devs; |
| 379 | num_sec_devs = ARRAY_SIZE(s2mps11_devs); | 404 | num_sec_devs = ARRAY_SIZE(s2mps11_devs); |
| 380 | break; | 405 | break; |
| 406 | case S2MPS13X: | ||
| 407 | sec_devs = s2mps13_devs; | ||
| 408 | num_sec_devs = ARRAY_SIZE(s2mps13_devs); | ||
| 409 | break; | ||
| 381 | case S2MPS14X: | 410 | case S2MPS14X: |
| 382 | sec_devs = s2mps14_devs; | 411 | sec_devs = s2mps14_devs; |
| 383 | num_sec_devs = ARRAY_SIZE(s2mps14_devs); | 412 | num_sec_devs = ARRAY_SIZE(s2mps14_devs); |
diff --git a/drivers/mfd/sec-irq.c b/drivers/mfd/sec-irq.c index f9a57869e3ec..ba86a918c2da 100644 --- a/drivers/mfd/sec-irq.c +++ b/drivers/mfd/sec-irq.c | |||
| @@ -389,14 +389,22 @@ static const struct regmap_irq_chip s2mps11_irq_chip = { | |||
| 389 | .ack_base = S2MPS11_REG_INT1, | 389 | .ack_base = S2MPS11_REG_INT1, |
| 390 | }; | 390 | }; |
| 391 | 391 | ||
| 392 | #define S2MPS1X_IRQ_CHIP_COMMON_DATA \ | ||
| 393 | .irqs = s2mps14_irqs, \ | ||
| 394 | .num_irqs = ARRAY_SIZE(s2mps14_irqs), \ | ||
| 395 | .num_regs = 3, \ | ||
| 396 | .status_base = S2MPS14_REG_INT1, \ | ||
| 397 | .mask_base = S2MPS14_REG_INT1M, \ | ||
| 398 | .ack_base = S2MPS14_REG_INT1 \ | ||
| 399 | |||
| 400 | static const struct regmap_irq_chip s2mps13_irq_chip = { | ||
| 401 | .name = "s2mps13", | ||
| 402 | S2MPS1X_IRQ_CHIP_COMMON_DATA, | ||
| 403 | }; | ||
| 404 | |||
| 392 | static const struct regmap_irq_chip s2mps14_irq_chip = { | 405 | static const struct regmap_irq_chip s2mps14_irq_chip = { |
| 393 | .name = "s2mps14", | 406 | .name = "s2mps14", |
| 394 | .irqs = s2mps14_irqs, | 407 | S2MPS1X_IRQ_CHIP_COMMON_DATA, |
| 395 | .num_irqs = ARRAY_SIZE(s2mps14_irqs), | ||
| 396 | .num_regs = 3, | ||
| 397 | .status_base = S2MPS14_REG_INT1, | ||
| 398 | .mask_base = S2MPS14_REG_INT1M, | ||
| 399 | .ack_base = S2MPS14_REG_INT1, | ||
| 400 | }; | 408 | }; |
| 401 | 409 | ||
| 402 | static const struct regmap_irq_chip s2mpu02_irq_chip = { | 410 | static const struct regmap_irq_chip s2mpu02_irq_chip = { |
| @@ -452,6 +460,9 @@ int sec_irq_init(struct sec_pmic_dev *sec_pmic) | |||
| 452 | case S2MPS11X: | 460 | case S2MPS11X: |
| 453 | sec_irq_chip = &s2mps11_irq_chip; | 461 | sec_irq_chip = &s2mps11_irq_chip; |
| 454 | break; | 462 | break; |
| 463 | case S2MPS13X: | ||
| 464 | sec_irq_chip = &s2mps13_irq_chip; | ||
| 465 | break; | ||
| 455 | case S2MPS14X: | 466 | case S2MPS14X: |
| 456 | sec_irq_chip = &s2mps14_irq_chip; | 467 | sec_irq_chip = &s2mps14_irq_chip; |
| 457 | break; | 468 | break; |
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 55d7b7b0f2e0..5e061347be93 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig | |||
| @@ -529,13 +529,13 @@ config REGULATOR_S2MPA01 | |||
| 529 | via I2C bus. S2MPA01 has 10 Bucks and 26 LDO outputs. | 529 | via I2C bus. S2MPA01 has 10 Bucks and 26 LDO outputs. |
| 530 | 530 | ||
| 531 | config REGULATOR_S2MPS11 | 531 | config REGULATOR_S2MPS11 |
| 532 | tristate "Samsung S2MPS11/S2MPS14/S2MPU02 voltage regulator" | 532 | tristate "Samsung S2MPS11/S2MPS13/S2MPS14/S2MPU02 voltage regulator" |
| 533 | depends on MFD_SEC_CORE | 533 | depends on MFD_SEC_CORE |
| 534 | help | 534 | help |
| 535 | This driver supports a Samsung S2MPS11/S2MPS14/S2MPU02 voltage output | 535 | This driver supports a Samsung S2MPS11/S2MPS13/S2MPS14/S2MPU02 voltage |
| 536 | regulator via I2C bus. The chip is comprised of high efficient Buck | 536 | output regulator via I2C bus. The chip is comprised of high efficient |
| 537 | converters including Dual-Phase Buck converter, Buck-Boost converter, | 537 | Buck converters including Dual-Phase Buck converter, Buck-Boost |
| 538 | various LDOs. | 538 | converter, various LDOs. |
| 539 | 539 | ||
| 540 | config REGULATOR_S5M8767 | 540 | config REGULATOR_S5M8767 |
| 541 | tristate "Samsung S5M8767A voltage regulator" | 541 | tristate "Samsung S5M8767A voltage regulator" |
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index adab82d5279f..738dc7763d47 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c | |||
| @@ -30,6 +30,7 @@ | |||
| 30 | #include <linux/of_gpio.h> | 30 | #include <linux/of_gpio.h> |
| 31 | #include <linux/mfd/samsung/core.h> | 31 | #include <linux/mfd/samsung/core.h> |
| 32 | #include <linux/mfd/samsung/s2mps11.h> | 32 | #include <linux/mfd/samsung/s2mps11.h> |
| 33 | #include <linux/mfd/samsung/s2mps13.h> | ||
| 33 | #include <linux/mfd/samsung/s2mps14.h> | 34 | #include <linux/mfd/samsung/s2mps14.h> |
| 34 | #include <linux/mfd/samsung/s2mpu02.h> | 35 | #include <linux/mfd/samsung/s2mpu02.h> |
| 35 | 36 | ||
| @@ -45,10 +46,10 @@ struct s2mps11_info { | |||
| 45 | enum sec_device_type dev_type; | 46 | enum sec_device_type dev_type; |
| 46 | 47 | ||
| 47 | /* | 48 | /* |
| 48 | * One bit for each S2MPS14/S2MPU02 regulator whether the suspend mode | 49 | * One bit for each S2MPS13/S2MPS14/S2MPU02 regulator whether |
| 49 | * was enabled. | 50 | * the suspend mode was enabled. |
| 50 | */ | 51 | */ |
| 51 | unsigned long long s2mps14_suspend_state:35; | 52 | unsigned long long s2mps14_suspend_state:50; |
| 52 | 53 | ||
| 53 | /* Array of size rdev_num with GPIO-s for external sleep control */ | 54 | /* Array of size rdev_num with GPIO-s for external sleep control */ |
| 54 | int *ext_control_gpio; | 55 | int *ext_control_gpio; |
| @@ -369,12 +370,101 @@ static const struct regulator_desc s2mps11_regulators[] = { | |||
| 369 | regulator_desc_s2mps11_buck6_10(10, MIN_750_MV, STEP_12_5_MV), | 370 | regulator_desc_s2mps11_buck6_10(10, MIN_750_MV, STEP_12_5_MV), |
| 370 | }; | 371 | }; |
| 371 | 372 | ||
| 373 | static struct regulator_ops s2mps14_reg_ops; | ||
| 374 | |||
| 375 | #define regulator_desc_s2mps13_ldo(num, min, step, min_sel) { \ | ||
| 376 | .name = "LDO"#num, \ | ||
| 377 | .id = S2MPS13_LDO##num, \ | ||
| 378 | .ops = &s2mps14_reg_ops, \ | ||
| 379 | .type = REGULATOR_VOLTAGE, \ | ||
| 380 | .owner = THIS_MODULE, \ | ||
| 381 | .min_uV = min, \ | ||
| 382 | .uV_step = step, \ | ||
| 383 | .linear_min_sel = min_sel, \ | ||
| 384 | .n_voltages = S2MPS14_LDO_N_VOLTAGES, \ | ||
| 385 | .vsel_reg = S2MPS13_REG_L1CTRL + num - 1, \ | ||
| 386 | .vsel_mask = S2MPS14_LDO_VSEL_MASK, \ | ||
| 387 | .enable_reg = S2MPS13_REG_L1CTRL + num - 1, \ | ||
| 388 | .enable_mask = S2MPS14_ENABLE_MASK \ | ||
| 389 | } | ||
| 390 | |||
| 391 | #define regulator_desc_s2mps13_buck(num, min, step, min_sel) { \ | ||
| 392 | .name = "BUCK"#num, \ | ||
| 393 | .id = S2MPS13_BUCK##num, \ | ||
| 394 | .ops = &s2mps14_reg_ops, \ | ||
| 395 | .type = REGULATOR_VOLTAGE, \ | ||
| 396 | .owner = THIS_MODULE, \ | ||
| 397 | .min_uV = min, \ | ||
| 398 | .uV_step = step, \ | ||
| 399 | .linear_min_sel = min_sel, \ | ||
| 400 | .n_voltages = S2MPS14_BUCK_N_VOLTAGES, \ | ||
| 401 | .ramp_delay = S2MPS13_BUCK_RAMP_DELAY, \ | ||
| 402 | .vsel_reg = S2MPS13_REG_B1OUT + (num - 1) * 2, \ | ||
| 403 | .vsel_mask = S2MPS14_BUCK_VSEL_MASK, \ | ||
| 404 | .enable_reg = S2MPS13_REG_B1CTRL + (num - 1) * 2, \ | ||
| 405 | .enable_mask = S2MPS14_ENABLE_MASK \ | ||
| 406 | } | ||
| 407 | |||
| 408 | static const struct regulator_desc s2mps13_regulators[] = { | ||
| 409 | regulator_desc_s2mps13_ldo(1, MIN_800_MV, STEP_12_5_MV, 0x00), | ||
| 410 | regulator_desc_s2mps13_ldo(2, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 411 | regulator_desc_s2mps13_ldo(3, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 412 | regulator_desc_s2mps13_ldo(4, MIN_800_MV, STEP_12_5_MV, 0x00), | ||
| 413 | regulator_desc_s2mps13_ldo(5, MIN_800_MV, STEP_12_5_MV, 0x00), | ||
| 414 | regulator_desc_s2mps13_ldo(6, MIN_800_MV, STEP_12_5_MV, 0x00), | ||
| 415 | regulator_desc_s2mps13_ldo(7, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 416 | regulator_desc_s2mps13_ldo(8, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 417 | regulator_desc_s2mps13_ldo(9, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 418 | regulator_desc_s2mps13_ldo(10, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 419 | regulator_desc_s2mps13_ldo(11, MIN_800_MV, STEP_25_MV, 0x10), | ||
| 420 | regulator_desc_s2mps13_ldo(12, MIN_800_MV, STEP_25_MV, 0x10), | ||
| 421 | regulator_desc_s2mps13_ldo(13, MIN_800_MV, STEP_25_MV, 0x10), | ||
| 422 | regulator_desc_s2mps13_ldo(14, MIN_800_MV, STEP_12_5_MV, 0x00), | ||
| 423 | regulator_desc_s2mps13_ldo(15, MIN_800_MV, STEP_12_5_MV, 0x00), | ||
| 424 | regulator_desc_s2mps13_ldo(16, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 425 | regulator_desc_s2mps13_ldo(17, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 426 | regulator_desc_s2mps13_ldo(18, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 427 | regulator_desc_s2mps13_ldo(19, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 428 | regulator_desc_s2mps13_ldo(20, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 429 | regulator_desc_s2mps13_ldo(21, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 430 | regulator_desc_s2mps13_ldo(22, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 431 | regulator_desc_s2mps13_ldo(23, MIN_800_MV, STEP_12_5_MV, 0x00), | ||
| 432 | regulator_desc_s2mps13_ldo(24, MIN_800_MV, STEP_12_5_MV, 0x00), | ||
| 433 | regulator_desc_s2mps13_ldo(25, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 434 | regulator_desc_s2mps13_ldo(26, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 435 | regulator_desc_s2mps13_ldo(27, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 436 | regulator_desc_s2mps13_ldo(28, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 437 | regulator_desc_s2mps13_ldo(29, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 438 | regulator_desc_s2mps13_ldo(30, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 439 | regulator_desc_s2mps13_ldo(31, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 440 | regulator_desc_s2mps13_ldo(32, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 441 | regulator_desc_s2mps13_ldo(33, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 442 | regulator_desc_s2mps13_ldo(34, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 443 | regulator_desc_s2mps13_ldo(35, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 444 | regulator_desc_s2mps13_ldo(36, MIN_800_MV, STEP_12_5_MV, 0x00), | ||
| 445 | regulator_desc_s2mps13_ldo(37, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 446 | regulator_desc_s2mps13_ldo(38, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 447 | regulator_desc_s2mps13_ldo(39, MIN_1000_MV, STEP_25_MV, 0x08), | ||
| 448 | regulator_desc_s2mps13_ldo(40, MIN_1400_MV, STEP_50_MV, 0x0C), | ||
| 449 | regulator_desc_s2mps13_buck(1, MIN_500_MV, STEP_6_25_MV, 0x10), | ||
| 450 | regulator_desc_s2mps13_buck(2, MIN_500_MV, STEP_6_25_MV, 0x10), | ||
| 451 | regulator_desc_s2mps13_buck(3, MIN_500_MV, STEP_6_25_MV, 0x10), | ||
| 452 | regulator_desc_s2mps13_buck(4, MIN_500_MV, STEP_6_25_MV, 0x10), | ||
| 453 | regulator_desc_s2mps13_buck(5, MIN_500_MV, STEP_6_25_MV, 0x10), | ||
| 454 | regulator_desc_s2mps13_buck(6, MIN_500_MV, STEP_6_25_MV, 0x10), | ||
| 455 | regulator_desc_s2mps13_buck(7, MIN_500_MV, STEP_6_25_MV, 0x10), | ||
| 456 | regulator_desc_s2mps13_buck(8, MIN_1000_MV, STEP_12_5_MV, 0x20), | ||
| 457 | regulator_desc_s2mps13_buck(9, MIN_1000_MV, STEP_12_5_MV, 0x20), | ||
| 458 | regulator_desc_s2mps13_buck(10, MIN_500_MV, STEP_6_25_MV, 0x10), | ||
| 459 | }; | ||
| 460 | |||
| 372 | static int s2mps14_regulator_enable(struct regulator_dev *rdev) | 461 | static int s2mps14_regulator_enable(struct regulator_dev *rdev) |
| 373 | { | 462 | { |
| 374 | struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev); | 463 | struct s2mps11_info *s2mps11 = rdev_get_drvdata(rdev); |
| 375 | unsigned int val; | 464 | unsigned int val; |
| 376 | 465 | ||
| 377 | switch (s2mps11->dev_type) { | 466 | switch (s2mps11->dev_type) { |
| 467 | case S2MPS13X: | ||
| 378 | case S2MPS14X: | 468 | case S2MPS14X: |
| 379 | if (s2mps11->s2mps14_suspend_state & (1 << rdev_get_id(rdev))) | 469 | if (s2mps11->s2mps14_suspend_state & (1 << rdev_get_id(rdev))) |
| 380 | val = S2MPS14_ENABLE_SUSPEND; | 470 | val = S2MPS14_ENABLE_SUSPEND; |
| @@ -406,6 +496,7 @@ static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev) | |||
| 406 | 496 | ||
| 407 | /* Below LDO should be always on or does not support suspend mode. */ | 497 | /* Below LDO should be always on or does not support suspend mode. */ |
| 408 | switch (s2mps11->dev_type) { | 498 | switch (s2mps11->dev_type) { |
| 499 | case S2MPS13X: | ||
| 409 | case S2MPS14X: | 500 | case S2MPS14X: |
| 410 | switch (rdev_id) { | 501 | switch (rdev_id) { |
| 411 | case S2MPS14_LDO3: | 502 | case S2MPS14_LDO3: |
| @@ -831,6 +922,10 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) | |||
| 831 | s2mps11->rdev_num = ARRAY_SIZE(s2mps11_regulators); | 922 | s2mps11->rdev_num = ARRAY_SIZE(s2mps11_regulators); |
| 832 | regulators = s2mps11_regulators; | 923 | regulators = s2mps11_regulators; |
| 833 | break; | 924 | break; |
| 925 | case S2MPS13X: | ||
| 926 | s2mps11->rdev_num = ARRAY_SIZE(s2mps13_regulators); | ||
| 927 | regulators = s2mps13_regulators; | ||
| 928 | break; | ||
| 834 | case S2MPS14X: | 929 | case S2MPS14X: |
| 835 | s2mps11->rdev_num = ARRAY_SIZE(s2mps14_regulators); | 930 | s2mps11->rdev_num = ARRAY_SIZE(s2mps14_regulators); |
| 836 | regulators = s2mps14_regulators; | 931 | regulators = s2mps14_regulators; |
| @@ -927,6 +1022,7 @@ out: | |||
| 927 | 1022 | ||
| 928 | static const struct platform_device_id s2mps11_pmic_id[] = { | 1023 | static const struct platform_device_id s2mps11_pmic_id[] = { |
| 929 | { "s2mps11-pmic", S2MPS11X}, | 1024 | { "s2mps11-pmic", S2MPS11X}, |
| 1025 | { "s2mps13-pmic", S2MPS13X}, | ||
| 930 | { "s2mps14-pmic", S2MPS14X}, | 1026 | { "s2mps14-pmic", S2MPS14X}, |
| 931 | { "s2mpu02-pmic", S2MPU02}, | 1027 | { "s2mpu02-pmic", S2MPU02}, |
| 932 | { }, | 1028 | { }, |
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index d0e31a2287ac..81589d176ae8 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | enum { | 14 | enum { |
| 15 | AXP202_ID = 0, | 15 | AXP202_ID = 0, |
| 16 | AXP209_ID, | 16 | AXP209_ID, |
| 17 | AXP288_ID, | ||
| 18 | NR_AXP20X_VARIANTS, | ||
| 17 | }; | 19 | }; |
| 18 | 20 | ||
| 19 | #define AXP20X_DATACACHE(m) (0x04 + (m)) | 21 | #define AXP20X_DATACACHE(m) (0x04 + (m)) |
| @@ -49,11 +51,13 @@ enum { | |||
| 49 | #define AXP20X_IRQ3_EN 0x42 | 51 | #define AXP20X_IRQ3_EN 0x42 |
| 50 | #define AXP20X_IRQ4_EN 0x43 | 52 | #define AXP20X_IRQ4_EN 0x43 |
| 51 | #define AXP20X_IRQ5_EN 0x44 | 53 | #define AXP20X_IRQ5_EN 0x44 |
| 54 | #define AXP20X_IRQ6_EN 0x45 | ||
| 52 | #define AXP20X_IRQ1_STATE 0x48 | 55 | #define AXP20X_IRQ1_STATE 0x48 |
| 53 | #define AXP20X_IRQ2_STATE 0x49 | 56 | #define AXP20X_IRQ2_STATE 0x49 |
| 54 | #define AXP20X_IRQ3_STATE 0x4a | 57 | #define AXP20X_IRQ3_STATE 0x4a |
| 55 | #define AXP20X_IRQ4_STATE 0x4b | 58 | #define AXP20X_IRQ4_STATE 0x4b |
| 56 | #define AXP20X_IRQ5_STATE 0x4c | 59 | #define AXP20X_IRQ5_STATE 0x4c |
| 60 | #define AXP20X_IRQ6_STATE 0x4d | ||
| 57 | 61 | ||
| 58 | /* ADC */ | 62 | /* ADC */ |
| 59 | #define AXP20X_ACIN_V_ADC_H 0x56 | 63 | #define AXP20X_ACIN_V_ADC_H 0x56 |
| @@ -116,6 +120,15 @@ enum { | |||
| 116 | #define AXP20X_CC_CTRL 0xb8 | 120 | #define AXP20X_CC_CTRL 0xb8 |
| 117 | #define AXP20X_FG_RES 0xb9 | 121 | #define AXP20X_FG_RES 0xb9 |
| 118 | 122 | ||
| 123 | /* AXP288 specific registers */ | ||
| 124 | #define AXP288_PMIC_ADC_H 0x56 | ||
| 125 | #define AXP288_PMIC_ADC_L 0x57 | ||
| 126 | #define AXP288_ADC_TS_PIN_CTRL 0x84 | ||
| 127 | |||
| 128 | #define AXP288_PMIC_ADC_EN 0x84 | ||
| 129 | #define AXP288_FG_TUNE5 0xed | ||
| 130 | |||
| 131 | |||
| 119 | /* Regulators IDs */ | 132 | /* Regulators IDs */ |
| 120 | enum { | 133 | enum { |
| 121 | AXP20X_LDO1 = 0, | 134 | AXP20X_LDO1 = 0, |
| @@ -169,12 +182,58 @@ enum { | |||
| 169 | AXP20X_IRQ_GPIO0_INPUT, | 182 | AXP20X_IRQ_GPIO0_INPUT, |
| 170 | }; | 183 | }; |
| 171 | 184 | ||
| 185 | enum axp288_irqs { | ||
| 186 | AXP288_IRQ_VBUS_FALL = 2, | ||
| 187 | AXP288_IRQ_VBUS_RISE, | ||
| 188 | AXP288_IRQ_OV, | ||
| 189 | AXP288_IRQ_FALLING_ALT, | ||
| 190 | AXP288_IRQ_RISING_ALT, | ||
| 191 | AXP288_IRQ_OV_ALT, | ||
| 192 | AXP288_IRQ_DONE = 10, | ||
| 193 | AXP288_IRQ_CHARGING, | ||
| 194 | AXP288_IRQ_SAFE_QUIT, | ||
| 195 | AXP288_IRQ_SAFE_ENTER, | ||
| 196 | AXP288_IRQ_ABSENT, | ||
| 197 | AXP288_IRQ_APPEND, | ||
| 198 | AXP288_IRQ_QWBTU, | ||
| 199 | AXP288_IRQ_WBTU, | ||
| 200 | AXP288_IRQ_QWBTO, | ||
| 201 | AXP288_IRQ_WBTO, | ||
| 202 | AXP288_IRQ_QCBTU, | ||
| 203 | AXP288_IRQ_CBTU, | ||
| 204 | AXP288_IRQ_QCBTO, | ||
| 205 | AXP288_IRQ_CBTO, | ||
| 206 | AXP288_IRQ_WL2, | ||
| 207 | AXP288_IRQ_WL1, | ||
| 208 | AXP288_IRQ_GPADC, | ||
| 209 | AXP288_IRQ_OT = 31, | ||
| 210 | AXP288_IRQ_GPIO0, | ||
| 211 | AXP288_IRQ_GPIO1, | ||
| 212 | AXP288_IRQ_POKO, | ||
| 213 | AXP288_IRQ_POKL, | ||
| 214 | AXP288_IRQ_POKS, | ||
| 215 | AXP288_IRQ_POKN, | ||
| 216 | AXP288_IRQ_POKP, | ||
| 217 | AXP288_IRQ_TIMER, | ||
| 218 | AXP288_IRQ_MV_CHNG, | ||
| 219 | AXP288_IRQ_BC_USB_CHNG, | ||
| 220 | }; | ||
| 221 | |||
| 222 | #define AXP288_TS_ADC_H 0x58 | ||
| 223 | #define AXP288_TS_ADC_L 0x59 | ||
| 224 | #define AXP288_GP_ADC_H 0x5a | ||
| 225 | #define AXP288_GP_ADC_L 0x5b | ||
| 226 | |||
| 172 | struct axp20x_dev { | 227 | struct axp20x_dev { |
| 173 | struct device *dev; | 228 | struct device *dev; |
| 174 | struct i2c_client *i2c_client; | 229 | struct i2c_client *i2c_client; |
| 175 | struct regmap *regmap; | 230 | struct regmap *regmap; |
| 176 | struct regmap_irq_chip_data *regmap_irqc; | 231 | struct regmap_irq_chip_data *regmap_irqc; |
| 177 | long variant; | 232 | long variant; |
| 233 | int nr_cells; | ||
| 234 | struct mfd_cell *cells; | ||
| 235 | const struct regmap_config *regmap_cfg; | ||
| 236 | const struct regmap_irq_chip *regmap_irq_chip; | ||
| 178 | }; | 237 | }; |
| 179 | 238 | ||
| 180 | #endif /* __LINUX_MFD_AXP20X_H */ | 239 | #endif /* __LINUX_MFD_AXP20X_H */ |
diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 73e1709d4c09..a76bc100bf97 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h | |||
| @@ -111,6 +111,13 @@ extern int mfd_add_devices(struct device *parent, int id, | |||
| 111 | struct resource *mem_base, | 111 | struct resource *mem_base, |
| 112 | int irq_base, struct irq_domain *irq_domain); | 112 | int irq_base, struct irq_domain *irq_domain); |
| 113 | 113 | ||
| 114 | static inline int mfd_add_hotplug_devices(struct device *parent, | ||
| 115 | const struct mfd_cell *cells, int n_devs) | ||
| 116 | { | ||
| 117 | return mfd_add_devices(parent, PLATFORM_DEVID_AUTO, cells, n_devs, | ||
| 118 | NULL, 0, NULL); | ||
| 119 | } | ||
| 120 | |||
| 114 | extern void mfd_remove_devices(struct device *parent); | 121 | extern void mfd_remove_devices(struct device *parent); |
| 115 | 122 | ||
| 116 | #endif | 123 | #endif |
diff --git a/include/linux/mfd/dln2.h b/include/linux/mfd/dln2.h new file mode 100644 index 000000000000..004b24576da8 --- /dev/null +++ b/include/linux/mfd/dln2.h | |||
| @@ -0,0 +1,103 @@ | |||
| 1 | #ifndef __LINUX_USB_DLN2_H | ||
| 2 | #define __LINUX_USB_DLN2_H | ||
| 3 | |||
| 4 | #define DLN2_CMD(cmd, id) ((cmd) | ((id) << 8)) | ||
| 5 | |||
| 6 | struct dln2_platform_data { | ||
| 7 | u16 handle; /* sub-driver handle (internally used only) */ | ||
| 8 | u8 port; /* I2C/SPI port */ | ||
| 9 | }; | ||
| 10 | |||
| 11 | /** | ||
| 12 | * dln2_event_cb_t - event callback function signature | ||
| 13 | * | ||
| 14 | * @pdev - the sub-device that registered this callback | ||
| 15 | * @echo - the echo header field received in the message | ||
| 16 | * @data - the data payload | ||
| 17 | * @len - the data payload length | ||
| 18 | * | ||
| 19 | * The callback function is called in interrupt context and the data payload is | ||
| 20 | * only valid during the call. If the user needs later access of the data, it | ||
| 21 | * must copy it. | ||
| 22 | */ | ||
| 23 | |||
| 24 | typedef void (*dln2_event_cb_t)(struct platform_device *pdev, u16 echo, | ||
| 25 | const void *data, int len); | ||
| 26 | |||
| 27 | /** | ||
| 28 | * dl2n_register_event_cb - register a callback function for an event | ||
| 29 | * | ||
| 30 | * @pdev - the sub-device that registers the callback | ||
| 31 | * @event - the event for which to register a callback | ||
| 32 | * @event_cb - the callback function | ||
| 33 | * | ||
| 34 | * @return 0 in case of success, negative value in case of error | ||
| 35 | */ | ||
| 36 | int dln2_register_event_cb(struct platform_device *pdev, u16 event, | ||
| 37 | dln2_event_cb_t event_cb); | ||
| 38 | |||
| 39 | /** | ||
| 40 | * dln2_unregister_event_cb - unregister the callback function for an event | ||
| 41 | * | ||
| 42 | * @pdev - the sub-device that registered the callback | ||
| 43 | * @event - the event for which to register a callback | ||
| 44 | */ | ||
| 45 | void dln2_unregister_event_cb(struct platform_device *pdev, u16 event); | ||
| 46 | |||
| 47 | /** | ||
| 48 | * dln2_transfer - issue a DLN2 command and wait for a response and the | ||
| 49 | * associated data | ||
| 50 | * | ||
| 51 | * @pdev - the sub-device which is issuing this transfer | ||
| 52 | * @cmd - the command to be sent to the device | ||
| 53 | * @obuf - the buffer to be sent to the device; it can be NULL if the user | ||
| 54 | * doesn't need to transmit data with this command | ||
| 55 | * @obuf_len - the size of the buffer to be sent to the device | ||
| 56 | * @ibuf - any data associated with the response will be copied here; it can be | ||
| 57 | * NULL if the user doesn't need the response data | ||
| 58 | * @ibuf_len - must be initialized to the input buffer size; it will be modified | ||
| 59 | * to indicate the actual data transferred; | ||
| 60 | * | ||
| 61 | * @return 0 for success, negative value for errors | ||
| 62 | */ | ||
| 63 | int dln2_transfer(struct platform_device *pdev, u16 cmd, | ||
| 64 | const void *obuf, unsigned obuf_len, | ||
| 65 | void *ibuf, unsigned *ibuf_len); | ||
| 66 | |||
| 67 | /** | ||
| 68 | * dln2_transfer_rx - variant of @dln2_transfer() where TX buffer is not needed | ||
| 69 | * | ||
| 70 | * @pdev - the sub-device which is issuing this transfer | ||
| 71 | * @cmd - the command to be sent to the device | ||
| 72 | * @ibuf - any data associated with the response will be copied here; it can be | ||
| 73 | * NULL if the user doesn't need the response data | ||
| 74 | * @ibuf_len - must be initialized to the input buffer size; it will be modified | ||
| 75 | * to indicate the actual data transferred; | ||
| 76 | * | ||
| 77 | * @return 0 for success, negative value for errors | ||
| 78 | */ | ||
| 79 | |||
| 80 | static inline int dln2_transfer_rx(struct platform_device *pdev, u16 cmd, | ||
| 81 | void *ibuf, unsigned *ibuf_len) | ||
| 82 | { | ||
| 83 | return dln2_transfer(pdev, cmd, NULL, 0, ibuf, ibuf_len); | ||
| 84 | } | ||
| 85 | |||
| 86 | /** | ||
| 87 | * dln2_transfer_tx - variant of @dln2_transfer() where RX buffer is not needed | ||
| 88 | * | ||
| 89 | * @pdev - the sub-device which is issuing this transfer | ||
| 90 | * @cmd - the command to be sent to the device | ||
| 91 | * @obuf - the buffer to be sent to the device; it can be NULL if the | ||
| 92 | * user doesn't need to transmit data with this command | ||
| 93 | * @obuf_len - the size of the buffer to be sent to the device | ||
| 94 | * | ||
| 95 | * @return 0 for success, negative value for errors | ||
| 96 | */ | ||
| 97 | static inline int dln2_transfer_tx(struct platform_device *pdev, u16 cmd, | ||
| 98 | const void *obuf, unsigned obuf_len) | ||
| 99 | { | ||
| 100 | return dln2_transfer(pdev, cmd, obuf, obuf_len, NULL, NULL); | ||
| 101 | } | ||
| 102 | |||
| 103 | #endif | ||
diff --git a/include/linux/mfd/samsung/core.h b/include/linux/mfd/samsung/core.h index 1825edacbda7..3fdb7cfbffb3 100644 --- a/include/linux/mfd/samsung/core.h +++ b/include/linux/mfd/samsung/core.h | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #define MIN_800_MV 800000 | 28 | #define MIN_800_MV 800000 |
| 29 | #define MIN_750_MV 750000 | 29 | #define MIN_750_MV 750000 |
| 30 | #define MIN_600_MV 600000 | 30 | #define MIN_600_MV 600000 |
| 31 | #define MIN_500_MV 500000 | ||
| 31 | 32 | ||
| 32 | /* Macros to represent steps for LDO/BUCK */ | 33 | /* Macros to represent steps for LDO/BUCK */ |
| 33 | #define STEP_50_MV 50000 | 34 | #define STEP_50_MV 50000 |
| @@ -41,6 +42,7 @@ enum sec_device_type { | |||
| 41 | S5M8767X, | 42 | S5M8767X, |
| 42 | S2MPA01, | 43 | S2MPA01, |
| 43 | S2MPS11X, | 44 | S2MPS11X, |
| 45 | S2MPS13X, | ||
| 44 | S2MPS14X, | 46 | S2MPS14X, |
| 45 | S2MPU02, | 47 | S2MPU02, |
| 46 | }; | 48 | }; |
diff --git a/include/linux/mfd/samsung/s2mps13.h b/include/linux/mfd/samsung/s2mps13.h new file mode 100644 index 000000000000..ce5dda8958fe --- /dev/null +++ b/include/linux/mfd/samsung/s2mps13.h | |||
| @@ -0,0 +1,186 @@ | |||
| 1 | /* | ||
| 2 | * s2mps13.h | ||
| 3 | * | ||
| 4 | * Copyright (c) 2014 Samsung Electronics Co., Ltd | ||
| 5 | * http://www.samsung.com | ||
| 6 | * | ||
| 7 | * This program is free software; you can redistribute it and/or modify it | ||
| 8 | * under the terms of the GNU General Public License as published by the | ||
| 9 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 10 | * option) any later version. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, | ||
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | * GNU General Public License for more details. | ||
| 16 | * | ||
| 17 | */ | ||
| 18 | |||
| 19 | #ifndef __LINUX_MFD_S2MPS13_H | ||
| 20 | #define __LINUX_MFD_S2MPS13_H | ||
| 21 | |||
| 22 | /* S2MPS13 registers */ | ||
| 23 | enum s2mps13_reg { | ||
| 24 | S2MPS13_REG_ID, | ||
| 25 | S2MPS13_REG_INT1, | ||
| 26 | S2MPS13_REG_INT2, | ||
| 27 | S2MPS13_REG_INT3, | ||
| 28 | S2MPS13_REG_INT1M, | ||
| 29 | S2MPS13_REG_INT2M, | ||
| 30 | S2MPS13_REG_INT3M, | ||
| 31 | S2MPS13_REG_ST1, | ||
| 32 | S2MPS13_REG_ST2, | ||
| 33 | S2MPS13_REG_PWRONSRC, | ||
| 34 | S2MPS13_REG_OFFSRC, | ||
| 35 | S2MPS13_REG_BU_CHG, | ||
| 36 | S2MPS13_REG_RTCCTRL, | ||
| 37 | S2MPS13_REG_CTRL1, | ||
| 38 | S2MPS13_REG_CTRL2, | ||
| 39 | S2MPS13_REG_RSVD1, | ||
| 40 | S2MPS13_REG_RSVD2, | ||
| 41 | S2MPS13_REG_RSVD3, | ||
| 42 | S2MPS13_REG_RSVD4, | ||
| 43 | S2MPS13_REG_RSVD5, | ||
| 44 | S2MPS13_REG_RSVD6, | ||
| 45 | S2MPS13_REG_CTRL3, | ||
| 46 | S2MPS13_REG_RSVD7, | ||
| 47 | S2MPS13_REG_RSVD8, | ||
| 48 | S2MPS13_REG_WRSTBI, | ||
| 49 | S2MPS13_REG_B1CTRL, | ||
| 50 | S2MPS13_REG_B1OUT, | ||
| 51 | S2MPS13_REG_B2CTRL, | ||
| 52 | S2MPS13_REG_B2OUT, | ||
| 53 | S2MPS13_REG_B3CTRL, | ||
| 54 | S2MPS13_REG_B3OUT, | ||
| 55 | S2MPS13_REG_B4CTRL, | ||
| 56 | S2MPS13_REG_B4OUT, | ||
| 57 | S2MPS13_REG_B5CTRL, | ||
| 58 | S2MPS13_REG_B5OUT, | ||
| 59 | S2MPS13_REG_B6CTRL, | ||
| 60 | S2MPS13_REG_B6OUT, | ||
| 61 | S2MPS13_REG_B7CTRL, | ||
| 62 | S2MPS13_REG_B7OUT, | ||
| 63 | S2MPS13_REG_B8CTRL, | ||
| 64 | S2MPS13_REG_B8OUT, | ||
| 65 | S2MPS13_REG_B9CTRL, | ||
| 66 | S2MPS13_REG_B9OUT, | ||
| 67 | S2MPS13_REG_B10CTRL, | ||
| 68 | S2MPS13_REG_B10OUT, | ||
| 69 | S2MPS13_REG_BB1CTRL, | ||
| 70 | S2MPS13_REG_BB1OUT, | ||
| 71 | S2MPS13_REG_BUCK_RAMP1, | ||
| 72 | S2MPS13_REG_BUCK_RAMP2, | ||
| 73 | S2MPS13_REG_LDO_DVS1, | ||
| 74 | S2MPS13_REG_LDO_DVS2, | ||
| 75 | S2MPS13_REG_LDO_DVS3, | ||
| 76 | S2MPS13_REG_B6OUT2, | ||
| 77 | S2MPS13_REG_L1CTRL, | ||
| 78 | S2MPS13_REG_L2CTRL, | ||
| 79 | S2MPS13_REG_L3CTRL, | ||
| 80 | S2MPS13_REG_L4CTRL, | ||
| 81 | S2MPS13_REG_L5CTRL, | ||
| 82 | S2MPS13_REG_L6CTRL, | ||
| 83 | S2MPS13_REG_L7CTRL, | ||
| 84 | S2MPS13_REG_L8CTRL, | ||
| 85 | S2MPS13_REG_L9CTRL, | ||
| 86 | S2MPS13_REG_L10CTRL, | ||
| 87 | S2MPS13_REG_L11CTRL, | ||
| 88 | S2MPS13_REG_L12CTRL, | ||
| 89 | S2MPS13_REG_L13CTRL, | ||
| 90 | S2MPS13_REG_L14CTRL, | ||
| 91 | S2MPS13_REG_L15CTRL, | ||
| 92 | S2MPS13_REG_L16CTRL, | ||
| 93 | S2MPS13_REG_L17CTRL, | ||
| 94 | S2MPS13_REG_L18CTRL, | ||
| 95 | S2MPS13_REG_L19CTRL, | ||
| 96 | S2MPS13_REG_L20CTRL, | ||
| 97 | S2MPS13_REG_L21CTRL, | ||
| 98 | S2MPS13_REG_L22CTRL, | ||
| 99 | S2MPS13_REG_L23CTRL, | ||
| 100 | S2MPS13_REG_L24CTRL, | ||
| 101 | S2MPS13_REG_L25CTRL, | ||
| 102 | S2MPS13_REG_L26CTRL, | ||
| 103 | S2MPS13_REG_L27CTRL, | ||
| 104 | S2MPS13_REG_L28CTRL, | ||
| 105 | S2MPS13_REG_L30CTRL, | ||
| 106 | S2MPS13_REG_L31CTRL, | ||
| 107 | S2MPS13_REG_L32CTRL, | ||
| 108 | S2MPS13_REG_L33CTRL, | ||
| 109 | S2MPS13_REG_L34CTRL, | ||
| 110 | S2MPS13_REG_L35CTRL, | ||
| 111 | S2MPS13_REG_L36CTRL, | ||
| 112 | S2MPS13_REG_L37CTRL, | ||
| 113 | S2MPS13_REG_L38CTRL, | ||
| 114 | S2MPS13_REG_L39CTRL, | ||
| 115 | S2MPS13_REG_L40CTRL, | ||
| 116 | S2MPS13_REG_LDODSCH1, | ||
| 117 | S2MPS13_REG_LDODSCH2, | ||
| 118 | S2MPS13_REG_LDODSCH3, | ||
| 119 | S2MPS13_REG_LDODSCH4, | ||
| 120 | S2MPS13_REG_LDODSCH5, | ||
| 121 | }; | ||
| 122 | |||
| 123 | /* regulator ids */ | ||
| 124 | enum s2mps13_regulators { | ||
| 125 | S2MPS13_LDO1, | ||
| 126 | S2MPS13_LDO2, | ||
| 127 | S2MPS13_LDO3, | ||
| 128 | S2MPS13_LDO4, | ||
| 129 | S2MPS13_LDO5, | ||
| 130 | S2MPS13_LDO6, | ||
| 131 | S2MPS13_LDO7, | ||
| 132 | S2MPS13_LDO8, | ||
| 133 | S2MPS13_LDO9, | ||
| 134 | S2MPS13_LDO10, | ||
| 135 | S2MPS13_LDO11, | ||
| 136 | S2MPS13_LDO12, | ||
| 137 | S2MPS13_LDO13, | ||
| 138 | S2MPS13_LDO14, | ||
| 139 | S2MPS13_LDO15, | ||
| 140 | S2MPS13_LDO16, | ||
| 141 | S2MPS13_LDO17, | ||
| 142 | S2MPS13_LDO18, | ||
| 143 | S2MPS13_LDO19, | ||
| 144 | S2MPS13_LDO20, | ||
| 145 | S2MPS13_LDO21, | ||
| 146 | S2MPS13_LDO22, | ||
| 147 | S2MPS13_LDO23, | ||
| 148 | S2MPS13_LDO24, | ||
| 149 | S2MPS13_LDO25, | ||
| 150 | S2MPS13_LDO26, | ||
| 151 | S2MPS13_LDO27, | ||
| 152 | S2MPS13_LDO28, | ||
| 153 | S2MPS13_LDO29, | ||
| 154 | S2MPS13_LDO30, | ||
| 155 | S2MPS13_LDO31, | ||
| 156 | S2MPS13_LDO32, | ||
| 157 | S2MPS13_LDO33, | ||
| 158 | S2MPS13_LDO34, | ||
| 159 | S2MPS13_LDO35, | ||
| 160 | S2MPS13_LDO36, | ||
| 161 | S2MPS13_LDO37, | ||
| 162 | S2MPS13_LDO38, | ||
| 163 | S2MPS13_LDO39, | ||
| 164 | S2MPS13_LDO40, | ||
| 165 | S2MPS13_BUCK1, | ||
| 166 | S2MPS13_BUCK2, | ||
| 167 | S2MPS13_BUCK3, | ||
| 168 | S2MPS13_BUCK4, | ||
| 169 | S2MPS13_BUCK5, | ||
| 170 | S2MPS13_BUCK6, | ||
| 171 | S2MPS13_BUCK7, | ||
| 172 | S2MPS13_BUCK8, | ||
| 173 | S2MPS13_BUCK9, | ||
| 174 | S2MPS13_BUCK10, | ||
| 175 | |||
| 176 | S2MPS13_REGULATOR_MAX, | ||
| 177 | }; | ||
| 178 | |||
| 179 | /* | ||
| 180 | * Default ramp delay in uv/us. Datasheet says that ramp delay can be | ||
| 181 | * controlled however it does not specify which register is used for that. | ||
| 182 | * Let's assume that default value will be set. | ||
| 183 | */ | ||
| 184 | #define S2MPS13_BUCK_RAMP_DELAY 12500 | ||
| 185 | |||
| 186 | #endif /* __LINUX_MFD_S2MPS13_H */ | ||
diff --git a/include/linux/of.h b/include/linux/of.h index 6545e7aec7bb..27b3ba1e9e59 100644 --- a/include/linux/of.h +++ b/include/linux/of.h | |||
| @@ -866,4 +866,15 @@ static inline int of_changeset_update_property(struct of_changeset *ocs, | |||
| 866 | /* CONFIG_OF_RESOLVE api */ | 866 | /* CONFIG_OF_RESOLVE api */ |
| 867 | extern int of_resolve_phandles(struct device_node *tree); | 867 | extern int of_resolve_phandles(struct device_node *tree); |
| 868 | 868 | ||
| 869 | /** | ||
| 870 | * of_system_has_poweroff_source - Tells if poweroff-source is found for device_node | ||
| 871 | * @np: Pointer to the given device_node | ||
| 872 | * | ||
| 873 | * return true if present false otherwise | ||
| 874 | */ | ||
| 875 | static inline bool of_system_has_poweroff_source(const struct device_node *np) | ||
| 876 | { | ||
| 877 | return of_property_read_bool(np, "poweroff-source"); | ||
| 878 | } | ||
| 879 | |||
| 869 | #endif /* _LINUX_OF_H */ | 880 | #endif /* _LINUX_OF_H */ |
