diff options
24 files changed, 1593 insertions, 221 deletions
diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt new file mode 100644 index 000000000000..131e8c11d26f --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiecap.txt | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | TI SOC ECAP based APWM controller | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible: Must be "ti,am33xx-ecap" | ||
| 5 | - #pwm-cells: Should be 3. Number of cells being used to specify PWM property. | ||
| 6 | First cell specifies the per-chip index of the PWM to use, the second | ||
| 7 | cell is the period in nanoseconds and bit 0 in the third cell is used to | ||
| 8 | encode the polarity of PWM output. Set bit 0 of the third in PWM specifier | ||
| 9 | to 1 for inverse polarity & set to 0 for normal polarity. | ||
| 10 | - reg: physical base address and size of the registers map. | ||
| 11 | |||
| 12 | Optional properties: | ||
| 13 | - ti,hwmods: Name of the hwmod associated to the ECAP: | ||
| 14 | "ecap<x>", <x> being the 0-based instance number from the HW spec | ||
| 15 | |||
| 16 | Example: | ||
| 17 | |||
| 18 | ecap0: ecap@0 { | ||
| 19 | compatible = "ti,am33xx-ecap"; | ||
| 20 | #pwm-cells = <3>; | ||
| 21 | reg = <0x48300100 0x80>; | ||
| 22 | ti,hwmods = "ecap0"; | ||
| 23 | }; | ||
diff --git a/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt new file mode 100644 index 000000000000..4fc7079d822e --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tiehrpwm.txt | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | TI SOC EHRPWM based PWM controller | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible : Must be "ti,am33xx-ehrpwm" | ||
| 5 | - #pwm-cells: Should be 3. Number of cells being used to specify PWM property. | ||
| 6 | First cell specifies the per-chip index of the PWM to use, the second | ||
| 7 | cell is the period in nanoseconds and bit 0 in the third cell is used to | ||
| 8 | encode the polarity of PWM output. Set bit 0 of the third in PWM specifier | ||
| 9 | to 1 for inverse polarity & set to 0 for normal polarity. | ||
| 10 | - reg: physical base address and size of the registers map. | ||
| 11 | |||
| 12 | Optional properties: | ||
| 13 | - ti,hwmods: Name of the hwmod associated to the EHRPWM: | ||
| 14 | "ehrpwm<x>", <x> being the 0-based instance number from the HW spec | ||
| 15 | |||
| 16 | Example: | ||
| 17 | |||
| 18 | ehrpwm0: ehrpwm@0 { | ||
| 19 | compatible = "ti,am33xx-ehrpwm"; | ||
| 20 | #pwm-cells = <3>; | ||
| 21 | reg = <0x48300200 0x100>; | ||
| 22 | ti,hwmods = "ehrpwm0"; | ||
| 23 | }; | ||
diff --git a/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt b/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt new file mode 100644 index 000000000000..f7eae77f8354 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/pwm-tipwmss.txt | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | TI SOC based PWM Subsystem | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible: Must be "ti,am33xx-pwmss"; | ||
| 5 | - reg: physical base address and size of the registers map. | ||
| 6 | - address-cells: Specify the number of u32 entries needed in child nodes. | ||
| 7 | Should set to 1. | ||
| 8 | - size-cells: specify number of u32 entries needed to specify child nodes size | ||
| 9 | in reg property. Should set to 1. | ||
| 10 | - ranges: describes the address mapping of a memory-mapped bus. Should set to | ||
| 11 | physical address map of child's base address, physical address within | ||
| 12 | parent's address space and length of the address map. For am33xx, | ||
| 13 | 3 set of child register maps present, ECAP register space, EQEP | ||
| 14 | register space, EHRPWM register space. | ||
| 15 | |||
| 16 | Also child nodes should also populated under PWMSS DT node. | ||
| 17 | |||
| 18 | Example: | ||
| 19 | pwmss0: pwmss@48300000 { | ||
| 20 | compatible = "ti,am33xx-pwmss"; | ||
| 21 | reg = <0x48300000 0x10>; | ||
| 22 | ti,hwmods = "epwmss0"; | ||
| 23 | #address-cells = <1>; | ||
| 24 | #size-cells = <1>; | ||
| 25 | status = "disabled"; | ||
| 26 | ranges = <0x48300100 0x48300100 0x80 /* ECAP */ | ||
| 27 | 0x48300180 0x48300180 0x80 /* EQEP */ | ||
| 28 | 0x48300200 0x48300200 0x80>; /* EHRPWM */ | ||
| 29 | |||
| 30 | /* child nodes go here */ | ||
| 31 | }; | ||
diff --git a/Documentation/devicetree/bindings/pwm/pwm.txt b/Documentation/devicetree/bindings/pwm/pwm.txt index 73ec962bfe8c..06e67247859a 100644 --- a/Documentation/devicetree/bindings/pwm/pwm.txt +++ b/Documentation/devicetree/bindings/pwm/pwm.txt | |||
| @@ -37,10 +37,21 @@ device: | |||
| 37 | pwm-names = "backlight"; | 37 | pwm-names = "backlight"; |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| 40 | Note that in the example above, specifying the "pwm-names" is redundant | ||
| 41 | because the name "backlight" would be used as fallback anyway. | ||
| 42 | |||
| 40 | pwm-specifier typically encodes the chip-relative PWM number and the PWM | 43 | pwm-specifier typically encodes the chip-relative PWM number and the PWM |
| 41 | period in nanoseconds. Note that in the example above, specifying the | 44 | period in nanoseconds. |
| 42 | "pwm-names" is redundant because the name "backlight" would be used as | 45 | |
| 43 | fallback anyway. | 46 | Optionally, the pwm-specifier can encode a number of flags in a third cell: |
| 47 | - bit 0: PWM signal polarity (0: normal polarity, 1: inverse polarity) | ||
| 48 | |||
| 49 | Example with optional PWM specifier for inverse polarity | ||
| 50 | |||
| 51 | bl: backlight { | ||
| 52 | pwms = <&pwm 0 5000000 1>; | ||
| 53 | pwm-names = "backlight"; | ||
| 54 | }; | ||
| 44 | 55 | ||
| 45 | 2) PWM controller nodes | 56 | 2) PWM controller nodes |
| 46 | ----------------------- | 57 | ----------------------- |
diff --git a/Documentation/devicetree/bindings/pwm/spear-pwm.txt b/Documentation/devicetree/bindings/pwm/spear-pwm.txt new file mode 100644 index 000000000000..3ac779d83386 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/spear-pwm.txt | |||
| @@ -0,0 +1,18 @@ | |||
| 1 | == ST SPEAr SoC PWM controller == | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible: should be one of: | ||
| 5 | - "st,spear320-pwm" | ||
| 6 | - "st,spear1340-pwm" | ||
| 7 | - reg: physical base address and length of the controller's registers | ||
| 8 | - #pwm-cells: number of cells used to specify PWM which is fixed to 2 on | ||
| 9 | SPEAr. The first cell specifies the per-chip index of the PWM to use and | ||
| 10 | the second cell is the period in nanoseconds. | ||
| 11 | |||
| 12 | Example: | ||
| 13 | |||
| 14 | pwm: pwm@a8000000 { | ||
| 15 | compatible ="st,spear320-pwm"; | ||
| 16 | reg = <0xa8000000 0x1000>; | ||
| 17 | #pwm-cells = <2>; | ||
| 18 | }; | ||
diff --git a/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt b/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt new file mode 100644 index 000000000000..2943ee5fce00 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/ti,twl-pwm.txt | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | Texas Instruments TWL series PWM drivers | ||
| 2 | |||
| 3 | Supported PWMs: | ||
| 4 | On TWL4030 series: PWM1 and PWM2 | ||
| 5 | On TWL6030 series: PWM0 and PWM1 | ||
| 6 | |||
| 7 | Required properties: | ||
| 8 | - compatible: "ti,twl4030-pwm" or "ti,twl6030-pwm" | ||
| 9 | - #pwm-cells: should be 2. The first cell specifies the per-chip index | ||
| 10 | of the PWM to use and the second cell is the period in nanoseconds. | ||
| 11 | |||
| 12 | Example: | ||
| 13 | |||
| 14 | twl_pwm: pwm { | ||
| 15 | compatible = "ti,twl6030-pwm"; | ||
| 16 | #pwm-cells = <2>; | ||
| 17 | }; | ||
diff --git a/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt b/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt new file mode 100644 index 000000000000..cb64f3acc10f --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/ti,twl-pwmled.txt | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | Texas Instruments TWL series PWM drivers connected to LED terminals | ||
| 2 | |||
| 3 | Supported PWMs: | ||
| 4 | On TWL4030 series: PWMA and PWMB (connected to LEDA and LEDB terminals) | ||
| 5 | On TWL6030 series: LED PWM (mainly used as charging indicator LED) | ||
| 6 | |||
| 7 | Required properties: | ||
| 8 | - compatible: "ti,twl4030-pwmled" or "ti,twl6030-pwmled" | ||
| 9 | - #pwm-cells: should be 2. The first cell specifies the per-chip index | ||
| 10 | of the PWM to use and the second cell is the period in nanoseconds. | ||
| 11 | |||
| 12 | Example: | ||
| 13 | |||
| 14 | twl_pwmled: pwmled { | ||
| 15 | compatible = "ti,twl6030-pwmled"; | ||
| 16 | #pwm-cells = <2>; | ||
| 17 | }; | ||
diff --git a/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt new file mode 100644 index 000000000000..bcc63678a9a5 --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/vt8500-pwm.txt | |||
| @@ -0,0 +1,17 @@ | |||
| 1 | VIA/Wondermedia VT8500/WM8xxx series SoC PWM controller | ||
| 2 | |||
| 3 | Required properties: | ||
| 4 | - compatible: should be "via,vt8500-pwm" | ||
| 5 | - reg: physical base address and length of the controller's registers | ||
| 6 | - #pwm-cells: should be 2. The first cell specifies the per-chip index | ||
| 7 | of the PWM to use and the second cell is the period in nanoseconds. | ||
| 8 | - clocks: phandle to the PWM source clock | ||
| 9 | |||
| 10 | Example: | ||
| 11 | |||
| 12 | pwm1: pwm@d8220000 { | ||
| 13 | #pwm-cells = <2>; | ||
| 14 | compatible = "via,vt8500-pwm"; | ||
| 15 | reg = <0xd8220000 0x1000>; | ||
| 16 | clocks = <&clkpwm>; | ||
| 17 | }; | ||
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index ed81720e7b2b..e513cd998170 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig | |||
| @@ -112,6 +112,17 @@ config PWM_SAMSUNG | |||
| 112 | To compile this driver as a module, choose M here: the module | 112 | To compile this driver as a module, choose M here: the module |
| 113 | will be called pwm-samsung. | 113 | will be called pwm-samsung. |
| 114 | 114 | ||
| 115 | config PWM_SPEAR | ||
| 116 | tristate "STMicroelectronics SPEAr PWM support" | ||
| 117 | depends on PLAT_SPEAR | ||
| 118 | depends on OF | ||
| 119 | help | ||
| 120 | Generic PWM framework driver for the PWM controller on ST | ||
| 121 | SPEAr SoCs. | ||
| 122 | |||
| 123 | To compile this driver as a module, choose M here: the module | ||
| 124 | will be called pwm-spear. | ||
| 125 | |||
| 115 | config PWM_TEGRA | 126 | config PWM_TEGRA |
| 116 | tristate "NVIDIA Tegra PWM support" | 127 | tristate "NVIDIA Tegra PWM support" |
| 117 | depends on ARCH_TEGRA | 128 | depends on ARCH_TEGRA |
| @@ -125,6 +136,7 @@ config PWM_TEGRA | |||
| 125 | config PWM_TIECAP | 136 | config PWM_TIECAP |
| 126 | tristate "ECAP PWM support" | 137 | tristate "ECAP PWM support" |
| 127 | depends on SOC_AM33XX | 138 | depends on SOC_AM33XX |
| 139 | select PWM_TIPWMSS | ||
| 128 | help | 140 | help |
| 129 | PWM driver support for the ECAP APWM controller found on AM33XX | 141 | PWM driver support for the ECAP APWM controller found on AM33XX |
| 130 | TI SOC | 142 | TI SOC |
| @@ -135,6 +147,7 @@ config PWM_TIECAP | |||
| 135 | config PWM_TIEHRPWM | 147 | config PWM_TIEHRPWM |
| 136 | tristate "EHRPWM PWM support" | 148 | tristate "EHRPWM PWM support" |
| 137 | depends on SOC_AM33XX | 149 | depends on SOC_AM33XX |
| 150 | select PWM_TIPWMSS | ||
| 138 | help | 151 | help |
| 139 | PWM driver support for the EHRPWM controller found on AM33XX | 152 | PWM driver support for the EHRPWM controller found on AM33XX |
| 140 | TI SOC | 153 | TI SOC |
| @@ -142,14 +155,32 @@ config PWM_TIEHRPWM | |||
| 142 | To compile this driver as a module, choose M here: the module | 155 | To compile this driver as a module, choose M here: the module |
| 143 | will be called pwm-tiehrpwm. | 156 | will be called pwm-tiehrpwm. |
| 144 | 157 | ||
| 145 | config PWM_TWL6030 | 158 | config PWM_TIPWMSS |
| 146 | tristate "TWL6030 PWM support" | 159 | bool |
| 160 | depends on SOC_AM33XX && (PWM_TIEHRPWM || PWM_TIECAP) | ||
| 161 | help | ||
| 162 | PWM Subsystem driver support for AM33xx SOC. | ||
| 163 | |||
| 164 | PWM submodules require PWM config space access from submodule | ||
| 165 | drivers and require common parent driver support. | ||
| 166 | |||
| 167 | config PWM_TWL | ||
| 168 | tristate "TWL4030/6030 PWM support" | ||
| 169 | depends on TWL4030_CORE | ||
| 170 | help | ||
| 171 | Generic PWM framework driver for TWL4030/6030. | ||
| 172 | |||
| 173 | To compile this driver as a module, choose M here: the module | ||
| 174 | will be called pwm-twl. | ||
| 175 | |||
| 176 | config PWM_TWL_LED | ||
| 177 | tristate "TWL4030/6030 PWM support for LED drivers" | ||
| 147 | depends on TWL4030_CORE | 178 | depends on TWL4030_CORE |
| 148 | help | 179 | help |
| 149 | Generic PWM framework driver for TWL6030. | 180 | Generic PWM framework driver for TWL4030/6030 LED terminals. |
| 150 | 181 | ||
| 151 | To compile this driver as a module, choose M here: the module | 182 | To compile this driver as a module, choose M here: the module |
| 152 | will be called pwm-twl6030. | 183 | will be called pwm-twl-led. |
| 153 | 184 | ||
| 154 | config PWM_VT8500 | 185 | config PWM_VT8500 |
| 155 | tristate "vt8500 pwm support" | 186 | tristate "vt8500 pwm support" |
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index acfe4821c58b..62a2963cfe58 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile | |||
| @@ -8,8 +8,11 @@ obj-$(CONFIG_PWM_MXS) += pwm-mxs.o | |||
| 8 | obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o | 8 | obj-$(CONFIG_PWM_PUV3) += pwm-puv3.o |
| 9 | obj-$(CONFIG_PWM_PXA) += pwm-pxa.o | 9 | obj-$(CONFIG_PWM_PXA) += pwm-pxa.o |
| 10 | obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o | 10 | obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o |
| 11 | obj-$(CONFIG_PWM_SPEAR) += pwm-spear.o | ||
| 11 | obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o | 12 | obj-$(CONFIG_PWM_TEGRA) += pwm-tegra.o |
| 12 | obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o | 13 | obj-$(CONFIG_PWM_TIECAP) += pwm-tiecap.o |
| 13 | obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o | 14 | obj-$(CONFIG_PWM_TIEHRPWM) += pwm-tiehrpwm.o |
| 14 | obj-$(CONFIG_PWM_TWL6030) += pwm-twl6030.o | 15 | obj-$(CONFIG_PWM_TIPWMSS) += pwm-tipwmss.o |
| 16 | obj-$(CONFIG_PWM_TWL) += pwm-twl.o | ||
| 17 | obj-$(CONFIG_PWM_TWL_LED) += pwm-twl-led.o | ||
| 15 | obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o | 18 | obj-$(CONFIG_PWM_VT8500) += pwm-vt8500.o |
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c index f5acdaa52707..903138b18842 100644 --- a/drivers/pwm/core.c +++ b/drivers/pwm/core.c | |||
| @@ -32,6 +32,9 @@ | |||
| 32 | 32 | ||
| 33 | #define MAX_PWMS 1024 | 33 | #define MAX_PWMS 1024 |
| 34 | 34 | ||
| 35 | /* flags in the third cell of the DT PWM specifier */ | ||
| 36 | #define PWM_SPEC_POLARITY (1 << 0) | ||
| 37 | |||
| 35 | static DEFINE_MUTEX(pwm_lookup_lock); | 38 | static DEFINE_MUTEX(pwm_lookup_lock); |
| 36 | static LIST_HEAD(pwm_lookup_list); | 39 | static LIST_HEAD(pwm_lookup_list); |
| 37 | static DEFINE_MUTEX(pwm_lock); | 40 | static DEFINE_MUTEX(pwm_lock); |
| @@ -129,6 +132,32 @@ static int pwm_device_request(struct pwm_device *pwm, const char *label) | |||
| 129 | return 0; | 132 | return 0; |
| 130 | } | 133 | } |
| 131 | 134 | ||
| 135 | struct pwm_device * | ||
| 136 | of_pwm_xlate_with_flags(struct pwm_chip *pc, const struct of_phandle_args *args) | ||
| 137 | { | ||
| 138 | struct pwm_device *pwm; | ||
| 139 | |||
| 140 | if (pc->of_pwm_n_cells < 3) | ||
| 141 | return ERR_PTR(-EINVAL); | ||
| 142 | |||
| 143 | if (args->args[0] >= pc->npwm) | ||
| 144 | return ERR_PTR(-EINVAL); | ||
| 145 | |||
| 146 | pwm = pwm_request_from_chip(pc, args->args[0], NULL); | ||
| 147 | if (IS_ERR(pwm)) | ||
| 148 | return pwm; | ||
| 149 | |||
| 150 | pwm_set_period(pwm, args->args[1]); | ||
| 151 | |||
| 152 | if (args->args[2] & PWM_SPEC_POLARITY) | ||
| 153 | pwm_set_polarity(pwm, PWM_POLARITY_INVERSED); | ||
| 154 | else | ||
| 155 | pwm_set_polarity(pwm, PWM_POLARITY_NORMAL); | ||
| 156 | |||
| 157 | return pwm; | ||
| 158 | } | ||
| 159 | EXPORT_SYMBOL_GPL(of_pwm_xlate_with_flags); | ||
| 160 | |||
| 132 | static struct pwm_device * | 161 | static struct pwm_device * |
| 133 | of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) | 162 | of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args) |
| 134 | { | 163 | { |
diff --git a/drivers/pwm/pwm-imx.c b/drivers/pwm/pwm-imx.c index 8f26e9fcea97..65a86bdeabed 100644 --- a/drivers/pwm/pwm-imx.c +++ b/drivers/pwm/pwm-imx.c | |||
| @@ -235,7 +235,7 @@ static int imx_pwm_probe(struct platform_device *pdev) | |||
| 235 | { | 235 | { |
| 236 | const struct of_device_id *of_id = | 236 | const struct of_device_id *of_id = |
| 237 | of_match_device(imx_pwm_dt_ids, &pdev->dev); | 237 | of_match_device(imx_pwm_dt_ids, &pdev->dev); |
| 238 | struct imx_pwm_data *data; | 238 | const struct imx_pwm_data *data; |
| 239 | struct imx_chip *imx; | 239 | struct imx_chip *imx; |
| 240 | struct resource *r; | 240 | struct resource *r; |
| 241 | int ret = 0; | 241 | int ret = 0; |
diff --git a/drivers/pwm/pwm-lpc32xx.c b/drivers/pwm/pwm-lpc32xx.c index 015a82235620..14106440294f 100644 --- a/drivers/pwm/pwm-lpc32xx.c +++ b/drivers/pwm/pwm-lpc32xx.c | |||
| @@ -49,9 +49,24 @@ static int lpc32xx_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 49 | c = 0; /* 0 set division by 256 */ | 49 | c = 0; /* 0 set division by 256 */ |
| 50 | period_cycles = c; | 50 | period_cycles = c; |
| 51 | 51 | ||
| 52 | /* The duty-cycle value is as follows: | ||
| 53 | * | ||
| 54 | * DUTY-CYCLE HIGH LEVEL | ||
| 55 | * 1 99.9% | ||
| 56 | * 25 90.0% | ||
| 57 | * 128 50.0% | ||
| 58 | * 220 10.0% | ||
| 59 | * 255 0.1% | ||
| 60 | * 0 0.0% | ||
| 61 | * | ||
| 62 | * In other words, the register value is duty-cycle % 256 with | ||
| 63 | * duty-cycle in the range 1-256. | ||
| 64 | */ | ||
| 52 | c = 256 * duty_ns; | 65 | c = 256 * duty_ns; |
| 53 | do_div(c, period_ns); | 66 | do_div(c, period_ns); |
| 54 | duty_cycles = c; | 67 | if (c > 255) |
| 68 | c = 255; | ||
| 69 | duty_cycles = 256 - c; | ||
| 55 | 70 | ||
| 56 | writel(PWM_ENABLE | PWM_RELOADV(period_cycles) | PWM_DUTY(duty_cycles), | 71 | writel(PWM_ENABLE | PWM_RELOADV(period_cycles) | PWM_DUTY(duty_cycles), |
| 57 | lpc32xx->base + (pwm->hwpwm << 2)); | 72 | lpc32xx->base + (pwm->hwpwm << 2)); |
| @@ -106,6 +121,7 @@ static int lpc32xx_pwm_probe(struct platform_device *pdev) | |||
| 106 | lpc32xx->chip.dev = &pdev->dev; | 121 | lpc32xx->chip.dev = &pdev->dev; |
| 107 | lpc32xx->chip.ops = &lpc32xx_pwm_ops; | 122 | lpc32xx->chip.ops = &lpc32xx_pwm_ops; |
| 108 | lpc32xx->chip.npwm = 2; | 123 | lpc32xx->chip.npwm = 2; |
| 124 | lpc32xx->chip.base = -1; | ||
| 109 | 125 | ||
| 110 | ret = pwmchip_add(&lpc32xx->chip); | 126 | ret = pwmchip_add(&lpc32xx->chip); |
| 111 | if (ret < 0) { | 127 | if (ret < 0) { |
| @@ -121,8 +137,11 @@ static int lpc32xx_pwm_probe(struct platform_device *pdev) | |||
| 121 | static int lpc32xx_pwm_remove(struct platform_device *pdev) | 137 | static int lpc32xx_pwm_remove(struct platform_device *pdev) |
| 122 | { | 138 | { |
| 123 | struct lpc32xx_pwm_chip *lpc32xx = platform_get_drvdata(pdev); | 139 | struct lpc32xx_pwm_chip *lpc32xx = platform_get_drvdata(pdev); |
| 140 | unsigned int i; | ||
| 141 | |||
| 142 | for (i = 0; i < lpc32xx->chip.npwm; i++) | ||
| 143 | pwm_disable(&lpc32xx->chip.pwms[i]); | ||
| 124 | 144 | ||
| 125 | clk_disable(lpc32xx->clk); | ||
| 126 | return pwmchip_remove(&lpc32xx->chip); | 145 | return pwmchip_remove(&lpc32xx->chip); |
| 127 | } | 146 | } |
| 128 | 147 | ||
diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c index e9b15d099c03..5207e6cd8648 100644 --- a/drivers/pwm/pwm-samsung.c +++ b/drivers/pwm/pwm-samsung.c | |||
| @@ -222,6 +222,7 @@ static int s3c_pwm_probe(struct platform_device *pdev) | |||
| 222 | 222 | ||
| 223 | /* calculate base of control bits in TCON */ | 223 | /* calculate base of control bits in TCON */ |
| 224 | s3c->tcon_base = id == 0 ? 0 : (id * 4) + 4; | 224 | s3c->tcon_base = id == 0 ? 0 : (id * 4) + 4; |
| 225 | s3c->pwm_id = id; | ||
| 225 | s3c->chip.dev = &pdev->dev; | 226 | s3c->chip.dev = &pdev->dev; |
| 226 | s3c->chip.ops = &s3c_pwm_ops; | 227 | s3c->chip.ops = &s3c_pwm_ops; |
| 227 | s3c->chip.base = -1; | 228 | s3c->chip.base = -1; |
diff --git a/drivers/pwm/pwm-spear.c b/drivers/pwm/pwm-spear.c new file mode 100644 index 000000000000..83b21d9d5cf9 --- /dev/null +++ b/drivers/pwm/pwm-spear.c | |||
| @@ -0,0 +1,276 @@ | |||
| 1 | /* | ||
| 2 | * ST Microelectronics SPEAr Pulse Width Modulator driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 ST Microelectronics | ||
| 5 | * Shiraz Hashim <shiraz.hashim@st.com> | ||
| 6 | * | ||
| 7 | * This file is licensed under the terms of the GNU General Public | ||
| 8 | * License version 2. This program is licensed "as is" without any | ||
| 9 | * warranty of any kind, whether express or implied. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #include <linux/clk.h> | ||
| 13 | #include <linux/err.h> | ||
| 14 | #include <linux/io.h> | ||
| 15 | #include <linux/ioport.h> | ||
| 16 | #include <linux/kernel.h> | ||
| 17 | #include <linux/math64.h> | ||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/of.h> | ||
| 20 | #include <linux/platform_device.h> | ||
| 21 | #include <linux/pwm.h> | ||
| 22 | #include <linux/slab.h> | ||
| 23 | #include <linux/types.h> | ||
| 24 | |||
| 25 | #define NUM_PWM 4 | ||
| 26 | |||
| 27 | /* PWM registers and bits definitions */ | ||
| 28 | #define PWMCR 0x00 /* Control Register */ | ||
| 29 | #define PWMCR_PWM_ENABLE 0x1 | ||
| 30 | #define PWMCR_PRESCALE_SHIFT 2 | ||
| 31 | #define PWMCR_MIN_PRESCALE 0x00 | ||
| 32 | #define PWMCR_MAX_PRESCALE 0x3FFF | ||
| 33 | |||
| 34 | #define PWMDCR 0x04 /* Duty Cycle Register */ | ||
| 35 | #define PWMDCR_MIN_DUTY 0x0001 | ||
| 36 | #define PWMDCR_MAX_DUTY 0xFFFF | ||
| 37 | |||
| 38 | #define PWMPCR 0x08 /* Period Register */ | ||
| 39 | #define PWMPCR_MIN_PERIOD 0x0001 | ||
| 40 | #define PWMPCR_MAX_PERIOD 0xFFFF | ||
| 41 | |||
| 42 | /* Following only available on 13xx SoCs */ | ||
| 43 | #define PWMMCR 0x3C /* Master Control Register */ | ||
| 44 | #define PWMMCR_PWM_ENABLE 0x1 | ||
| 45 | |||
| 46 | /** | ||
| 47 | * struct spear_pwm_chip - struct representing pwm chip | ||
| 48 | * | ||
| 49 | * @mmio_base: base address of pwm chip | ||
| 50 | * @clk: pointer to clk structure of pwm chip | ||
| 51 | * @chip: linux pwm chip representation | ||
| 52 | * @dev: pointer to device structure of pwm chip | ||
| 53 | */ | ||
| 54 | struct spear_pwm_chip { | ||
| 55 | void __iomem *mmio_base; | ||
| 56 | struct clk *clk; | ||
| 57 | struct pwm_chip chip; | ||
| 58 | struct device *dev; | ||
| 59 | }; | ||
| 60 | |||
| 61 | static inline struct spear_pwm_chip *to_spear_pwm_chip(struct pwm_chip *chip) | ||
| 62 | { | ||
| 63 | return container_of(chip, struct spear_pwm_chip, chip); | ||
| 64 | } | ||
| 65 | |||
| 66 | static inline u32 spear_pwm_readl(struct spear_pwm_chip *chip, unsigned int num, | ||
| 67 | unsigned long offset) | ||
| 68 | { | ||
| 69 | return readl_relaxed(chip->mmio_base + (num << 4) + offset); | ||
| 70 | } | ||
| 71 | |||
| 72 | static inline void spear_pwm_writel(struct spear_pwm_chip *chip, | ||
| 73 | unsigned int num, unsigned long offset, | ||
| 74 | unsigned long val) | ||
| 75 | { | ||
| 76 | writel_relaxed(val, chip->mmio_base + (num << 4) + offset); | ||
| 77 | } | ||
| 78 | |||
| 79 | static int spear_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
| 80 | int duty_ns, int period_ns) | ||
| 81 | { | ||
| 82 | struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); | ||
| 83 | u64 val, div, clk_rate; | ||
| 84 | unsigned long prescale = PWMCR_MIN_PRESCALE, pv, dc; | ||
| 85 | int ret; | ||
| 86 | |||
| 87 | /* | ||
| 88 | * Find pv, dc and prescale to suit duty_ns and period_ns. This is done | ||
| 89 | * according to formulas described below: | ||
| 90 | * | ||
| 91 | * period_ns = 10^9 * (PRESCALE + 1) * PV / PWM_CLK_RATE | ||
| 92 | * duty_ns = 10^9 * (PRESCALE + 1) * DC / PWM_CLK_RATE | ||
| 93 | * | ||
| 94 | * PV = (PWM_CLK_RATE * period_ns) / (10^9 * (PRESCALE + 1)) | ||
| 95 | * DC = (PWM_CLK_RATE * duty_ns) / (10^9 * (PRESCALE + 1)) | ||
| 96 | */ | ||
| 97 | clk_rate = clk_get_rate(pc->clk); | ||
| 98 | while (1) { | ||
| 99 | div = 1000000000; | ||
| 100 | div *= 1 + prescale; | ||
| 101 | val = clk_rate * period_ns; | ||
| 102 | pv = div64_u64(val, div); | ||
| 103 | val = clk_rate * duty_ns; | ||
| 104 | dc = div64_u64(val, div); | ||
| 105 | |||
| 106 | /* if duty_ns and period_ns are not achievable then return */ | ||
| 107 | if (pv < PWMPCR_MIN_PERIOD || dc < PWMDCR_MIN_DUTY) | ||
| 108 | return -EINVAL; | ||
| 109 | |||
| 110 | /* | ||
| 111 | * if pv and dc have crossed their upper limit, then increase | ||
| 112 | * prescale and recalculate pv and dc. | ||
| 113 | */ | ||
| 114 | if (pv > PWMPCR_MAX_PERIOD || dc > PWMDCR_MAX_DUTY) { | ||
| 115 | if (++prescale > PWMCR_MAX_PRESCALE) | ||
| 116 | return -EINVAL; | ||
| 117 | continue; | ||
| 118 | } | ||
| 119 | break; | ||
| 120 | } | ||
| 121 | |||
| 122 | /* | ||
| 123 | * NOTE: the clock to PWM has to be enabled first before writing to the | ||
| 124 | * registers. | ||
| 125 | */ | ||
| 126 | ret = clk_enable(pc->clk); | ||
| 127 | if (ret) | ||
| 128 | return ret; | ||
| 129 | |||
| 130 | spear_pwm_writel(pc, pwm->hwpwm, PWMCR, | ||
| 131 | prescale << PWMCR_PRESCALE_SHIFT); | ||
| 132 | spear_pwm_writel(pc, pwm->hwpwm, PWMDCR, dc); | ||
| 133 | spear_pwm_writel(pc, pwm->hwpwm, PWMPCR, pv); | ||
| 134 | clk_disable(pc->clk); | ||
| 135 | |||
| 136 | return 0; | ||
| 137 | } | ||
| 138 | |||
| 139 | static int spear_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 140 | { | ||
| 141 | struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); | ||
| 142 | int rc = 0; | ||
| 143 | u32 val; | ||
| 144 | |||
| 145 | rc = clk_enable(pc->clk); | ||
| 146 | if (!rc) | ||
| 147 | return rc; | ||
| 148 | |||
| 149 | val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR); | ||
| 150 | val |= PWMCR_PWM_ENABLE; | ||
| 151 | spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val); | ||
| 152 | |||
| 153 | return 0; | ||
| 154 | } | ||
| 155 | |||
| 156 | static void spear_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 157 | { | ||
| 158 | struct spear_pwm_chip *pc = to_spear_pwm_chip(chip); | ||
| 159 | u32 val; | ||
| 160 | |||
| 161 | val = spear_pwm_readl(pc, pwm->hwpwm, PWMCR); | ||
| 162 | val &= ~PWMCR_PWM_ENABLE; | ||
| 163 | spear_pwm_writel(pc, pwm->hwpwm, PWMCR, val); | ||
| 164 | |||
| 165 | clk_disable(pc->clk); | ||
| 166 | } | ||
| 167 | |||
| 168 | static const struct pwm_ops spear_pwm_ops = { | ||
| 169 | .config = spear_pwm_config, | ||
| 170 | .enable = spear_pwm_enable, | ||
| 171 | .disable = spear_pwm_disable, | ||
| 172 | .owner = THIS_MODULE, | ||
| 173 | }; | ||
| 174 | |||
| 175 | static int spear_pwm_probe(struct platform_device *pdev) | ||
| 176 | { | ||
| 177 | struct device_node *np = pdev->dev.of_node; | ||
| 178 | struct spear_pwm_chip *pc; | ||
| 179 | struct resource *r; | ||
| 180 | int ret; | ||
| 181 | u32 val; | ||
| 182 | |||
| 183 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 184 | if (!r) { | ||
| 185 | dev_err(&pdev->dev, "no memory resources defined\n"); | ||
| 186 | return -ENODEV; | ||
| 187 | } | ||
| 188 | |||
| 189 | pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); | ||
| 190 | if (!pc) { | ||
| 191 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
| 192 | return -ENOMEM; | ||
| 193 | } | ||
| 194 | |||
| 195 | pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r); | ||
| 196 | if (!pc->mmio_base) | ||
| 197 | return -EADDRNOTAVAIL; | ||
| 198 | |||
| 199 | pc->clk = devm_clk_get(&pdev->dev, NULL); | ||
| 200 | if (IS_ERR(pc->clk)) | ||
| 201 | return PTR_ERR(pc->clk); | ||
| 202 | |||
| 203 | pc->dev = &pdev->dev; | ||
| 204 | platform_set_drvdata(pdev, pc); | ||
| 205 | |||
| 206 | pc->chip.dev = &pdev->dev; | ||
| 207 | pc->chip.ops = &spear_pwm_ops; | ||
| 208 | pc->chip.base = -1; | ||
| 209 | pc->chip.npwm = NUM_PWM; | ||
| 210 | |||
| 211 | ret = clk_prepare(pc->clk); | ||
| 212 | if (!ret) | ||
| 213 | return ret; | ||
| 214 | |||
| 215 | if (of_device_is_compatible(np, "st,spear1340-pwm")) { | ||
| 216 | ret = clk_enable(pc->clk); | ||
| 217 | if (!ret) { | ||
| 218 | clk_unprepare(pc->clk); | ||
| 219 | return ret; | ||
| 220 | } | ||
| 221 | /* | ||
| 222 | * Following enables PWM chip, channels would still be | ||
| 223 | * enabled individually through their control register | ||
| 224 | */ | ||
| 225 | val = readl_relaxed(pc->mmio_base + PWMMCR); | ||
| 226 | val |= PWMMCR_PWM_ENABLE; | ||
| 227 | writel_relaxed(val, pc->mmio_base + PWMMCR); | ||
| 228 | |||
| 229 | clk_disable(pc->clk); | ||
| 230 | } | ||
| 231 | |||
| 232 | ret = pwmchip_add(&pc->chip); | ||
| 233 | if (!ret) { | ||
| 234 | clk_unprepare(pc->clk); | ||
| 235 | dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); | ||
| 236 | } | ||
| 237 | |||
| 238 | return ret; | ||
| 239 | } | ||
| 240 | |||
| 241 | static int spear_pwm_remove(struct platform_device *pdev) | ||
| 242 | { | ||
| 243 | struct spear_pwm_chip *pc = platform_get_drvdata(pdev); | ||
| 244 | int i; | ||
| 245 | |||
| 246 | for (i = 0; i < NUM_PWM; i++) | ||
| 247 | pwm_disable(&pc->chip.pwms[i]); | ||
| 248 | |||
| 249 | /* clk was prepared in probe, hence unprepare it here */ | ||
| 250 | clk_unprepare(pc->clk); | ||
| 251 | return pwmchip_remove(&pc->chip); | ||
| 252 | } | ||
| 253 | |||
| 254 | static struct of_device_id spear_pwm_of_match[] = { | ||
| 255 | { .compatible = "st,spear320-pwm" }, | ||
| 256 | { .compatible = "st,spear1340-pwm" }, | ||
| 257 | { } | ||
| 258 | }; | ||
| 259 | |||
| 260 | MODULE_DEVICE_TABLE(of, spear_pwm_of_match); | ||
| 261 | |||
| 262 | static struct platform_driver spear_pwm_driver = { | ||
| 263 | .driver = { | ||
| 264 | .name = "spear-pwm", | ||
| 265 | .of_match_table = spear_pwm_of_match, | ||
| 266 | }, | ||
| 267 | .probe = spear_pwm_probe, | ||
| 268 | .remove = spear_pwm_remove, | ||
| 269 | }; | ||
| 270 | |||
| 271 | module_platform_driver(spear_pwm_driver); | ||
| 272 | |||
| 273 | MODULE_LICENSE("GPL"); | ||
| 274 | MODULE_AUTHOR("Shiraz Hashim <shiraz.hashim@st.com>"); | ||
| 275 | MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.com>"); | ||
| 276 | MODULE_ALIAS("platform:spear-pwm"); | ||
diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c index 87c091b245cc..5cf016dd9822 100644 --- a/drivers/pwm/pwm-tiecap.c +++ b/drivers/pwm/pwm-tiecap.c | |||
| @@ -25,6 +25,10 @@ | |||
| 25 | #include <linux/clk.h> | 25 | #include <linux/clk.h> |
| 26 | #include <linux/pm_runtime.h> | 26 | #include <linux/pm_runtime.h> |
| 27 | #include <linux/pwm.h> | 27 | #include <linux/pwm.h> |
| 28 | #include <linux/of_device.h> | ||
| 29 | #include <linux/pinctrl/consumer.h> | ||
| 30 | |||
| 31 | #include "pwm-tipwmss.h" | ||
| 28 | 32 | ||
| 29 | /* ECAP registers and bits definitions */ | 33 | /* ECAP registers and bits definitions */ |
| 30 | #define CAP1 0x08 | 34 | #define CAP1 0x08 |
| @@ -184,12 +188,24 @@ static const struct pwm_ops ecap_pwm_ops = { | |||
| 184 | .owner = THIS_MODULE, | 188 | .owner = THIS_MODULE, |
| 185 | }; | 189 | }; |
| 186 | 190 | ||
| 191 | static const struct of_device_id ecap_of_match[] = { | ||
| 192 | { .compatible = "ti,am33xx-ecap" }, | ||
| 193 | {}, | ||
| 194 | }; | ||
| 195 | MODULE_DEVICE_TABLE(of, ecap_of_match); | ||
| 196 | |||
| 187 | static int ecap_pwm_probe(struct platform_device *pdev) | 197 | static int ecap_pwm_probe(struct platform_device *pdev) |
| 188 | { | 198 | { |
| 189 | int ret; | 199 | int ret; |
| 190 | struct resource *r; | 200 | struct resource *r; |
| 191 | struct clk *clk; | 201 | struct clk *clk; |
| 192 | struct ecap_pwm_chip *pc; | 202 | struct ecap_pwm_chip *pc; |
| 203 | u16 status; | ||
| 204 | struct pinctrl *pinctrl; | ||
| 205 | |||
| 206 | pinctrl = devm_pinctrl_get_select_default(&pdev->dev); | ||
| 207 | if (IS_ERR(pinctrl)) | ||
| 208 | dev_warn(&pdev->dev, "unable to select pin group\n"); | ||
| 193 | 209 | ||
| 194 | pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); | 210 | pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); |
| 195 | if (!pc) { | 211 | if (!pc) { |
| @@ -211,6 +227,8 @@ static int ecap_pwm_probe(struct platform_device *pdev) | |||
| 211 | 227 | ||
| 212 | pc->chip.dev = &pdev->dev; | 228 | pc->chip.dev = &pdev->dev; |
| 213 | pc->chip.ops = &ecap_pwm_ops; | 229 | pc->chip.ops = &ecap_pwm_ops; |
| 230 | pc->chip.of_xlate = of_pwm_xlate_with_flags; | ||
| 231 | pc->chip.of_pwm_n_cells = 3; | ||
| 214 | pc->chip.base = -1; | 232 | pc->chip.base = -1; |
| 215 | pc->chip.npwm = 1; | 233 | pc->chip.npwm = 1; |
| 216 | 234 | ||
| @@ -231,14 +249,40 @@ static int ecap_pwm_probe(struct platform_device *pdev) | |||
| 231 | } | 249 | } |
| 232 | 250 | ||
| 233 | pm_runtime_enable(&pdev->dev); | 251 | pm_runtime_enable(&pdev->dev); |
| 252 | pm_runtime_get_sync(&pdev->dev); | ||
| 253 | |||
| 254 | status = pwmss_submodule_state_change(pdev->dev.parent, | ||
| 255 | PWMSS_ECAPCLK_EN); | ||
| 256 | if (!(status & PWMSS_ECAPCLK_EN_ACK)) { | ||
| 257 | dev_err(&pdev->dev, "PWMSS config space clock enable failed\n"); | ||
| 258 | ret = -EINVAL; | ||
| 259 | goto pwmss_clk_failure; | ||
| 260 | } | ||
| 261 | |||
| 262 | pm_runtime_put_sync(&pdev->dev); | ||
| 263 | |||
| 234 | platform_set_drvdata(pdev, pc); | 264 | platform_set_drvdata(pdev, pc); |
| 235 | return 0; | 265 | return 0; |
| 266 | |||
| 267 | pwmss_clk_failure: | ||
| 268 | pm_runtime_put_sync(&pdev->dev); | ||
| 269 | pm_runtime_disable(&pdev->dev); | ||
| 270 | pwmchip_remove(&pc->chip); | ||
| 271 | return ret; | ||
| 236 | } | 272 | } |
| 237 | 273 | ||
| 238 | static int ecap_pwm_remove(struct platform_device *pdev) | 274 | static int ecap_pwm_remove(struct platform_device *pdev) |
| 239 | { | 275 | { |
| 240 | struct ecap_pwm_chip *pc = platform_get_drvdata(pdev); | 276 | struct ecap_pwm_chip *pc = platform_get_drvdata(pdev); |
| 241 | 277 | ||
| 278 | pm_runtime_get_sync(&pdev->dev); | ||
| 279 | /* | ||
| 280 | * Due to hardware misbehaviour, acknowledge of the stop_req | ||
| 281 | * is missing. Hence checking of the status bit skipped. | ||
| 282 | */ | ||
| 283 | pwmss_submodule_state_change(pdev->dev.parent, PWMSS_ECAPCLK_STOP_REQ); | ||
| 284 | pm_runtime_put_sync(&pdev->dev); | ||
| 285 | |||
| 242 | pm_runtime_put_sync(&pdev->dev); | 286 | pm_runtime_put_sync(&pdev->dev); |
| 243 | pm_runtime_disable(&pdev->dev); | 287 | pm_runtime_disable(&pdev->dev); |
| 244 | return pwmchip_remove(&pc->chip); | 288 | return pwmchip_remove(&pc->chip); |
| @@ -246,7 +290,9 @@ static int ecap_pwm_remove(struct platform_device *pdev) | |||
| 246 | 290 | ||
| 247 | static struct platform_driver ecap_pwm_driver = { | 291 | static struct platform_driver ecap_pwm_driver = { |
| 248 | .driver = { | 292 | .driver = { |
| 249 | .name = "ecap", | 293 | .name = "ecap", |
| 294 | .owner = THIS_MODULE, | ||
| 295 | .of_match_table = ecap_of_match, | ||
| 250 | }, | 296 | }, |
| 251 | .probe = ecap_pwm_probe, | 297 | .probe = ecap_pwm_probe, |
| 252 | .remove = ecap_pwm_remove, | 298 | .remove = ecap_pwm_remove, |
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c index 9ffd389d0c8b..72a6dd40c9ec 100644 --- a/drivers/pwm/pwm-tiehrpwm.c +++ b/drivers/pwm/pwm-tiehrpwm.c | |||
| @@ -25,6 +25,10 @@ | |||
| 25 | #include <linux/err.h> | 25 | #include <linux/err.h> |
| 26 | #include <linux/clk.h> | 26 | #include <linux/clk.h> |
| 27 | #include <linux/pm_runtime.h> | 27 | #include <linux/pm_runtime.h> |
| 28 | #include <linux/of_device.h> | ||
| 29 | #include <linux/pinctrl/consumer.h> | ||
| 30 | |||
| 31 | #include "pwm-tipwmss.h" | ||
| 28 | 32 | ||
| 29 | /* EHRPWM registers and bits definitions */ | 33 | /* EHRPWM registers and bits definitions */ |
| 30 | 34 | ||
| @@ -115,6 +119,7 @@ struct ehrpwm_pwm_chip { | |||
| 115 | void __iomem *mmio_base; | 119 | void __iomem *mmio_base; |
| 116 | unsigned long period_cycles[NUM_PWM_CHANNEL]; | 120 | unsigned long period_cycles[NUM_PWM_CHANNEL]; |
| 117 | enum pwm_polarity polarity[NUM_PWM_CHANNEL]; | 121 | enum pwm_polarity polarity[NUM_PWM_CHANNEL]; |
| 122 | struct clk *tbclk; | ||
| 118 | }; | 123 | }; |
| 119 | 124 | ||
| 120 | static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) | 125 | static inline struct ehrpwm_pwm_chip *to_ehrpwm_pwm_chip(struct pwm_chip *chip) |
| @@ -335,6 +340,9 @@ static int ehrpwm_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
| 335 | /* Channels polarity can be configured from action qualifier module */ | 340 | /* Channels polarity can be configured from action qualifier module */ |
| 336 | configure_polarity(pc, pwm->hwpwm); | 341 | configure_polarity(pc, pwm->hwpwm); |
| 337 | 342 | ||
| 343 | /* Enable TBCLK before enabling PWM device */ | ||
| 344 | clk_enable(pc->tbclk); | ||
| 345 | |||
| 338 | /* Enable time counter for free_run */ | 346 | /* Enable time counter for free_run */ |
| 339 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); | 347 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_FREE_RUN); |
| 340 | return 0; | 348 | return 0; |
| @@ -363,6 +371,9 @@ static void ehrpwm_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
| 363 | 371 | ||
| 364 | ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); | 372 | ehrpwm_modify(pc->mmio_base, AQCSFRC, aqcsfrc_mask, aqcsfrc_val); |
| 365 | 373 | ||
| 374 | /* Disabling TBCLK on PWM disable */ | ||
| 375 | clk_disable(pc->tbclk); | ||
| 376 | |||
| 366 | /* Stop Time base counter */ | 377 | /* Stop Time base counter */ |
| 367 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT); | 378 | ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_RUN_MASK, TBCTL_STOP_NEXT); |
| 368 | 379 | ||
| @@ -392,12 +403,24 @@ static const struct pwm_ops ehrpwm_pwm_ops = { | |||
| 392 | .owner = THIS_MODULE, | 403 | .owner = THIS_MODULE, |
| 393 | }; | 404 | }; |
| 394 | 405 | ||
| 406 | static const struct of_device_id ehrpwm_of_match[] = { | ||
| 407 | { .compatible = "ti,am33xx-ehrpwm" }, | ||
| 408 | {}, | ||
| 409 | }; | ||
| 410 | MODULE_DEVICE_TABLE(of, ehrpwm_of_match); | ||
| 411 | |||
| 395 | static int ehrpwm_pwm_probe(struct platform_device *pdev) | 412 | static int ehrpwm_pwm_probe(struct platform_device *pdev) |
| 396 | { | 413 | { |
| 397 | int ret; | 414 | int ret; |
| 398 | struct resource *r; | 415 | struct resource *r; |
| 399 | struct clk *clk; | 416 | struct clk *clk; |
| 400 | struct ehrpwm_pwm_chip *pc; | 417 | struct ehrpwm_pwm_chip *pc; |
| 418 | u16 status; | ||
| 419 | struct pinctrl *pinctrl; | ||
| 420 | |||
| 421 | pinctrl = devm_pinctrl_get_select_default(&pdev->dev); | ||
| 422 | if (IS_ERR(pinctrl)) | ||
| 423 | dev_warn(&pdev->dev, "unable to select pin group\n"); | ||
| 401 | 424 | ||
| 402 | pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); | 425 | pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL); |
| 403 | if (!pc) { | 426 | if (!pc) { |
| @@ -419,6 +442,8 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) | |||
| 419 | 442 | ||
| 420 | pc->chip.dev = &pdev->dev; | 443 | pc->chip.dev = &pdev->dev; |
| 421 | pc->chip.ops = &ehrpwm_pwm_ops; | 444 | pc->chip.ops = &ehrpwm_pwm_ops; |
| 445 | pc->chip.of_xlate = of_pwm_xlate_with_flags; | ||
| 446 | pc->chip.of_pwm_n_cells = 3; | ||
| 422 | pc->chip.base = -1; | 447 | pc->chip.base = -1; |
| 423 | pc->chip.npwm = NUM_PWM_CHANNEL; | 448 | pc->chip.npwm = NUM_PWM_CHANNEL; |
| 424 | 449 | ||
| @@ -432,6 +457,13 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) | |||
| 432 | if (!pc->mmio_base) | 457 | if (!pc->mmio_base) |
| 433 | return -EADDRNOTAVAIL; | 458 | return -EADDRNOTAVAIL; |
| 434 | 459 | ||
| 460 | /* Acquire tbclk for Time Base EHRPWM submodule */ | ||
| 461 | pc->tbclk = devm_clk_get(&pdev->dev, "tbclk"); | ||
| 462 | if (IS_ERR(pc->tbclk)) { | ||
| 463 | dev_err(&pdev->dev, "Failed to get tbclk\n"); | ||
| 464 | return PTR_ERR(pc->tbclk); | ||
| 465 | } | ||
| 466 | |||
| 435 | ret = pwmchip_add(&pc->chip); | 467 | ret = pwmchip_add(&pc->chip); |
| 436 | if (ret < 0) { | 468 | if (ret < 0) { |
| 437 | dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); | 469 | dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); |
| @@ -439,14 +471,40 @@ static int ehrpwm_pwm_probe(struct platform_device *pdev) | |||
| 439 | } | 471 | } |
| 440 | 472 | ||
| 441 | pm_runtime_enable(&pdev->dev); | 473 | pm_runtime_enable(&pdev->dev); |
| 474 | pm_runtime_get_sync(&pdev->dev); | ||
| 475 | |||
| 476 | status = pwmss_submodule_state_change(pdev->dev.parent, | ||
| 477 | PWMSS_EPWMCLK_EN); | ||
| 478 | if (!(status & PWMSS_EPWMCLK_EN_ACK)) { | ||
| 479 | dev_err(&pdev->dev, "PWMSS config space clock enable failed\n"); | ||
| 480 | ret = -EINVAL; | ||
| 481 | goto pwmss_clk_failure; | ||
| 482 | } | ||
| 483 | |||
| 484 | pm_runtime_put_sync(&pdev->dev); | ||
| 485 | |||
| 442 | platform_set_drvdata(pdev, pc); | 486 | platform_set_drvdata(pdev, pc); |
| 443 | return 0; | 487 | return 0; |
| 488 | |||
| 489 | pwmss_clk_failure: | ||
| 490 | pm_runtime_put_sync(&pdev->dev); | ||
| 491 | pm_runtime_disable(&pdev->dev); | ||
| 492 | pwmchip_remove(&pc->chip); | ||
| 493 | return ret; | ||
| 444 | } | 494 | } |
| 445 | 495 | ||
| 446 | static int ehrpwm_pwm_remove(struct platform_device *pdev) | 496 | static int ehrpwm_pwm_remove(struct platform_device *pdev) |
| 447 | { | 497 | { |
| 448 | struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev); | 498 | struct ehrpwm_pwm_chip *pc = platform_get_drvdata(pdev); |
| 449 | 499 | ||
| 500 | pm_runtime_get_sync(&pdev->dev); | ||
| 501 | /* | ||
| 502 | * Due to hardware misbehaviour, acknowledge of the stop_req | ||
| 503 | * is missing. Hence checking of the status bit skipped. | ||
| 504 | */ | ||
| 505 | pwmss_submodule_state_change(pdev->dev.parent, PWMSS_EPWMCLK_STOP_REQ); | ||
| 506 | pm_runtime_put_sync(&pdev->dev); | ||
| 507 | |||
| 450 | pm_runtime_put_sync(&pdev->dev); | 508 | pm_runtime_put_sync(&pdev->dev); |
| 451 | pm_runtime_disable(&pdev->dev); | 509 | pm_runtime_disable(&pdev->dev); |
| 452 | return pwmchip_remove(&pc->chip); | 510 | return pwmchip_remove(&pc->chip); |
| @@ -454,7 +512,9 @@ static int ehrpwm_pwm_remove(struct platform_device *pdev) | |||
| 454 | 512 | ||
| 455 | static struct platform_driver ehrpwm_pwm_driver = { | 513 | static struct platform_driver ehrpwm_pwm_driver = { |
| 456 | .driver = { | 514 | .driver = { |
| 457 | .name = "ehrpwm", | 515 | .name = "ehrpwm", |
| 516 | .owner = THIS_MODULE, | ||
| 517 | .of_match_table = ehrpwm_of_match, | ||
| 458 | }, | 518 | }, |
| 459 | .probe = ehrpwm_pwm_probe, | 519 | .probe = ehrpwm_pwm_probe, |
| 460 | .remove = ehrpwm_pwm_remove, | 520 | .remove = ehrpwm_pwm_remove, |
diff --git a/drivers/pwm/pwm-tipwmss.c b/drivers/pwm/pwm-tipwmss.c new file mode 100644 index 000000000000..3448a1c88590 --- /dev/null +++ b/drivers/pwm/pwm-tipwmss.c | |||
| @@ -0,0 +1,139 @@ | |||
| 1 | /* | ||
| 2 | * TI PWM Subsystem driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #include <linux/module.h> | ||
| 19 | #include <linux/platform_device.h> | ||
| 20 | #include <linux/io.h> | ||
| 21 | #include <linux/err.h> | ||
| 22 | #include <linux/pm_runtime.h> | ||
| 23 | #include <linux/of_device.h> | ||
| 24 | |||
| 25 | #include "pwm-tipwmss.h" | ||
| 26 | |||
| 27 | #define PWMSS_CLKCONFIG 0x8 /* Clock gating reg */ | ||
| 28 | #define PWMSS_CLKSTATUS 0xc /* Clock gating status reg */ | ||
| 29 | |||
| 30 | struct pwmss_info { | ||
| 31 | void __iomem *mmio_base; | ||
| 32 | struct mutex pwmss_lock; | ||
| 33 | u16 pwmss_clkconfig; | ||
| 34 | }; | ||
| 35 | |||
| 36 | u16 pwmss_submodule_state_change(struct device *dev, int set) | ||
| 37 | { | ||
| 38 | struct pwmss_info *info = dev_get_drvdata(dev); | ||
| 39 | u16 val; | ||
| 40 | |||
| 41 | mutex_lock(&info->pwmss_lock); | ||
| 42 | val = readw(info->mmio_base + PWMSS_CLKCONFIG); | ||
| 43 | val |= set; | ||
| 44 | writew(val , info->mmio_base + PWMSS_CLKCONFIG); | ||
| 45 | mutex_unlock(&info->pwmss_lock); | ||
| 46 | |||
| 47 | return readw(info->mmio_base + PWMSS_CLKSTATUS); | ||
| 48 | } | ||
| 49 | EXPORT_SYMBOL(pwmss_submodule_state_change); | ||
| 50 | |||
| 51 | static const struct of_device_id pwmss_of_match[] = { | ||
| 52 | { .compatible = "ti,am33xx-pwmss" }, | ||
| 53 | {}, | ||
| 54 | }; | ||
| 55 | MODULE_DEVICE_TABLE(of, pwmss_of_match); | ||
| 56 | |||
| 57 | static int pwmss_probe(struct platform_device *pdev) | ||
| 58 | { | ||
| 59 | int ret; | ||
| 60 | struct resource *r; | ||
| 61 | struct pwmss_info *info; | ||
| 62 | struct device_node *node = pdev->dev.of_node; | ||
| 63 | |||
| 64 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | ||
| 65 | if (!info) { | ||
| 66 | dev_err(&pdev->dev, "failed to allocate memory\n"); | ||
| 67 | return -ENOMEM; | ||
| 68 | } | ||
| 69 | |||
| 70 | mutex_init(&info->pwmss_lock); | ||
| 71 | |||
| 72 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
| 73 | if (!r) { | ||
| 74 | dev_err(&pdev->dev, "no memory resource defined\n"); | ||
| 75 | return -ENODEV; | ||
| 76 | } | ||
| 77 | |||
| 78 | info->mmio_base = devm_request_and_ioremap(&pdev->dev, r); | ||
| 79 | if (!info->mmio_base) | ||
| 80 | return -EADDRNOTAVAIL; | ||
| 81 | |||
| 82 | pm_runtime_enable(&pdev->dev); | ||
| 83 | pm_runtime_get_sync(&pdev->dev); | ||
| 84 | platform_set_drvdata(pdev, info); | ||
| 85 | |||
| 86 | /* Populate all the child nodes here... */ | ||
| 87 | ret = of_platform_populate(node, NULL, NULL, &pdev->dev); | ||
| 88 | if (ret) | ||
| 89 | dev_err(&pdev->dev, "no child node found\n"); | ||
| 90 | |||
| 91 | return ret; | ||
| 92 | } | ||
| 93 | |||
| 94 | static int pwmss_remove(struct platform_device *pdev) | ||
| 95 | { | ||
| 96 | struct pwmss_info *info = platform_get_drvdata(pdev); | ||
| 97 | |||
| 98 | pm_runtime_put_sync(&pdev->dev); | ||
| 99 | pm_runtime_disable(&pdev->dev); | ||
| 100 | mutex_destroy(&info->pwmss_lock); | ||
| 101 | return 0; | ||
| 102 | } | ||
| 103 | |||
| 104 | static int pwmss_suspend(struct device *dev) | ||
| 105 | { | ||
| 106 | struct pwmss_info *info = dev_get_drvdata(dev); | ||
| 107 | |||
| 108 | info->pwmss_clkconfig = readw(info->mmio_base + PWMSS_CLKCONFIG); | ||
| 109 | pm_runtime_put_sync(dev); | ||
| 110 | return 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | static int pwmss_resume(struct device *dev) | ||
| 114 | { | ||
| 115 | struct pwmss_info *info = dev_get_drvdata(dev); | ||
| 116 | |||
| 117 | pm_runtime_get_sync(dev); | ||
| 118 | writew(info->pwmss_clkconfig, info->mmio_base + PWMSS_CLKCONFIG); | ||
| 119 | return 0; | ||
| 120 | } | ||
| 121 | |||
| 122 | static SIMPLE_DEV_PM_OPS(pwmss_pm_ops, pwmss_suspend, pwmss_resume); | ||
| 123 | |||
| 124 | static struct platform_driver pwmss_driver = { | ||
| 125 | .driver = { | ||
| 126 | .name = "pwmss", | ||
| 127 | .owner = THIS_MODULE, | ||
| 128 | .pm = &pwmss_pm_ops, | ||
| 129 | .of_match_table = pwmss_of_match, | ||
| 130 | }, | ||
| 131 | .probe = pwmss_probe, | ||
| 132 | .remove = pwmss_remove, | ||
| 133 | }; | ||
| 134 | |||
| 135 | module_platform_driver(pwmss_driver); | ||
| 136 | |||
| 137 | MODULE_DESCRIPTION("PWM Subsystem driver"); | ||
| 138 | MODULE_AUTHOR("Texas Instruments"); | ||
| 139 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/pwm/pwm-tipwmss.h b/drivers/pwm/pwm-tipwmss.h new file mode 100644 index 000000000000..11f76a1e266b --- /dev/null +++ b/drivers/pwm/pwm-tipwmss.h | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | /* | ||
| 2 | * TI PWM Subsystem driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/ | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | */ | ||
| 17 | |||
| 18 | #ifndef __TIPWMSS_H | ||
| 19 | #define __TIPWMSS_H | ||
| 20 | |||
| 21 | #ifdef CONFIG_PWM_TIPWMSS | ||
| 22 | /* PWM substem clock gating */ | ||
| 23 | #define PWMSS_ECAPCLK_EN BIT(0) | ||
| 24 | #define PWMSS_ECAPCLK_STOP_REQ BIT(1) | ||
| 25 | #define PWMSS_EPWMCLK_EN BIT(8) | ||
| 26 | #define PWMSS_EPWMCLK_STOP_REQ BIT(9) | ||
| 27 | |||
| 28 | #define PWMSS_ECAPCLK_EN_ACK BIT(0) | ||
| 29 | #define PWMSS_EPWMCLK_EN_ACK BIT(8) | ||
| 30 | |||
| 31 | extern u16 pwmss_submodule_state_change(struct device *dev, int set); | ||
| 32 | #else | ||
| 33 | static inline u16 pwmss_submodule_state_change(struct device *dev, int set) | ||
| 34 | { | ||
| 35 | /* return success status value */ | ||
| 36 | return 0xFFFF; | ||
| 37 | } | ||
| 38 | #endif | ||
| 39 | #endif /* __TIPWMSS_H */ | ||
diff --git a/drivers/pwm/pwm-twl-led.c b/drivers/pwm/pwm-twl-led.c new file mode 100644 index 000000000000..9dfa0f3eca30 --- /dev/null +++ b/drivers/pwm/pwm-twl-led.c | |||
| @@ -0,0 +1,344 @@ | |||
| 1 | /* | ||
| 2 | * Driver for TWL4030/6030 Pulse Width Modulator used as LED driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 Texas Instruments | ||
| 5 | * Author: Peter Ujfalusi <peter.ujfalusi@ti.com> | ||
| 6 | * | ||
| 7 | * This driver is a complete rewrite of the former pwm-twl6030.c authorded by: | ||
| 8 | * Hemanth V <hemanthv@ti.com> | ||
| 9 | * | ||
| 10 | * This program is free software; you can redistribute it and/or modify it | ||
| 11 | * under the terms of the GNU General Public License version 2 as published by | ||
| 12 | * the Free Software Foundation. | ||
| 13 | * | ||
| 14 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 17 | * more details. | ||
| 18 | * | ||
| 19 | * You should have received a copy of the GNU General Public License along with | ||
| 20 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 21 | */ | ||
| 22 | |||
| 23 | #include <linux/module.h> | ||
| 24 | #include <linux/platform_device.h> | ||
| 25 | #include <linux/pwm.h> | ||
| 26 | #include <linux/i2c/twl.h> | ||
| 27 | #include <linux/slab.h> | ||
| 28 | |||
| 29 | /* | ||
| 30 | * This driver handles the PWM driven LED terminals of TWL4030 and TWL6030. | ||
| 31 | * To generate the signal on TWL4030: | ||
| 32 | * - LEDA uses PWMA | ||
| 33 | * - LEDB uses PWMB | ||
| 34 | * TWL6030 has one LED pin with dedicated LEDPWM | ||
| 35 | */ | ||
| 36 | |||
| 37 | #define TWL4030_LED_MAX 0x7f | ||
| 38 | #define TWL6030_LED_MAX 0xff | ||
| 39 | |||
| 40 | /* Registers, bits and macro for TWL4030 */ | ||
| 41 | #define TWL4030_LEDEN_REG 0x00 | ||
| 42 | #define TWL4030_PWMA_REG 0x01 | ||
| 43 | |||
| 44 | #define TWL4030_LEDXON (1 << 0) | ||
| 45 | #define TWL4030_LEDXPWM (1 << 4) | ||
| 46 | #define TWL4030_LED_PINS (TWL4030_LEDXON | TWL4030_LEDXPWM) | ||
| 47 | #define TWL4030_LED_TOGGLE(led, x) ((x) << (led)) | ||
| 48 | |||
| 49 | /* Register, bits and macro for TWL6030 */ | ||
| 50 | #define TWL6030_LED_PWM_CTRL1 0xf4 | ||
| 51 | #define TWL6030_LED_PWM_CTRL2 0xf5 | ||
| 52 | |||
| 53 | #define TWL6040_LED_MODE_HW 0x00 | ||
| 54 | #define TWL6040_LED_MODE_ON 0x01 | ||
| 55 | #define TWL6040_LED_MODE_OFF 0x02 | ||
| 56 | #define TWL6040_LED_MODE_MASK 0x03 | ||
| 57 | |||
| 58 | struct twl_pwmled_chip { | ||
| 59 | struct pwm_chip chip; | ||
| 60 | struct mutex mutex; | ||
| 61 | }; | ||
| 62 | |||
| 63 | static inline struct twl_pwmled_chip *to_twl(struct pwm_chip *chip) | ||
| 64 | { | ||
| 65 | return container_of(chip, struct twl_pwmled_chip, chip); | ||
| 66 | } | ||
| 67 | |||
| 68 | static int twl4030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
| 69 | int duty_ns, int period_ns) | ||
| 70 | { | ||
| 71 | int duty_cycle = DIV_ROUND_UP(duty_ns * TWL4030_LED_MAX, period_ns) + 1; | ||
| 72 | u8 pwm_config[2] = { 1, 0 }; | ||
| 73 | int base, ret; | ||
| 74 | |||
| 75 | /* | ||
| 76 | * To configure the duty period: | ||
| 77 | * On-cycle is set to 1 (the minimum allowed value) | ||
| 78 | * The off time of 0 is not configurable, so the mapping is: | ||
| 79 | * 0 -> off cycle = 2, | ||
| 80 | * 1 -> off cycle = 2, | ||
| 81 | * 2 -> off cycle = 3, | ||
| 82 | * 126 - > off cycle 127, | ||
| 83 | * 127 - > off cycle 1 | ||
| 84 | * When on cycle == off cycle the PWM will be always on | ||
| 85 | */ | ||
| 86 | if (duty_cycle == 1) | ||
| 87 | duty_cycle = 2; | ||
| 88 | else if (duty_cycle > TWL4030_LED_MAX) | ||
| 89 | duty_cycle = 1; | ||
| 90 | |||
| 91 | base = pwm->hwpwm * 2 + TWL4030_PWMA_REG; | ||
| 92 | |||
| 93 | pwm_config[1] = duty_cycle; | ||
| 94 | |||
| 95 | ret = twl_i2c_write(TWL4030_MODULE_LED, pwm_config, base, 2); | ||
| 96 | if (ret < 0) | ||
| 97 | dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); | ||
| 98 | |||
| 99 | return ret; | ||
| 100 | } | ||
| 101 | |||
| 102 | static int twl4030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 103 | { | ||
| 104 | struct twl_pwmled_chip *twl = to_twl(chip); | ||
| 105 | int ret; | ||
| 106 | u8 val; | ||
| 107 | |||
| 108 | mutex_lock(&twl->mutex); | ||
| 109 | ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); | ||
| 110 | if (ret < 0) { | ||
| 111 | dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label); | ||
| 112 | goto out; | ||
| 113 | } | ||
| 114 | |||
| 115 | val |= TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS); | ||
| 116 | |||
| 117 | ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG); | ||
| 118 | if (ret < 0) | ||
| 119 | dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); | ||
| 120 | |||
| 121 | out: | ||
| 122 | mutex_unlock(&twl->mutex); | ||
| 123 | return ret; | ||
| 124 | } | ||
| 125 | |||
| 126 | static void twl4030_pwmled_disable(struct pwm_chip *chip, | ||
| 127 | struct pwm_device *pwm) | ||
| 128 | { | ||
| 129 | struct twl_pwmled_chip *twl = to_twl(chip); | ||
| 130 | int ret; | ||
| 131 | u8 val; | ||
| 132 | |||
| 133 | mutex_lock(&twl->mutex); | ||
| 134 | ret = twl_i2c_read_u8(TWL4030_MODULE_LED, &val, TWL4030_LEDEN_REG); | ||
| 135 | if (ret < 0) { | ||
| 136 | dev_err(chip->dev, "%s: Failed to read LEDEN\n", pwm->label); | ||
| 137 | goto out; | ||
| 138 | } | ||
| 139 | |||
| 140 | val &= ~TWL4030_LED_TOGGLE(pwm->hwpwm, TWL4030_LED_PINS); | ||
| 141 | |||
| 142 | ret = twl_i2c_write_u8(TWL4030_MODULE_LED, val, TWL4030_LEDEN_REG); | ||
| 143 | if (ret < 0) | ||
| 144 | dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); | ||
| 145 | |||
| 146 | out: | ||
| 147 | mutex_unlock(&twl->mutex); | ||
| 148 | } | ||
| 149 | |||
| 150 | static int twl6030_pwmled_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
| 151 | int duty_ns, int period_ns) | ||
| 152 | { | ||
| 153 | int duty_cycle = (duty_ns * TWL6030_LED_MAX) / period_ns; | ||
| 154 | u8 on_time; | ||
| 155 | int ret; | ||
| 156 | |||
| 157 | on_time = duty_cycle & 0xff; | ||
| 158 | |||
| 159 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, on_time, | ||
| 160 | TWL6030_LED_PWM_CTRL1); | ||
| 161 | if (ret < 0) | ||
| 162 | dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); | ||
| 163 | |||
| 164 | return ret; | ||
| 165 | } | ||
| 166 | |||
| 167 | static int twl6030_pwmled_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 168 | { | ||
| 169 | struct twl_pwmled_chip *twl = to_twl(chip); | ||
| 170 | int ret; | ||
| 171 | u8 val; | ||
| 172 | |||
| 173 | mutex_lock(&twl->mutex); | ||
| 174 | ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); | ||
| 175 | if (ret < 0) { | ||
| 176 | dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", | ||
| 177 | pwm->label); | ||
| 178 | goto out; | ||
| 179 | } | ||
| 180 | |||
| 181 | val &= ~TWL6040_LED_MODE_MASK; | ||
| 182 | val |= TWL6040_LED_MODE_ON; | ||
| 183 | |||
| 184 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); | ||
| 185 | if (ret < 0) | ||
| 186 | dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); | ||
| 187 | |||
| 188 | out: | ||
| 189 | mutex_unlock(&twl->mutex); | ||
| 190 | return ret; | ||
| 191 | } | ||
| 192 | |||
| 193 | static void twl6030_pwmled_disable(struct pwm_chip *chip, | ||
| 194 | struct pwm_device *pwm) | ||
| 195 | { | ||
| 196 | struct twl_pwmled_chip *twl = to_twl(chip); | ||
| 197 | int ret; | ||
| 198 | u8 val; | ||
| 199 | |||
| 200 | mutex_lock(&twl->mutex); | ||
| 201 | ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); | ||
| 202 | if (ret < 0) { | ||
| 203 | dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", | ||
| 204 | pwm->label); | ||
| 205 | goto out; | ||
| 206 | } | ||
| 207 | |||
| 208 | val &= ~TWL6040_LED_MODE_MASK; | ||
| 209 | val |= TWL6040_LED_MODE_OFF; | ||
| 210 | |||
| 211 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); | ||
| 212 | if (ret < 0) | ||
| 213 | dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); | ||
| 214 | |||
| 215 | out: | ||
| 216 | mutex_unlock(&twl->mutex); | ||
| 217 | } | ||
| 218 | |||
| 219 | static int twl6030_pwmled_request(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 220 | { | ||
| 221 | struct twl_pwmled_chip *twl = to_twl(chip); | ||
| 222 | int ret; | ||
| 223 | u8 val; | ||
| 224 | |||
| 225 | mutex_lock(&twl->mutex); | ||
| 226 | ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); | ||
| 227 | if (ret < 0) { | ||
| 228 | dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", | ||
| 229 | pwm->label); | ||
| 230 | goto out; | ||
| 231 | } | ||
| 232 | |||
| 233 | val &= ~TWL6040_LED_MODE_MASK; | ||
| 234 | val |= TWL6040_LED_MODE_OFF; | ||
| 235 | |||
| 236 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); | ||
| 237 | if (ret < 0) | ||
| 238 | dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label); | ||
| 239 | |||
| 240 | out: | ||
| 241 | mutex_unlock(&twl->mutex); | ||
| 242 | return ret; | ||
| 243 | } | ||
| 244 | |||
| 245 | static void twl6030_pwmled_free(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 246 | { | ||
| 247 | struct twl_pwmled_chip *twl = to_twl(chip); | ||
| 248 | int ret; | ||
| 249 | u8 val; | ||
| 250 | |||
| 251 | mutex_lock(&twl->mutex); | ||
| 252 | ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, TWL6030_LED_PWM_CTRL2); | ||
| 253 | if (ret < 0) { | ||
| 254 | dev_err(chip->dev, "%s: Failed to read PWM_CTRL2\n", | ||
| 255 | pwm->label); | ||
| 256 | goto out; | ||
| 257 | } | ||
| 258 | |||
| 259 | val &= ~TWL6040_LED_MODE_MASK; | ||
| 260 | val |= TWL6040_LED_MODE_HW; | ||
| 261 | |||
| 262 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_LED_PWM_CTRL2); | ||
| 263 | if (ret < 0) | ||
| 264 | dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label); | ||
| 265 | |||
| 266 | out: | ||
| 267 | mutex_unlock(&twl->mutex); | ||
| 268 | } | ||
| 269 | |||
| 270 | static const struct pwm_ops twl4030_pwmled_ops = { | ||
| 271 | .enable = twl4030_pwmled_enable, | ||
| 272 | .disable = twl4030_pwmled_disable, | ||
| 273 | .config = twl4030_pwmled_config, | ||
| 274 | }; | ||
| 275 | |||
| 276 | static const struct pwm_ops twl6030_pwmled_ops = { | ||
| 277 | .enable = twl6030_pwmled_enable, | ||
| 278 | .disable = twl6030_pwmled_disable, | ||
| 279 | .config = twl6030_pwmled_config, | ||
| 280 | .request = twl6030_pwmled_request, | ||
| 281 | .free = twl6030_pwmled_free, | ||
| 282 | }; | ||
| 283 | |||
| 284 | static int twl_pwmled_probe(struct platform_device *pdev) | ||
| 285 | { | ||
| 286 | struct twl_pwmled_chip *twl; | ||
| 287 | int ret; | ||
| 288 | |||
| 289 | twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); | ||
| 290 | if (!twl) | ||
| 291 | return -ENOMEM; | ||
| 292 | |||
| 293 | if (twl_class_is_4030()) { | ||
| 294 | twl->chip.ops = &twl4030_pwmled_ops; | ||
| 295 | twl->chip.npwm = 2; | ||
| 296 | } else { | ||
| 297 | twl->chip.ops = &twl6030_pwmled_ops; | ||
| 298 | twl->chip.npwm = 1; | ||
| 299 | } | ||
| 300 | |||
| 301 | twl->chip.dev = &pdev->dev; | ||
| 302 | twl->chip.base = -1; | ||
| 303 | |||
| 304 | mutex_init(&twl->mutex); | ||
| 305 | |||
| 306 | ret = pwmchip_add(&twl->chip); | ||
| 307 | if (ret < 0) | ||
| 308 | return ret; | ||
| 309 | |||
| 310 | platform_set_drvdata(pdev, twl); | ||
| 311 | |||
| 312 | return 0; | ||
| 313 | } | ||
| 314 | |||
| 315 | static int twl_pwmled_remove(struct platform_device *pdev) | ||
| 316 | { | ||
| 317 | struct twl_pwmled_chip *twl = platform_get_drvdata(pdev); | ||
| 318 | |||
| 319 | return pwmchip_remove(&twl->chip); | ||
| 320 | } | ||
| 321 | |||
| 322 | #ifdef CONFIG_OF | ||
| 323 | static struct of_device_id twl_pwmled_of_match[] = { | ||
| 324 | { .compatible = "ti,twl4030-pwmled" }, | ||
| 325 | { .compatible = "ti,twl6030-pwmled" }, | ||
| 326 | { }, | ||
| 327 | }; | ||
| 328 | MODULE_DEVICE_TABLE(of, twl_pwmled_of_match); | ||
| 329 | #endif | ||
| 330 | |||
| 331 | static struct platform_driver twl_pwmled_driver = { | ||
| 332 | .driver = { | ||
| 333 | .name = "twl-pwmled", | ||
| 334 | .of_match_table = of_match_ptr(twl_pwmled_of_match), | ||
| 335 | }, | ||
| 336 | .probe = twl_pwmled_probe, | ||
| 337 | .remove = twl_pwmled_remove, | ||
| 338 | }; | ||
| 339 | module_platform_driver(twl_pwmled_driver); | ||
| 340 | |||
| 341 | MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); | ||
| 342 | MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030 LED outputs"); | ||
| 343 | MODULE_ALIAS("platform:twl-pwmled"); | ||
| 344 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/pwm/pwm-twl.c b/drivers/pwm/pwm-twl.c new file mode 100644 index 000000000000..e65db95d5e59 --- /dev/null +++ b/drivers/pwm/pwm-twl.c | |||
| @@ -0,0 +1,359 @@ | |||
| 1 | /* | ||
| 2 | * Driver for TWL4030/6030 Generic Pulse Width Modulator | ||
| 3 | * | ||
| 4 | * Copyright (C) 2012 Texas Instruments | ||
| 5 | * Author: Peter Ujfalusi <peter.ujfalusi@ti.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 version 2 as published by | ||
| 9 | * the Free Software Foundation. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 14 | * more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License along with | ||
| 17 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 18 | */ | ||
| 19 | |||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/platform_device.h> | ||
| 22 | #include <linux/pwm.h> | ||
| 23 | #include <linux/i2c/twl.h> | ||
| 24 | #include <linux/slab.h> | ||
| 25 | |||
| 26 | /* | ||
| 27 | * This driver handles the PWMs of TWL4030 and TWL6030. | ||
| 28 | * The TRM names for the PWMs on TWL4030 are: PWM0, PWM1 | ||
| 29 | * TWL6030 also have two PWMs named in the TRM as PWM1, PWM2 | ||
| 30 | */ | ||
| 31 | |||
| 32 | #define TWL_PWM_MAX 0x7f | ||
| 33 | |||
| 34 | /* Registers, bits and macro for TWL4030 */ | ||
| 35 | #define TWL4030_GPBR1_REG 0x0c | ||
| 36 | #define TWL4030_PMBR1_REG 0x0d | ||
| 37 | |||
| 38 | /* GPBR1 register bits */ | ||
| 39 | #define TWL4030_PWMXCLK_ENABLE (1 << 0) | ||
| 40 | #define TWL4030_PWMX_ENABLE (1 << 2) | ||
| 41 | #define TWL4030_PWMX_BITS (TWL4030_PWMX_ENABLE | TWL4030_PWMXCLK_ENABLE) | ||
| 42 | #define TWL4030_PWM_TOGGLE(pwm, x) ((x) << (pwm)) | ||
| 43 | |||
| 44 | /* PMBR1 register bits */ | ||
| 45 | #define TWL4030_GPIO6_PWM0_MUTE_MASK (0x03 << 2) | ||
| 46 | #define TWL4030_GPIO6_PWM0_MUTE_PWM0 (0x01 << 2) | ||
| 47 | #define TWL4030_GPIO7_VIBRASYNC_PWM1_MASK (0x03 << 4) | ||
| 48 | #define TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1 (0x03 << 4) | ||
| 49 | |||
| 50 | /* Register, bits and macro for TWL6030 */ | ||
| 51 | #define TWL6030_TOGGLE3_REG 0x92 | ||
| 52 | |||
| 53 | #define TWL6030_PWMXR (1 << 0) | ||
| 54 | #define TWL6030_PWMXS (1 << 1) | ||
| 55 | #define TWL6030_PWMXEN (1 << 2) | ||
| 56 | #define TWL6030_PWM_TOGGLE(pwm, x) ((x) << (pwm * 3)) | ||
| 57 | |||
| 58 | struct twl_pwm_chip { | ||
| 59 | struct pwm_chip chip; | ||
| 60 | struct mutex mutex; | ||
| 61 | u8 twl6030_toggle3; | ||
| 62 | u8 twl4030_pwm_mux; | ||
| 63 | }; | ||
| 64 | |||
| 65 | static inline struct twl_pwm_chip *to_twl(struct pwm_chip *chip) | ||
| 66 | { | ||
| 67 | return container_of(chip, struct twl_pwm_chip, chip); | ||
| 68 | } | ||
| 69 | |||
| 70 | static int twl_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
| 71 | int duty_ns, int period_ns) | ||
| 72 | { | ||
| 73 | int duty_cycle = DIV_ROUND_UP(duty_ns * TWL_PWM_MAX, period_ns) + 1; | ||
| 74 | u8 pwm_config[2] = { 1, 0 }; | ||
| 75 | int base, ret; | ||
| 76 | |||
| 77 | /* | ||
| 78 | * To configure the duty period: | ||
| 79 | * On-cycle is set to 1 (the minimum allowed value) | ||
| 80 | * The off time of 0 is not configurable, so the mapping is: | ||
| 81 | * 0 -> off cycle = 2, | ||
| 82 | * 1 -> off cycle = 2, | ||
| 83 | * 2 -> off cycle = 3, | ||
| 84 | * 126 - > off cycle 127, | ||
| 85 | * 127 - > off cycle 1 | ||
| 86 | * When on cycle == off cycle the PWM will be always on | ||
| 87 | */ | ||
| 88 | if (duty_cycle == 1) | ||
| 89 | duty_cycle = 2; | ||
| 90 | else if (duty_cycle > TWL_PWM_MAX) | ||
| 91 | duty_cycle = 1; | ||
| 92 | |||
| 93 | base = pwm->hwpwm * 3; | ||
| 94 | |||
| 95 | pwm_config[1] = duty_cycle; | ||
| 96 | |||
| 97 | ret = twl_i2c_write(TWL_MODULE_PWM, pwm_config, base, 2); | ||
| 98 | if (ret < 0) | ||
| 99 | dev_err(chip->dev, "%s: Failed to configure PWM\n", pwm->label); | ||
| 100 | |||
| 101 | return ret; | ||
| 102 | } | ||
| 103 | |||
| 104 | static int twl4030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 105 | { | ||
| 106 | struct twl_pwm_chip *twl = to_twl(chip); | ||
| 107 | int ret; | ||
| 108 | u8 val; | ||
| 109 | |||
| 110 | mutex_lock(&twl->mutex); | ||
| 111 | ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG); | ||
| 112 | if (ret < 0) { | ||
| 113 | dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label); | ||
| 114 | goto out; | ||
| 115 | } | ||
| 116 | |||
| 117 | val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE); | ||
| 118 | |||
| 119 | ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); | ||
| 120 | if (ret < 0) | ||
| 121 | dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); | ||
| 122 | |||
| 123 | val |= TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE); | ||
| 124 | |||
| 125 | ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); | ||
| 126 | if (ret < 0) | ||
| 127 | dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); | ||
| 128 | |||
| 129 | out: | ||
| 130 | mutex_unlock(&twl->mutex); | ||
| 131 | return ret; | ||
| 132 | } | ||
| 133 | |||
| 134 | static void twl4030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 135 | { | ||
| 136 | struct twl_pwm_chip *twl = to_twl(chip); | ||
| 137 | int ret; | ||
| 138 | u8 val; | ||
| 139 | |||
| 140 | mutex_lock(&twl->mutex); | ||
| 141 | ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_GPBR1_REG); | ||
| 142 | if (ret < 0) { | ||
| 143 | dev_err(chip->dev, "%s: Failed to read GPBR1\n", pwm->label); | ||
| 144 | goto out; | ||
| 145 | } | ||
| 146 | |||
| 147 | val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMX_ENABLE); | ||
| 148 | |||
| 149 | ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); | ||
| 150 | if (ret < 0) | ||
| 151 | dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); | ||
| 152 | |||
| 153 | val &= ~TWL4030_PWM_TOGGLE(pwm->hwpwm, TWL4030_PWMXCLK_ENABLE); | ||
| 154 | |||
| 155 | ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_GPBR1_REG); | ||
| 156 | if (ret < 0) | ||
| 157 | dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); | ||
| 158 | |||
| 159 | out: | ||
| 160 | mutex_unlock(&twl->mutex); | ||
| 161 | } | ||
| 162 | |||
| 163 | static int twl4030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 164 | { | ||
| 165 | struct twl_pwm_chip *twl = to_twl(chip); | ||
| 166 | int ret; | ||
| 167 | u8 val, mask, bits; | ||
| 168 | |||
| 169 | if (pwm->hwpwm == 1) { | ||
| 170 | mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK; | ||
| 171 | bits = TWL4030_GPIO7_VIBRASYNC_PWM1_PWM1; | ||
| 172 | } else { | ||
| 173 | mask = TWL4030_GPIO6_PWM0_MUTE_MASK; | ||
| 174 | bits = TWL4030_GPIO6_PWM0_MUTE_PWM0; | ||
| 175 | } | ||
| 176 | |||
| 177 | mutex_lock(&twl->mutex); | ||
| 178 | ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG); | ||
| 179 | if (ret < 0) { | ||
| 180 | dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label); | ||
| 181 | goto out; | ||
| 182 | } | ||
| 183 | |||
| 184 | /* Save the current MUX configuration for the PWM */ | ||
| 185 | twl->twl4030_pwm_mux &= ~mask; | ||
| 186 | twl->twl4030_pwm_mux |= (val & mask); | ||
| 187 | |||
| 188 | /* Select PWM functionality */ | ||
| 189 | val &= ~mask; | ||
| 190 | val |= bits; | ||
| 191 | |||
| 192 | ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG); | ||
| 193 | if (ret < 0) | ||
| 194 | dev_err(chip->dev, "%s: Failed to request PWM\n", pwm->label); | ||
| 195 | |||
| 196 | out: | ||
| 197 | mutex_unlock(&twl->mutex); | ||
| 198 | return ret; | ||
| 199 | } | ||
| 200 | |||
| 201 | static void twl4030_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 202 | { | ||
| 203 | struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, | ||
| 204 | chip); | ||
| 205 | int ret; | ||
| 206 | u8 val, mask; | ||
| 207 | |||
| 208 | if (pwm->hwpwm == 1) | ||
| 209 | mask = TWL4030_GPIO7_VIBRASYNC_PWM1_MASK; | ||
| 210 | else | ||
| 211 | mask = TWL4030_GPIO6_PWM0_MUTE_MASK; | ||
| 212 | |||
| 213 | mutex_lock(&twl->mutex); | ||
| 214 | ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, &val, TWL4030_PMBR1_REG); | ||
| 215 | if (ret < 0) { | ||
| 216 | dev_err(chip->dev, "%s: Failed to read PMBR1\n", pwm->label); | ||
| 217 | goto out; | ||
| 218 | } | ||
| 219 | |||
| 220 | /* Restore the MUX configuration for the PWM */ | ||
| 221 | val &= ~mask; | ||
| 222 | val |= (twl->twl4030_pwm_mux & mask); | ||
| 223 | |||
| 224 | ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, val, TWL4030_PMBR1_REG); | ||
| 225 | if (ret < 0) | ||
| 226 | dev_err(chip->dev, "%s: Failed to free PWM\n", pwm->label); | ||
| 227 | |||
| 228 | out: | ||
| 229 | mutex_unlock(&twl->mutex); | ||
| 230 | } | ||
| 231 | |||
| 232 | static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 233 | { | ||
| 234 | struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, | ||
| 235 | chip); | ||
| 236 | int ret; | ||
| 237 | u8 val; | ||
| 238 | |||
| 239 | mutex_lock(&twl->mutex); | ||
| 240 | val = twl->twl6030_toggle3; | ||
| 241 | val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); | ||
| 242 | val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR); | ||
| 243 | |||
| 244 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); | ||
| 245 | if (ret < 0) { | ||
| 246 | dev_err(chip->dev, "%s: Failed to enable PWM\n", pwm->label); | ||
| 247 | goto out; | ||
| 248 | } | ||
| 249 | |||
| 250 | twl->twl6030_toggle3 = val; | ||
| 251 | out: | ||
| 252 | mutex_unlock(&twl->mutex); | ||
| 253 | return 0; | ||
| 254 | } | ||
| 255 | |||
| 256 | static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 257 | { | ||
| 258 | struct twl_pwm_chip *twl = container_of(chip, struct twl_pwm_chip, | ||
| 259 | chip); | ||
| 260 | int ret; | ||
| 261 | u8 val; | ||
| 262 | |||
| 263 | mutex_lock(&twl->mutex); | ||
| 264 | val = twl->twl6030_toggle3; | ||
| 265 | val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXR); | ||
| 266 | val &= ~TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); | ||
| 267 | |||
| 268 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); | ||
| 269 | if (ret < 0) { | ||
| 270 | dev_err(chip->dev, "%s: Failed to read TOGGLE3\n", pwm->label); | ||
| 271 | goto out; | ||
| 272 | } | ||
| 273 | |||
| 274 | val |= TWL6030_PWM_TOGGLE(pwm->hwpwm, TWL6030_PWMXS | TWL6030_PWMXEN); | ||
| 275 | |||
| 276 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, TWL6030_TOGGLE3_REG); | ||
| 277 | if (ret < 0) { | ||
| 278 | dev_err(chip->dev, "%s: Failed to disable PWM\n", pwm->label); | ||
| 279 | goto out; | ||
| 280 | } | ||
| 281 | |||
| 282 | twl->twl6030_toggle3 = val; | ||
| 283 | out: | ||
| 284 | mutex_unlock(&twl->mutex); | ||
| 285 | } | ||
| 286 | |||
| 287 | static const struct pwm_ops twl4030_pwm_ops = { | ||
| 288 | .config = twl_pwm_config, | ||
| 289 | .enable = twl4030_pwm_enable, | ||
| 290 | .disable = twl4030_pwm_disable, | ||
| 291 | .request = twl4030_pwm_request, | ||
| 292 | .free = twl4030_pwm_free, | ||
| 293 | }; | ||
| 294 | |||
| 295 | static const struct pwm_ops twl6030_pwm_ops = { | ||
| 296 | .config = twl_pwm_config, | ||
| 297 | .enable = twl6030_pwm_enable, | ||
| 298 | .disable = twl6030_pwm_disable, | ||
| 299 | }; | ||
| 300 | |||
| 301 | static int twl_pwm_probe(struct platform_device *pdev) | ||
| 302 | { | ||
| 303 | struct twl_pwm_chip *twl; | ||
| 304 | int ret; | ||
| 305 | |||
| 306 | twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL); | ||
| 307 | if (!twl) | ||
| 308 | return -ENOMEM; | ||
| 309 | |||
| 310 | if (twl_class_is_4030()) | ||
| 311 | twl->chip.ops = &twl4030_pwm_ops; | ||
| 312 | else | ||
| 313 | twl->chip.ops = &twl6030_pwm_ops; | ||
| 314 | |||
| 315 | twl->chip.dev = &pdev->dev; | ||
| 316 | twl->chip.base = -1; | ||
| 317 | twl->chip.npwm = 2; | ||
| 318 | |||
| 319 | mutex_init(&twl->mutex); | ||
| 320 | |||
| 321 | ret = pwmchip_add(&twl->chip); | ||
| 322 | if (ret < 0) | ||
| 323 | return ret; | ||
| 324 | |||
| 325 | platform_set_drvdata(pdev, twl); | ||
| 326 | |||
| 327 | return 0; | ||
| 328 | } | ||
| 329 | |||
| 330 | static int twl_pwm_remove(struct platform_device *pdev) | ||
| 331 | { | ||
| 332 | struct twl_pwm_chip *twl = platform_get_drvdata(pdev); | ||
| 333 | |||
| 334 | return pwmchip_remove(&twl->chip); | ||
| 335 | } | ||
| 336 | |||
| 337 | #ifdef CONFIG_OF | ||
| 338 | static struct of_device_id twl_pwm_of_match[] = { | ||
| 339 | { .compatible = "ti,twl4030-pwm" }, | ||
| 340 | { .compatible = "ti,twl6030-pwm" }, | ||
| 341 | { }, | ||
| 342 | }; | ||
| 343 | MODULE_DEVICE_TABLE(of, twl_pwm_of_match); | ||
| 344 | #endif | ||
| 345 | |||
| 346 | static struct platform_driver twl_pwm_driver = { | ||
| 347 | .driver = { | ||
| 348 | .name = "twl-pwm", | ||
| 349 | .of_match_table = of_match_ptr(twl_pwm_of_match), | ||
| 350 | }, | ||
| 351 | .probe = twl_pwm_probe, | ||
| 352 | .remove = twl_pwm_remove, | ||
| 353 | }; | ||
| 354 | module_platform_driver(twl_pwm_driver); | ||
| 355 | |||
| 356 | MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>"); | ||
| 357 | MODULE_DESCRIPTION("PWM driver for TWL4030 and TWL6030"); | ||
| 358 | MODULE_ALIAS("platform:twl-pwm"); | ||
| 359 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/pwm/pwm-twl6030.c b/drivers/pwm/pwm-twl6030.c deleted file mode 100644 index 378a7e286366..000000000000 --- a/drivers/pwm/pwm-twl6030.c +++ /dev/null | |||
| @@ -1,184 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * twl6030_pwm.c | ||
| 3 | * Driver for PHOENIX (TWL6030) Pulse Width Modulator | ||
| 4 | * | ||
| 5 | * Copyright (C) 2010 Texas Instruments | ||
| 6 | * Author: Hemanth V <hemanthv@ti.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify it | ||
| 9 | * under the terms of the GNU General Public License version 2 as published by | ||
| 10 | * the Free Software Foundation. | ||
| 11 | * | ||
| 12 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
| 13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 15 | * more details. | ||
| 16 | * | ||
| 17 | * You should have received a copy of the GNU General Public License along with | ||
| 18 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 19 | */ | ||
| 20 | |||
| 21 | #include <linux/module.h> | ||
| 22 | #include <linux/platform_device.h> | ||
| 23 | #include <linux/pwm.h> | ||
| 24 | #include <linux/i2c/twl.h> | ||
| 25 | #include <linux/slab.h> | ||
| 26 | |||
| 27 | #define LED_PWM_CTRL1 0xF4 | ||
| 28 | #define LED_PWM_CTRL2 0xF5 | ||
| 29 | |||
| 30 | /* Max value for CTRL1 register */ | ||
| 31 | #define PWM_CTRL1_MAX 255 | ||
| 32 | |||
| 33 | /* Pull down disable */ | ||
| 34 | #define PWM_CTRL2_DIS_PD (1 << 6) | ||
| 35 | |||
| 36 | /* Current control 2.5 milli Amps */ | ||
| 37 | #define PWM_CTRL2_CURR_02 (2 << 4) | ||
| 38 | |||
| 39 | /* LED supply source */ | ||
| 40 | #define PWM_CTRL2_SRC_VAC (1 << 2) | ||
| 41 | |||
| 42 | /* LED modes */ | ||
| 43 | #define PWM_CTRL2_MODE_HW (0 << 0) | ||
| 44 | #define PWM_CTRL2_MODE_SW (1 << 0) | ||
| 45 | #define PWM_CTRL2_MODE_DIS (2 << 0) | ||
| 46 | |||
| 47 | #define PWM_CTRL2_MODE_MASK 0x3 | ||
| 48 | |||
| 49 | struct twl6030_pwm_chip { | ||
| 50 | struct pwm_chip chip; | ||
| 51 | }; | ||
| 52 | |||
| 53 | static int twl6030_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 54 | { | ||
| 55 | int ret; | ||
| 56 | u8 val; | ||
| 57 | |||
| 58 | /* Configure PWM */ | ||
| 59 | val = PWM_CTRL2_DIS_PD | PWM_CTRL2_CURR_02 | PWM_CTRL2_SRC_VAC | | ||
| 60 | PWM_CTRL2_MODE_HW; | ||
| 61 | |||
| 62 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); | ||
| 63 | if (ret < 0) { | ||
| 64 | dev_err(chip->dev, "%s: Failed to configure PWM, Error %d\n", | ||
| 65 | pwm->label, ret); | ||
| 66 | return ret; | ||
| 67 | } | ||
| 68 | |||
| 69 | return 0; | ||
| 70 | } | ||
| 71 | |||
| 72 | static int twl6030_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | ||
| 73 | int duty_ns, int period_ns) | ||
| 74 | { | ||
| 75 | u8 duty_cycle = (duty_ns * PWM_CTRL1_MAX) / period_ns; | ||
| 76 | int ret; | ||
| 77 | |||
| 78 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, duty_cycle, LED_PWM_CTRL1); | ||
| 79 | if (ret < 0) { | ||
| 80 | pr_err("%s: Failed to configure PWM, Error %d\n", | ||
| 81 | pwm->label, ret); | ||
| 82 | return ret; | ||
| 83 | } | ||
| 84 | |||
| 85 | return 0; | ||
| 86 | } | ||
| 87 | |||
| 88 | static int twl6030_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 89 | { | ||
| 90 | int ret; | ||
| 91 | u8 val; | ||
| 92 | |||
| 93 | ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); | ||
| 94 | if (ret < 0) { | ||
| 95 | dev_err(chip->dev, "%s: Failed to enable PWM, Error %d\n", | ||
| 96 | pwm->label, ret); | ||
| 97 | return ret; | ||
| 98 | } | ||
| 99 | |||
| 100 | /* Change mode to software control */ | ||
| 101 | val &= ~PWM_CTRL2_MODE_MASK; | ||
| 102 | val |= PWM_CTRL2_MODE_SW; | ||
| 103 | |||
| 104 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); | ||
| 105 | if (ret < 0) { | ||
| 106 | dev_err(chip->dev, "%s: Failed to enable PWM, Error %d\n", | ||
| 107 | pwm->label, ret); | ||
| 108 | return ret; | ||
| 109 | } | ||
| 110 | |||
| 111 | twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); | ||
| 112 | return 0; | ||
| 113 | } | ||
| 114 | |||
| 115 | static void twl6030_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | ||
| 116 | { | ||
| 117 | int ret; | ||
| 118 | u8 val; | ||
| 119 | |||
| 120 | ret = twl_i2c_read_u8(TWL6030_MODULE_ID1, &val, LED_PWM_CTRL2); | ||
| 121 | if (ret < 0) { | ||
| 122 | dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n", | ||
| 123 | pwm->label, ret); | ||
| 124 | return; | ||
| 125 | } | ||
| 126 | |||
| 127 | val &= ~PWM_CTRL2_MODE_MASK; | ||
| 128 | val |= PWM_CTRL2_MODE_HW; | ||
| 129 | |||
| 130 | ret = twl_i2c_write_u8(TWL6030_MODULE_ID1, val, LED_PWM_CTRL2); | ||
| 131 | if (ret < 0) { | ||
| 132 | dev_err(chip->dev, "%s: Failed to disable PWM, Error %d\n", | ||
| 133 | pwm->label, ret); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | |||
| 137 | static const struct pwm_ops twl6030_pwm_ops = { | ||
| 138 | .request = twl6030_pwm_request, | ||
| 139 | .config = twl6030_pwm_config, | ||
| 140 | .enable = twl6030_pwm_enable, | ||
| 141 | .disable = twl6030_pwm_disable, | ||
| 142 | }; | ||
| 143 | |||
| 144 | static int twl6030_pwm_probe(struct platform_device *pdev) | ||
| 145 | { | ||
| 146 | struct twl6030_pwm_chip *twl6030; | ||
| 147 | int ret; | ||
| 148 | |||
| 149 | twl6030 = devm_kzalloc(&pdev->dev, sizeof(*twl6030), GFP_KERNEL); | ||
| 150 | if (!twl6030) | ||
| 151 | return -ENOMEM; | ||
| 152 | |||
| 153 | twl6030->chip.dev = &pdev->dev; | ||
| 154 | twl6030->chip.ops = &twl6030_pwm_ops; | ||
| 155 | twl6030->chip.base = -1; | ||
| 156 | twl6030->chip.npwm = 1; | ||
| 157 | |||
| 158 | ret = pwmchip_add(&twl6030->chip); | ||
| 159 | if (ret < 0) | ||
| 160 | return ret; | ||
| 161 | |||
| 162 | platform_set_drvdata(pdev, twl6030); | ||
| 163 | |||
| 164 | return 0; | ||
| 165 | } | ||
| 166 | |||
| 167 | static int twl6030_pwm_remove(struct platform_device *pdev) | ||
| 168 | { | ||
| 169 | struct twl6030_pwm_chip *twl6030 = platform_get_drvdata(pdev); | ||
| 170 | |||
| 171 | return pwmchip_remove(&twl6030->chip); | ||
| 172 | } | ||
| 173 | |||
| 174 | static struct platform_driver twl6030_pwm_driver = { | ||
| 175 | .driver = { | ||
| 176 | .name = "twl6030-pwm", | ||
| 177 | }, | ||
| 178 | .probe = twl6030_pwm_probe, | ||
| 179 | .remove = twl6030_pwm_remove, | ||
| 180 | }; | ||
| 181 | module_platform_driver(twl6030_pwm_driver); | ||
| 182 | |||
| 183 | MODULE_ALIAS("platform:twl6030-pwm"); | ||
| 184 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c index ad14389b7144..b0ba2d403439 100644 --- a/drivers/pwm/pwm-vt8500.c +++ b/drivers/pwm/pwm-vt8500.c | |||
| @@ -1,7 +1,8 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * drivers/pwm/pwm-vt8500.c | 2 | * drivers/pwm/pwm-vt8500.c |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> | 4 | * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz> |
| 5 | * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> | ||
| 5 | * | 6 | * |
| 6 | * This software is licensed under the terms of the GNU General Public | 7 | * This software is licensed under the terms of the GNU General Public |
| 7 | * License version 2, as published by the Free Software Foundation, and | 8 | * License version 2, as published by the Free Software Foundation, and |
| @@ -21,14 +22,24 @@ | |||
| 21 | #include <linux/io.h> | 22 | #include <linux/io.h> |
| 22 | #include <linux/pwm.h> | 23 | #include <linux/pwm.h> |
| 23 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
| 25 | #include <linux/clk.h> | ||
| 24 | 26 | ||
| 25 | #include <asm/div64.h> | 27 | #include <asm/div64.h> |
| 26 | 28 | ||
| 27 | #define VT8500_NR_PWMS 4 | 29 | #include <linux/of.h> |
| 30 | #include <linux/of_device.h> | ||
| 31 | #include <linux/of_address.h> | ||
| 32 | |||
| 33 | /* | ||
| 34 | * SoC architecture allocates register space for 4 PWMs but only | ||
| 35 | * 2 are currently implemented. | ||
| 36 | */ | ||
| 37 | #define VT8500_NR_PWMS 2 | ||
| 28 | 38 | ||
| 29 | struct vt8500_chip { | 39 | struct vt8500_chip { |
| 30 | struct pwm_chip chip; | 40 | struct pwm_chip chip; |
| 31 | void __iomem *base; | 41 | void __iomem *base; |
| 42 | struct clk *clk; | ||
| 32 | }; | 43 | }; |
| 33 | 44 | ||
| 34 | #define to_vt8500_chip(chip) container_of(chip, struct vt8500_chip, chip) | 45 | #define to_vt8500_chip(chip) container_of(chip, struct vt8500_chip, chip) |
| @@ -51,8 +62,15 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 51 | struct vt8500_chip *vt8500 = to_vt8500_chip(chip); | 62 | struct vt8500_chip *vt8500 = to_vt8500_chip(chip); |
| 52 | unsigned long long c; | 63 | unsigned long long c; |
| 53 | unsigned long period_cycles, prescale, pv, dc; | 64 | unsigned long period_cycles, prescale, pv, dc; |
| 65 | int err; | ||
| 54 | 66 | ||
| 55 | c = 25000000/2; /* wild guess --- need to implement clocks */ | 67 | err = clk_enable(vt8500->clk); |
| 68 | if (err < 0) { | ||
| 69 | dev_err(chip->dev, "failed to enable clock\n"); | ||
| 70 | return err; | ||
| 71 | } | ||
| 72 | |||
| 73 | c = clk_get_rate(vt8500->clk); | ||
| 56 | c = c * period_ns; | 74 | c = c * period_ns; |
| 57 | do_div(c, 1000000000); | 75 | do_div(c, 1000000000); |
| 58 | period_cycles = c; | 76 | period_cycles = c; |
| @@ -64,8 +82,10 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 64 | if (pv > 4095) | 82 | if (pv > 4095) |
| 65 | pv = 4095; | 83 | pv = 4095; |
| 66 | 84 | ||
| 67 | if (prescale > 1023) | 85 | if (prescale > 1023) { |
| 86 | clk_disable(vt8500->clk); | ||
| 68 | return -EINVAL; | 87 | return -EINVAL; |
| 88 | } | ||
| 69 | 89 | ||
| 70 | c = (unsigned long long)pv * duty_ns; | 90 | c = (unsigned long long)pv * duty_ns; |
| 71 | do_div(c, period_ns); | 91 | do_div(c, period_ns); |
| @@ -80,13 +100,21 @@ static int vt8500_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, | |||
| 80 | pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 3)); | 100 | pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 3)); |
| 81 | writel(dc, vt8500->base + 0xc + (pwm->hwpwm << 4)); | 101 | writel(dc, vt8500->base + 0xc + (pwm->hwpwm << 4)); |
| 82 | 102 | ||
| 103 | clk_disable(vt8500->clk); | ||
| 83 | return 0; | 104 | return 0; |
| 84 | } | 105 | } |
| 85 | 106 | ||
| 86 | static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) | 107 | static int vt8500_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) |
| 87 | { | 108 | { |
| 109 | int err; | ||
| 88 | struct vt8500_chip *vt8500 = to_vt8500_chip(chip); | 110 | struct vt8500_chip *vt8500 = to_vt8500_chip(chip); |
| 89 | 111 | ||
| 112 | err = clk_enable(vt8500->clk); | ||
| 113 | if (err < 0) { | ||
| 114 | dev_err(chip->dev, "failed to enable clock\n"); | ||
| 115 | return err; | ||
| 116 | } | ||
| 117 | |||
| 90 | pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); | 118 | pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); |
| 91 | writel(5, vt8500->base + (pwm->hwpwm << 4)); | 119 | writel(5, vt8500->base + (pwm->hwpwm << 4)); |
| 92 | return 0; | 120 | return 0; |
| @@ -98,6 +126,8 @@ static void vt8500_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) | |||
| 98 | 126 | ||
| 99 | pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); | 127 | pwm_busy_wait(vt8500->base + 0x40 + pwm->hwpwm, (1 << 0)); |
| 100 | writel(0, vt8500->base + (pwm->hwpwm << 4)); | 128 | writel(0, vt8500->base + (pwm->hwpwm << 4)); |
| 129 | |||
| 130 | clk_disable(vt8500->clk); | ||
| 101 | } | 131 | } |
| 102 | 132 | ||
| 103 | static struct pwm_ops vt8500_pwm_ops = { | 133 | static struct pwm_ops vt8500_pwm_ops = { |
| @@ -107,12 +137,24 @@ static struct pwm_ops vt8500_pwm_ops = { | |||
| 107 | .owner = THIS_MODULE, | 137 | .owner = THIS_MODULE, |
| 108 | }; | 138 | }; |
| 109 | 139 | ||
| 110 | static int __devinit pwm_probe(struct platform_device *pdev) | 140 | static const struct of_device_id vt8500_pwm_dt_ids[] = { |
| 141 | { .compatible = "via,vt8500-pwm", }, | ||
| 142 | { /* Sentinel */ } | ||
| 143 | }; | ||
| 144 | MODULE_DEVICE_TABLE(of, vt8500_pwm_dt_ids); | ||
| 145 | |||
| 146 | static int vt8500_pwm_probe(struct platform_device *pdev) | ||
| 111 | { | 147 | { |
| 112 | struct vt8500_chip *chip; | 148 | struct vt8500_chip *chip; |
| 113 | struct resource *r; | 149 | struct resource *r; |
| 150 | struct device_node *np = pdev->dev.of_node; | ||
| 114 | int ret; | 151 | int ret; |
| 115 | 152 | ||
| 153 | if (!np) { | ||
| 154 | dev_err(&pdev->dev, "invalid devicetree node\n"); | ||
| 155 | return -EINVAL; | ||
| 156 | } | ||
| 157 | |||
| 116 | chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); | 158 | chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); |
| 117 | if (chip == NULL) { | 159 | if (chip == NULL) { |
| 118 | dev_err(&pdev->dev, "failed to allocate memory\n"); | 160 | dev_err(&pdev->dev, "failed to allocate memory\n"); |
| @@ -124,6 +166,12 @@ static int __devinit pwm_probe(struct platform_device *pdev) | |||
| 124 | chip->chip.base = -1; | 166 | chip->chip.base = -1; |
| 125 | chip->chip.npwm = VT8500_NR_PWMS; | 167 | chip->chip.npwm = VT8500_NR_PWMS; |
| 126 | 168 | ||
| 169 | chip->clk = devm_clk_get(&pdev->dev, NULL); | ||
| 170 | if (IS_ERR(chip->clk)) { | ||
| 171 | dev_err(&pdev->dev, "clock source not specified\n"); | ||
| 172 | return PTR_ERR(chip->clk); | ||
| 173 | } | ||
| 174 | |||
| 127 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 175 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 128 | if (r == NULL) { | 176 | if (r == NULL) { |
| 129 | dev_err(&pdev->dev, "no memory resource defined\n"); | 177 | dev_err(&pdev->dev, "no memory resource defined\n"); |
| @@ -131,18 +179,26 @@ static int __devinit pwm_probe(struct platform_device *pdev) | |||
| 131 | } | 179 | } |
| 132 | 180 | ||
| 133 | chip->base = devm_request_and_ioremap(&pdev->dev, r); | 181 | chip->base = devm_request_and_ioremap(&pdev->dev, r); |
| 134 | if (chip->base == NULL) | 182 | if (!chip->base) |
| 135 | return -EADDRNOTAVAIL; | 183 | return -EADDRNOTAVAIL; |
| 136 | 184 | ||
| 185 | ret = clk_prepare(chip->clk); | ||
| 186 | if (ret < 0) { | ||
| 187 | dev_err(&pdev->dev, "failed to prepare clock\n"); | ||
| 188 | return ret; | ||
| 189 | } | ||
| 190 | |||
| 137 | ret = pwmchip_add(&chip->chip); | 191 | ret = pwmchip_add(&chip->chip); |
| 138 | if (ret < 0) | 192 | if (ret < 0) { |
| 193 | dev_err(&pdev->dev, "failed to add PWM chip\n"); | ||
| 139 | return ret; | 194 | return ret; |
| 195 | } | ||
| 140 | 196 | ||
| 141 | platform_set_drvdata(pdev, chip); | 197 | platform_set_drvdata(pdev, chip); |
| 142 | return ret; | 198 | return ret; |
| 143 | } | 199 | } |
| 144 | 200 | ||
| 145 | static int __devexit pwm_remove(struct platform_device *pdev) | 201 | static int vt8500_pwm_remove(struct platform_device *pdev) |
| 146 | { | 202 | { |
| 147 | struct vt8500_chip *chip; | 203 | struct vt8500_chip *chip; |
| 148 | 204 | ||
| @@ -150,28 +206,22 @@ static int __devexit pwm_remove(struct platform_device *pdev) | |||
| 150 | if (chip == NULL) | 206 | if (chip == NULL) |
| 151 | return -ENODEV; | 207 | return -ENODEV; |
| 152 | 208 | ||
| 209 | clk_unprepare(chip->clk); | ||
| 210 | |||
| 153 | return pwmchip_remove(&chip->chip); | 211 | return pwmchip_remove(&chip->chip); |
| 154 | } | 212 | } |
| 155 | 213 | ||
| 156 | static struct platform_driver pwm_driver = { | 214 | static struct platform_driver vt8500_pwm_driver = { |
| 215 | .probe = vt8500_pwm_probe, | ||
| 216 | .remove = vt8500_pwm_remove, | ||
| 157 | .driver = { | 217 | .driver = { |
| 158 | .name = "vt8500-pwm", | 218 | .name = "vt8500-pwm", |
| 159 | .owner = THIS_MODULE, | 219 | .owner = THIS_MODULE, |
| 220 | .of_match_table = vt8500_pwm_dt_ids, | ||
| 160 | }, | 221 | }, |
| 161 | .probe = pwm_probe, | ||
| 162 | .remove = __devexit_p(pwm_remove), | ||
| 163 | }; | 222 | }; |
| 223 | module_platform_driver(vt8500_pwm_driver); | ||
| 164 | 224 | ||
| 165 | static int __init pwm_init(void) | 225 | MODULE_DESCRIPTION("VT8500 PWM Driver"); |
| 166 | { | 226 | MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>"); |
| 167 | return platform_driver_register(&pwm_driver); | 227 | MODULE_LICENSE("GPL v2"); |
| 168 | } | ||
| 169 | arch_initcall(pwm_init); | ||
| 170 | |||
| 171 | static void __exit pwm_exit(void) | ||
| 172 | { | ||
| 173 | platform_driver_unregister(&pwm_driver); | ||
| 174 | } | ||
| 175 | module_exit(pwm_exit); | ||
| 176 | |||
| 177 | MODULE_LICENSE("GPL"); | ||
diff --git a/include/linux/pwm.h b/include/linux/pwm.h index 112b31436848..6d661f32e0e4 100644 --- a/include/linux/pwm.h +++ b/include/linux/pwm.h | |||
| @@ -171,6 +171,9 @@ struct pwm_device *pwm_request_from_chip(struct pwm_chip *chip, | |||
| 171 | unsigned int index, | 171 | unsigned int index, |
| 172 | const char *label); | 172 | const char *label); |
| 173 | 173 | ||
| 174 | struct pwm_device *of_pwm_xlate_with_flags(struct pwm_chip *pc, | ||
| 175 | const struct of_phandle_args *args); | ||
| 176 | |||
| 174 | struct pwm_device *pwm_get(struct device *dev, const char *consumer); | 177 | struct pwm_device *pwm_get(struct device *dev, const char *consumer); |
| 175 | void pwm_put(struct pwm_device *pwm); | 178 | void pwm_put(struct pwm_device *pwm); |
| 176 | 179 | ||
