build/patch/kernel/sun7i-default/0015-rewrite-function-bestprescale.patch
Igor Pecovnik 51e014f772 Added sunxi default kernel related patches:
- ax88179 usb3 driver
- enabled zram / zcache
- enabled IR autorepeat
- enabled RC core driver
- spdif fixes
2016-04-25 14:57:19 +02:00

1663 lines
53 KiB
Diff

From 2920d2cfb3fed734404e4ace8d43c95846c06e77 Mon Sep 17 00:00:00 2001
From: Damien Pageot <damien.pageot78@gmail.com>
Date: Thu, 31 Mar 2016 16:38:56 +0200
Subject: [PATCH 15/19] rewrite function bestprescale
recode best prescale function and add non prescale
---
drivers/misc/pwm-sunxi.c | 1564 +++++++++++++++++++++++-----------------------
1 file changed, 791 insertions(+), 773 deletions(-)
diff --git a/drivers/misc/pwm-sunxi.c b/drivers/misc/pwm-sunxi.c
index 310798e..6f1e498 100644
--- a/drivers/misc/pwm-sunxi.c
+++ b/drivers/misc/pwm-sunxi.c
@@ -1,10 +1,10 @@
-/* pwm-sunxi.c
- *
- * pwm module for sun4i (and others) like cubieboard and pcduino
- *
- * (C) Copyright 2013
- * David H. Wilkins <dwil...@conecuh.com>
- *
+/* pwm-sunxi.c
+ *
+ * pwm module for sun4i (and others) like cubieboard and pcduino
+ *
+ * (C) Copyright 2013
+ * David H. Wilkins <dwil...@conecuh.com>
+ *
* CHANGELOG:
* 10.08.2013 - Stefan Voit <stefan.voit@voit-consulting.com>
* - Added script.bin support for [pwm0_para] and [pwm1_para]: pwm_used, pwm_period, pwm_duty_percent
@@ -16,130 +16,130 @@
* - Implement duty_percent=0 to set pwm line to 0 - right now it goes to 100%
* - Change the script_bin settings loader for pwm_period to allow text based values (100ms, 10hz,...)
* - Merge h & c file
- * -
+ * -
*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
- */
-
-#include <linux/kobject.h>
-#include <linux/slab.h>
-#include <linux/device.h>
-#include <linux/sysfs.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/err.h>
-#include <asm/io.h>
-#include <asm/delay.h>
-#include <mach/platform.h>
-#include <linux/pwm.h>
-#include <linux/ctype.h>
-#include <linux/limits.h>
-#include <linux/pwm.h>
-#include <linux/kdev_t.h>
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/kobject.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <asm/io.h>
+#include <asm/delay.h>
+#include <mach/platform.h>
+#include <linux/pwm.h>
+#include <linux/ctype.h>
+#include <linux/limits.h>
+#include <linux/pwm.h>
+#include <linux/kdev_t.h>
#include <plat/system.h>
#include <plat/sys_config.h>
-#include "pwm-sunxi.h"
-/*
- * Forward Declarations
- */
+#include "pwm-sunxi.h"
+/*
+ * Forward Declarations
+ */
#define SUNXI_PWM_DEBUG
//comment to get debug messages
#undef SUNXI_PWM_DEBUG
-
-void release_pwm_sunxi(struct kobject *kobj);
-void pwm_setup_available_channels(void );
-ssize_t pwm_set_mode(unsigned int enable, struct sun4i_pwm_available_channel *chan);
-enum sun4i_pwm_prescale pwm_get_best_prescale(unsigned long long period);
-unsigned int get_entire_cycles(struct sun4i_pwm_available_channel *chan);
-unsigned int get_active_cycles(struct sun4i_pwm_available_channel *chan);
-unsigned long convert_string_to_microseconds(const char *buf);
-int pwm_set_period_and_duty(struct sun4i_pwm_available_channel *chan);
-void fixup_duty(struct sun4i_pwm_available_channel *chan);
-
-
-static DEFINE_MUTEX(sysfs_lock);
-static struct class pwm_class;
-
-void *PWM_CTRL_REG_BASE = NULL;
-
-
-static struct class_attribute pwm_class_attrs[] = {
- __ATTR_NULL
-};
-
-
-static struct class pwm_class = {
- .name = "pwm-sunxi",
- .owner = THIS_MODULE,
- .class_attrs = pwm_class_attrs,
-};
-
-
-/*
- * sysfs store / show functions
- */
-
-static ssize_t pwm_polarity_show(struct device *dev, struct device_attribute *attr, char *buf);
-static ssize_t pwm_period_show(struct device *dev, struct device_attribute *attr, char *buf);
-static ssize_t pwm_duty_show(struct device *dev, struct device_attribute *attr, char *buf);
-static ssize_t pwm_run_show(struct device *dev,struct device_attribute *attr, char *buf);
-static ssize_t pwm_duty_percent_show(struct device *dev,struct device_attribute *attr, char *buf);
-static ssize_t pwm_pulse_show(struct device *dev,struct device_attribute *attr, char *buf);
-static ssize_t pwm_pin_show(struct device *dev,struct device_attribute *attr, char *buf);
-
-static ssize_t pwm_polarity_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size);
-static ssize_t pwm_period_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size);
-static ssize_t pwm_duty_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size);
-static ssize_t pwm_run_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size);
-static ssize_t pwm_duty_percent_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size);
-static ssize_t pwm_pulse_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size);
-
-static DEVICE_ATTR(polarity, 0644,pwm_polarity_show, pwm_polarity_store);
-static DEVICE_ATTR(period, 0644, pwm_period_show, pwm_period_store);
-static DEVICE_ATTR(duty, 0644,pwm_duty_show, pwm_duty_store);
-static DEVICE_ATTR(run, 0644, pwm_run_show, pwm_run_store);
-static DEVICE_ATTR(duty_percent, 0644, pwm_duty_percent_show, pwm_duty_percent_store);
-static DEVICE_ATTR(pulse, 0644, pwm_pulse_show, pwm_pulse_store);
-static DEVICE_ATTR(pin, 0644, pwm_pin_show, NULL);
-
-static const struct attribute *pwm_attrs[] = {
- &dev_attr_polarity.attr,
- &dev_attr_period.attr,
- &dev_attr_duty.attr,
- &dev_attr_run.attr,
- &dev_attr_duty_percent.attr,
- &dev_attr_pulse.attr,
- &dev_attr_pin.attr,
- NULL,
-};
-
-static const struct attribute_group pwm_attr_group = {
- .attrs = (struct attribute **) pwm_attrs
-};
-
-struct device *pwm0;
-struct device *pwm1;
-
-
-static struct sun4i_pwm_available_channel pwm_available_chan[SUN4I_MAX_HARDWARE_PWM_CHANNELS];
+
+void release_pwm_sunxi(struct kobject *kobj);
+void pwm_setup_available_channels(void );
+ssize_t pwm_set_mode(unsigned int enable, struct sun4i_pwm_available_channel *chan);
+enum sun4i_pwm_prescale pwm_get_best_prescale(unsigned long long period);
+unsigned int get_entire_cycles(struct sun4i_pwm_available_channel *chan);
+unsigned int get_active_cycles(struct sun4i_pwm_available_channel *chan);
+unsigned long convert_string_to_microseconds(const char *buf);
+int pwm_set_period_and_duty(struct sun4i_pwm_available_channel *chan);
+void fixup_duty(struct sun4i_pwm_available_channel *chan);
+
+
+static DEFINE_MUTEX(sysfs_lock);
+static struct class pwm_class;
+
+void *PWM_CTRL_REG_BASE = NULL;
+
+
+static struct class_attribute pwm_class_attrs[] = {
+ __ATTR_NULL
+};
+
+
+static struct class pwm_class = {
+ .name = "pwm-sunxi",
+ .owner = THIS_MODULE,
+ .class_attrs = pwm_class_attrs,
+};
+
+
+/*
+ * sysfs store / show functions
+ */
+
+static ssize_t pwm_polarity_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t pwm_period_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t pwm_duty_show(struct device *dev, struct device_attribute *attr, char *buf);
+static ssize_t pwm_run_show(struct device *dev,struct device_attribute *attr, char *buf);
+static ssize_t pwm_duty_percent_show(struct device *dev,struct device_attribute *attr, char *buf);
+static ssize_t pwm_pulse_show(struct device *dev,struct device_attribute *attr, char *buf);
+static ssize_t pwm_pin_show(struct device *dev,struct device_attribute *attr, char *buf);
+
+static ssize_t pwm_polarity_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size);
+static ssize_t pwm_period_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size);
+static ssize_t pwm_duty_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size);
+static ssize_t pwm_run_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size);
+static ssize_t pwm_duty_percent_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size);
+static ssize_t pwm_pulse_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size);
+
+static DEVICE_ATTR(polarity, 0644,pwm_polarity_show, pwm_polarity_store);
+static DEVICE_ATTR(period, 0644, pwm_period_show, pwm_period_store);
+static DEVICE_ATTR(duty, 0644,pwm_duty_show, pwm_duty_store);
+static DEVICE_ATTR(run, 0644, pwm_run_show, pwm_run_store);
+static DEVICE_ATTR(duty_percent, 0644, pwm_duty_percent_show, pwm_duty_percent_store);
+static DEVICE_ATTR(pulse, 0644, pwm_pulse_show, pwm_pulse_store);
+static DEVICE_ATTR(pin, 0644, pwm_pin_show, NULL);
+
+static const struct attribute *pwm_attrs[] = {
+ &dev_attr_polarity.attr,
+ &dev_attr_period.attr,
+ &dev_attr_duty.attr,
+ &dev_attr_run.attr,
+ &dev_attr_duty_percent.attr,
+ &dev_attr_pulse.attr,
+ &dev_attr_pin.attr,
+ NULL,
+};
+
+static const struct attribute_group pwm_attr_group = {
+ .attrs = (struct attribute **) pwm_attrs
+};
+
+struct device *pwm0;
+struct device *pwm1;
+
+
+static struct sun4i_pwm_available_channel pwm_available_chan[SUN4I_MAX_HARDWARE_PWM_CHANNELS];
static int sunxi_pwm_class_registered = 0;
static void __init sunxi_pwm_register_class(void)
@@ -154,18 +154,18 @@ static void __init sunxi_pwm_register_class(void)
sunxi_pwm_class_registered = 1;
}
-static int __init sunxi_pwm_init(void)
-{
+static int __init sunxi_pwm_init(void)
+{
int init_enable, init_duty_percent, init_period;
struct sun4i_pwm_available_channel *chan;
int err = 0;
- pwm_setup_available_channels();
+ pwm_setup_available_channels();
//PWM 0
//printk("pwm-sunxi: configuring pwm0...\n");
chan = &pwm_available_chan[0];
-
+
init_enable=0;
init_period=0;
init_duty_percent=100;
@@ -193,13 +193,13 @@ static int __init sunxi_pwm_init(void)
}
chan->duty_percent=init_duty_percent;
- chan->period = init_period;
- chan->prescale = pwm_get_best_prescale(init_period);
- fixup_duty(chan);
+ chan->period = init_period;
+ chan->prescale = pwm_get_best_prescale(init_period);
+ fixup_duty(chan);
#ifdef SUNXI_PWM_DEBUG
printk("pwm-sunxi: pwm0 set initial values\n");
#endif
- pwm_set_mode(init_enable,chan);
+ pwm_set_mode(init_enable,chan);
printk("pwm-sunxi: pwm0 configured - period: %ld, duty_percent: %d, duty: %ld\n",
chan->period, chan->duty_percent, chan->duty);
}
@@ -210,7 +210,7 @@ static int __init sunxi_pwm_init(void)
//PWM 1
//printk("pwm-sunxi: configuring pwm1...\n");
chan = &pwm_available_chan[1];
-
+
init_enable=0;
init_period=0;
init_duty_percent=100;
@@ -238,23 +238,23 @@ static int __init sunxi_pwm_init(void)
}
chan->duty_percent=init_duty_percent;
- chan->period = init_period;
- chan->prescale = pwm_get_best_prescale(init_period);
- fixup_duty(chan);
+ chan->period = init_period;
+ chan->prescale = pwm_get_best_prescale(init_period);
+ fixup_duty(chan);
#ifdef SUNXI_PWM_DEBUG
printk("pwm-sunxi: pwm0 set initial values\n");
#endif
- pwm_set_mode(init_enable,chan);
+ pwm_set_mode(init_enable,chan);
printk("pwm-sunxi: pwm1 configured - period: %ld, duty_percent: %d, duty: %ld\n",
chan->period, chan->duty_percent, chan->duty);
}
return 0;
-}
+}
-void sunxi_pwm_exit(void)
-{
- void *timer_base = ioremap(SW_PA_TIMERC_IO_BASE, 0x400);
- void *PWM_CTRL_REG_BASE = timer_base + 0x200;
+void sunxi_pwm_exit(void)
+{
+ void *timer_base = ioremap(SW_PA_TIMERC_IO_BASE, 0x400);
+ void *PWM_CTRL_REG_BASE = timer_base + 0x200;
if (pwm0) {
device_destroy(&pwm_class, pwm0->devt);
@@ -268,647 +268,665 @@ void sunxi_pwm_exit(void)
}
if (sunxi_pwm_class_registered) {
- writel(0, PWM_CTRL_REG_BASE + 0);
+ writel(0, PWM_CTRL_REG_BASE + 0);
class_unregister(&pwm_class);
}
}
-/*
- * Functions to display the pwm variables currently set
- */
-
-static ssize_t pwm_polarity_show(struct device *dev, struct device_attribute *attr, char *buf) {
- const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
- ssize_t status;
- switch (chan->channel) {
- case 0:
- status = scnprintf(buf,PAGE_SIZE,"%d",chan->ctrl_current.s.ch0_act_state);
- break;
- case 1:
- status = scnprintf(buf,PAGE_SIZE,"%d",chan->ctrl_current.s.ch1_act_state);
- break;
- default:
- status = -EINVAL;
- break;
- }
- return status;
-}
-static ssize_t pwm_period_show(struct device *dev, struct device_attribute *attr, char *buf) {
- const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
- ssize_t status;
- status = sprintf(buf,"%lu",chan->period);
- return status;
-}
-static ssize_t pwm_duty_show(struct device *dev, struct device_attribute *attr, char *buf) {
- const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
- ssize_t status;
- status = sprintf(buf,"%lu",chan->duty);
- return status;
-}
-static ssize_t pwm_run_show(struct device *dev,struct device_attribute *attr, char *buf) {
- const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
- ssize_t status;
- switch (chan->channel) {
- case 0:
- status = sprintf(buf,"%d",chan->ctrl_current.s.ch0_en);
- break;
- case 1:
- status = sprintf(buf,"%d",chan->ctrl_current.s.ch1_en);
- break;
- default:
- status = -EINVAL;
- break;
- }
-
- return status;
-}
-static ssize_t pwm_duty_percent_show(struct device *dev,struct device_attribute *attr, char *buf) {
- const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
- return sprintf(buf,"%u",chan->duty_percent);
-}
-static ssize_t pwm_pulse_show(struct device *dev,struct device_attribute *attr, char *buf) {
- const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
- ssize_t status;
- switch (chan->channel) {
- case 0:
- status = sprintf(buf,"%d",chan->ctrl_current.s.ch0_pulse_start);
- break;
- case 1:
- status = sprintf(buf,"%d",chan->ctrl_current.s.ch1_pulse_start);
- break;
- default:
- status = -EINVAL;
- break;
- }
- return status;
-}
-
-static ssize_t pwm_pin_show(struct device *dev,struct device_attribute *attr, char *buf) {
- const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
- ssize_t status;
- status = sprintf(buf,"%s",chan->pin_name);
-
- return status;
-}
-
-/*
- * Functions to store values for pwm
- */
-
-static ssize_t pwm_polarity_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) {
- struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
- ssize_t status = -EINVAL;
- int act_state = 0;
-
- sscanf(buf,"%d",&act_state);
- if(act_state < 2) {
- switch (chan->channel) {
- case 0:
- chan->ctrl_current.s.ch0_act_state = act_state;
- break;
- case 1:
- chan->ctrl_current.s.ch1_act_state = act_state;
- break;
- default:
- status = -EINVAL;
- break;
- }
- status = size;
- }
- return status;
-}
-static ssize_t pwm_period_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) {
- unsigned long long period = 0;
- struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
-
- period = convert_string_to_microseconds(buf);
- if(!period || period > ULONG_MAX) {
- size = -EINVAL;
- } else {
- if(period <= chan->duty) {
- chan->duty = period;
- }
- chan->period = period;
- chan->prescale = pwm_get_best_prescale(period);
- fixup_duty(chan);
- if(chan->duty) {
- pwm_set_mode(NO_ENABLE_CHANGE,chan);
- }
- }
- return size;
-}
-static ssize_t pwm_duty_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) {
- unsigned long long duty = 0;
- struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
-
- /* sscanf(buf,"%Lu",&duty); */ /* L means long long pointer */
- duty = convert_string_to_microseconds(buf);
- duty = duty > ULONG_MAX ? ULONG_MAX : duty;
- duty = duty > chan->period ? chan->period : duty;
- chan->duty_percent = -1; /* disable duty_percent if duty is set by hand */
- chan->duty = duty;
- pwm_set_mode(NO_ENABLE_CHANGE,chan);
- return size;
-}
-
-struct time_suffix suffixes[] = {
- [0] = { .suffix = "hz", .multiplier = 1, .freq = true }, /* f = 1/T */
- [1] = { .suffix = "khz", .multiplier = 1000, .freq = true },
- [2] = { .suffix = "mhz", .multiplier = 1000000, .freq = true },
- [3] = { .suffix = "ghz", .multiplier = 1000000000, .freq = true },
- [4] = { .suffix = "ms", .multiplier = 1000, .freq = false }, /* T = 1/f */
- [5] = { .suffix = "us", .multiplier = 1, .freq = false },
- [6] = { .suffix = "ns", .multiplier = 1, .freq = false },
- [7] = { .suffix = NULL, .multiplier = 0, .freq = false },
-};
-
-
-unsigned long convert_string_to_microseconds(const char *buf) {
- unsigned char ch = 0;
- char numbers[10];
- char letters[4];
- const char *bufptr = buf;
- int i = 0;
- unsigned long microseconds = 0;
- unsigned long numeric_part = 0;
- int found_suffix = -1;
- int numbers_index = 0, letters_index = 0;
- while(bufptr && *bufptr && (ch = *bufptr) && isdigit(ch) && numbers_index < (sizeof(numbers)-1)) {
- numbers[numbers_index++] = *bufptr++;
- }
- numbers[numbers_index] = 0;
- while(bufptr && *bufptr && (ch = *bufptr) && strchr("usmhznhzkg",tolower(ch)) && letters_index < (sizeof(letters)-1)) {
- letters[letters_index++] = tolower(*bufptr);
- bufptr++;
- }
- letters[letters_index] = 0;
- sscanf(numbers,"%lu",&numeric_part);
- while(suffixes[i].suffix) {
- if(!strcmp(suffixes[i].suffix,letters)) {
- found_suffix = i;
- break;
- }
- i++;
- }
- if(found_suffix > -1) {
- if(suffixes[found_suffix].freq) {
- microseconds = 1000000 / (numeric_part * suffixes[found_suffix].multiplier);
- } else {
- microseconds = suffixes[found_suffix].multiplier * numeric_part;
- }
- }
- return microseconds;
-}
-
-
-
-
-static const unsigned int prescale_divisor[13] = {120,
- 180,
- 240,
- 360,
- 480,
- 480, /* Invalid Option */
- 480, /* Invalid Option */
- 480, /* Invalid Option */
- 12000,
- 24000,
- 36000,
- 48000,
- 72000};
-
-/*
- * Find the best prescale value for the period
- * We want to get the highest period cycle count possible, so we look
- * make a run through the prescale values looking for numbers over
- * min_optimal_period_cycles. If none are found then root though again
- * taking anything that works
- */
-enum sun4i_pwm_prescale pwm_get_best_prescale(unsigned long long period_in) {
- int i;
- unsigned long period = period_in;
- const unsigned long min_optimal_period_cycles = MAX_CYCLES / 2;
- const unsigned long min_period_cycles = 0x02;
- enum sun4i_pwm_prescale best_prescale = 0;
-
- best_prescale = -1;
- for(i = 0 ; i < 13 ; i++) {
- unsigned long int check_value = (prescale_divisor[i] /24);
- if(check_value < 1 || check_value > period) {
- break;
- }
- if(((period / check_value) >= min_optimal_period_cycles) &&
- ((period / check_value) <= MAX_CYCLES)) {
- best_prescale = i;
- break;
- }
- }
-
- if(best_prescale > 13) {
- for(i = 0 ; i < 13 ; i++) {
- unsigned long int check_value = (prescale_divisor[i] /24);
- if(check_value < 1 || check_value > period) {
- break;
- }
- if(((period / check_value) >= min_period_cycles) &&
- ((period / check_value) <= MAX_CYCLES)) {
- best_prescale = i;
- break;
- }
- }
- }
- if(best_prescale > 13) {
- best_prescale = PRESCALE_DIV480; /* Something that's not zero - use invalid prescale value */
- }
-
- return best_prescale;
-}
-
-/*
- * return the number of cycles for the channel period computed from the microseconds
- * for the period. Allwinner docs call this "entire" cycles
- */
-unsigned int get_entire_cycles(struct sun4i_pwm_available_channel *chan) {
- unsigned int entire_cycles = 0x01;
- if ((2 * prescale_divisor[chan->prescale] * MAX_CYCLES) > 0) {
- entire_cycles = chan->period / (prescale_divisor[chan->prescale] /24);
- }
- if(entire_cycles == 0) {entire_cycles = MAX_CYCLES;}
- if(entire_cycles > MAX_CYCLES) {entire_cycles = MAX_CYCLES;}
+/*
+ * Functions to display the pwm variables currently set
+ */
+
+static ssize_t pwm_polarity_show(struct device *dev, struct device_attribute *attr, char *buf) {
+ const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
+ ssize_t status;
+ switch (chan->channel) {
+ case 0:
+ status = scnprintf(buf,PAGE_SIZE,"%d",chan->ctrl_current.s.ch0_act_state);
+ break;
+ case 1:
+ status = scnprintf(buf,PAGE_SIZE,"%d",chan->ctrl_current.s.ch1_act_state);
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+ return status;
+}
+static ssize_t pwm_period_show(struct device *dev, struct device_attribute *attr, char *buf) {
+ const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
+ ssize_t status;
+ status = sprintf(buf,"%lu",chan->period);
+ return status;
+}
+static ssize_t pwm_duty_show(struct device *dev, struct device_attribute *attr, char *buf) {
+ const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
+ ssize_t status;
+ status = sprintf(buf,"%lu",chan->duty);
+ return status;
+}
+static ssize_t pwm_run_show(struct device *dev,struct device_attribute *attr, char *buf) {
+ const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
+ ssize_t status;
+ switch (chan->channel) {
+ case 0:
+ status = sprintf(buf,"%d",chan->ctrl_current.s.ch0_en);
+ break;
+ case 1:
+ status = sprintf(buf,"%d",chan->ctrl_current.s.ch1_en);
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+
+ return status;
+}
+static ssize_t pwm_duty_percent_show(struct device *dev,struct device_attribute *attr, char *buf) {
+ const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
+ return sprintf(buf,"%u",chan->duty_percent);
+}
+static ssize_t pwm_pulse_show(struct device *dev,struct device_attribute *attr, char *buf) {
+ const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
+ ssize_t status;
+ switch (chan->channel) {
+ case 0:
+ status = sprintf(buf,"%d",chan->ctrl_current.s.ch0_pulse_start);
+ break;
+ case 1:
+ status = sprintf(buf,"%d",chan->ctrl_current.s.ch1_pulse_start);
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+ return status;
+}
+
+static ssize_t pwm_pin_show(struct device *dev,struct device_attribute *attr, char *buf) {
+ const struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
+ ssize_t status;
+ status = sprintf(buf,"%s",chan->pin_name);
+
+ return status;
+}
+
+/*
+ * Functions to store values for pwm
+ */
+
+static ssize_t pwm_polarity_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) {
+ struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
+ ssize_t status = -EINVAL;
+ int act_state = 0;
+
+ sscanf(buf,"%d",&act_state);
+ if(act_state < 2) {
+ switch (chan->channel) {
+ case 0:
+ chan->ctrl_current.s.ch0_act_state = act_state;
+ break;
+ case 1:
+ chan->ctrl_current.s.ch1_act_state = act_state;
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+ status = size;
+ }
+ return status;
+}
+static ssize_t pwm_period_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) {
+ unsigned long long period = 0;
+ struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
+
+ period = convert_string_to_microseconds(buf);
+ if(!period || period > ULONG_MAX) {
+ size = -EINVAL;
+ } else {
+ if(period <= chan->duty) {
+ chan->duty = period;
+ }
+ chan->period = period;
+ chan->prescale = pwm_get_best_prescale(period);
+ fixup_duty(chan);
+ if(chan->duty) {
+ pwm_set_mode(NO_ENABLE_CHANGE,chan);
+ }
+ }
+ return size;
+}
+static ssize_t pwm_duty_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) {
+ unsigned long long duty = 0;
+ struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
+
+ /* sscanf(buf,"%Lu",&duty); */ /* L means long long pointer */
+ duty = convert_string_to_microseconds(buf);
+ duty = duty > ULONG_MAX ? ULONG_MAX : duty;
+ duty = duty > chan->period ? chan->period : duty;
+ chan->duty_percent = -1; /* disable duty_percent if duty is set by hand */
+ chan->duty = duty;
+ pwm_set_mode(NO_ENABLE_CHANGE,chan);
+ return size;
+}
+
+struct time_suffix suffixes[] = {
+ [0] = { .suffix = "hz", .multiplier = 1, .freq = true }, /* f = 1/T */
+ [1] = { .suffix = "khz", .multiplier = 1000, .freq = true },
+ [2] = { .suffix = "mhz", .multiplier = 1000000, .freq = true },
+ [3] = { .suffix = "ghz", .multiplier = 1000000000, .freq = true },
+ [4] = { .suffix = "ms", .multiplier = 1000, .freq = false }, /* T = 1/f */
+ [5] = { .suffix = "us", .multiplier = 1, .freq = false },
+ [6] = { .suffix = "ns", .multiplier = 1, .freq = false },
+ [7] = { .suffix = NULL, .multiplier = 0, .freq = false },
+};
+
+
+unsigned long convert_string_to_microseconds(const char *buf) {
+ unsigned char ch = 0;
+ char numbers[10];
+ char letters[4];
+ const char *bufptr = buf;
+ int i = 0;
+ unsigned long microseconds = 0;
+ unsigned long numeric_part = 0;
+ int found_suffix = -1;
+ int numbers_index = 0, letters_index = 0;
+ while(bufptr && *bufptr && (ch = *bufptr) && isdigit(ch) && numbers_index < (sizeof(numbers)-1)) {
+ numbers[numbers_index++] = *bufptr++;
+ }
+ numbers[numbers_index] = 0;
+ while(bufptr && *bufptr && (ch = *bufptr) && strchr("usmhznhzkg",tolower(ch)) && letters_index < (sizeof(letters)-1)) {
+ letters[letters_index++] = tolower(*bufptr);
+ bufptr++;
+ }
+ letters[letters_index] = 0;
+ sscanf(numbers,"%lu",&numeric_part);
+ while(suffixes[i].suffix) {
+ if(!strcmp(suffixes[i].suffix,letters)) {
+ found_suffix = i;
+ break;
+ }
+ i++;
+ }
+ if(found_suffix > -1) {
+ if(suffixes[found_suffix].freq) {
+ microseconds = 1000000 / (numeric_part * suffixes[found_suffix].multiplier);
+ } else {
+ microseconds = suffixes[found_suffix].multiplier * numeric_part;
+ }
+ }
+ return microseconds;
+}
+
+
+
+
+static const unsigned int prescale_divisor[13] = {120,
+ 180,
+ 240,
+ 360,
+ 480,
+ 480, /* Invalid Option */
+ 480, /* Invalid Option */
+ 480, /* Invalid Option */
+ 12000,
+ 24000,
+ 36000,
+ 48000,
+ 72000};
+
+/*
+ * Find the best prescale value for the period
+ * We want to get the highest period cycle count possible, so we look
+ * make a run through the prescale values looking for numbers over
+ * min_optimal_period_cycles. If none are found then root though again
+ * taking anything that works
+ */
+enum sun4i_pwm_prescale pwm_get_best_prescale(unsigned long long period_in_us) {
+ int i;
+ unsigned long period_ticks_ns = 42; /* non exact value this introduce 0.8% per clock of error */
+ unsigned long period_ns = period_in_us *1000;
+ enum sun4i_pwm_prescale best_prescale = PRESCALE_DIV1;
+/* with this code the lowest prescaler is always selected */
+ if (period_in_us!= 0){
+
+ if (period_ns < MAX_CYCLES * period_ticks_ns) /* ~2.7ms */
+ goto finded;
+
+ if (period_ns < MAX_CYCLES * 120 * period_ticks_ns){ /*~327ms~*/
+ best_prescale = PRESCALE_DIV120;
+ goto finded;
+ }
+ if (period_ns < MAX_CYCLES * 180 * period_ticks_ns){ /*~491ms~*/
+ best_prescale = PRESCALE_DIV180;
+ goto finded;
+ }
+ if (period_ns < MAX_CYCLES * 240 * period_ticks_ns){ /*~655ms~*/
+ best_prescale = PRESCALE_DIV240;
+ goto finded;
+ }
+ if (period_ns < MAX_CYCLES * 360 * period_ticks_ns){ /*~983ms~*/
+ best_prescale = PRESCALE_DIV360;
+ goto finded;
+ }
+ if (period_ns < MAX_CYCLES * 480 * period_ticks_ns){ /*~1310ms~*/
+ best_prescale = PRESCALE_DIV480;
+ goto finded;
+ }
+ if (period_in_us < MAX_CYCLES * 12 * period_ticks_ns){ /*~32767.5ms~*/
+ best_prescale = PRESCALE_DIV12k;
+ goto finded;
+ }
+
+ if (period_in_us < MAX_CYCLES * 24 * period_ticks_ns){ /*~65535ms*/
+ best_prescale = PRESCALE_DIV24k;
+ goto finded;
+ }
+ if (period_in_us < MAX_CYCLES * 36 * period_ticks_ns){ /*~98302ms~*/
+ best_prescale = PRESCALE_DIV36k;
+ goto finded;
+ }
+ if (period_in_us < MAX_CYCLES * 48 * period_ticks_ns){ /*~130070ms~*/
+ best_prescale = PRESCALE_DIV48k;
+ goto finded;
+ }
+ if (period_in_us < MAX_CYCLES * 72 * period_ticks_ns){ /*max absolute error ~~1.5ms */
+ best_prescale = PRESCALE_DIV72k;
+ }
+ }
+ else
+ best_prescale = PRESCALE_DIV480; /*default value when non found or input is 0*/
+ finded:
+ return best_prescale;
+}
+
+/*
+ * return the number of cycles for the channel period computed from the microseconds
+ * for the period. Allwinner docs call this "entire" cycles
+ */
+unsigned int get_entire_cycles(struct sun4i_pwm_available_channel *chan) {
+ unsigned int entire_cycles = 0x01;
+ if ((2 * prescale_divisor[chan->prescale] * MAX_CYCLES) > 0) {
+ entire_cycles = chan->period / (prescale_divisor[chan->prescale] /24);
+ }
+ if(entire_cycles == 0) {entire_cycles = MAX_CYCLES;}
+ if(entire_cycles > MAX_CYCLES) {entire_cycles = MAX_CYCLES;}
#ifdef SUNXI_PWM_DEBUG
- printk("Best prescale was %d, entire cycles was %u\n",chan->prescale, entire_cycles);
+ printk("Best prescale was %d, entire cycles was %u\n",chan->prescale, entire_cycles);
#endif
-
- return entire_cycles;
-}
-
-/*
- * return the number of cycles for the channel duty computed from the microseconds
- * for the duty. Allwinner docs call this "active" cycles
- */
-unsigned int get_active_cycles(struct sun4i_pwm_available_channel *chan) {
- unsigned int active_cycles = 0x01;
- unsigned int entire_cycles = get_entire_cycles(chan);
- if(chan->duty < 0 && chan->period) {
- active_cycles = entire_cycles-1;
- } else if ((2 * prescale_divisor[chan->prescale] * MAX_CYCLES) > 0) {
- active_cycles = chan->duty / (prescale_divisor[chan->prescale] /24);
- }
-/* if(active_cycles == 0) {active_cycles = 0x0ff;} */
+
+ return entire_cycles;
+}
+
+/*
+ * return the number of cycles for the channel duty computed from the microseconds
+ * for the duty. Allwinner docs call this "active" cycles
+ */
+unsigned int get_active_cycles(struct sun4i_pwm_available_channel *chan) {
+ unsigned int active_cycles = 0x01;
+ unsigned int entire_cycles = get_entire_cycles(chan);
+ if(chan->duty < 0 && chan->period) {
+ active_cycles = entire_cycles-1;
+ } else if ((2 * prescale_divisor[chan->prescale] * MAX_CYCLES) > 0) {
+ active_cycles = chan->duty / (prescale_divisor[chan->prescale] /24);
+ }
+/* if(active_cycles == 0) {active_cycles = 0x0ff;} */
#ifdef SUNXI_PWM_DEBUG
- printk("Best prescale was %d, active cycles was %u (before entire check)\n",chan->prescale, active_cycles);
+ printk("Best prescale was %d, active cycles was %u (before entire check)\n",chan->prescale, active_cycles);
#endif
- if(active_cycles > MAX_CYCLES) {active_cycles = entire_cycles-1;}
+ if(active_cycles > MAX_CYCLES) {active_cycles = entire_cycles-1;}
#ifdef SUNXI_PWM_DEBUG
- printk("Best prescale was %d, active cycles was %u (after entire check)\n",chan->prescale, active_cycles);
+ printk("Best prescale was %d, active cycles was %u (after entire check)\n",chan->prescale, active_cycles);
#endif
- return active_cycles;
-}
-
-/*
- * When the duty is set, compute the number of microseconds
- * based on the period.
- */
-
-void fixup_duty(struct sun4i_pwm_available_channel *chan) {
- if(chan->duty_percent >= 0) {
- chan->duty = chan->period * chan->duty_percent / 100;
- }
-}
-
-/*
- * Stores the run (enable) bit.
- */
-
-static ssize_t pwm_run_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) {
- struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
- ssize_t status = -EINVAL;
- int enable = 0;
-
- sscanf(buf,"%d",&enable);
- if(enable < 2) {
- status = pwm_set_mode(enable, chan);
- }
- return size;
-}
-
-static ssize_t pwm_duty_percent_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) {
- unsigned int duty_percent = 0;
- struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
-
- sscanf(buf,"%u",&duty_percent);
- if(duty_percent > 100) {
- size = -EINVAL;
- } else {
- chan->duty_percent = duty_percent;
- if(chan->period) {
- fixup_duty(chan);
- pwm_set_mode(NO_ENABLE_CHANGE,chan);
- }
- }
-
- return size;
-}
-static ssize_t pwm_pulse_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) {
- struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
- ssize_t status = -EINVAL;
- int pulse = 0;
- sscanf(buf,"%d",&pulse);
- if(pulse < 2) {
- switch (chan->channel) {
- case 0:
- chan->ctrl_current.s.ch0_pulse_start = pulse;
- break;
- case 1:
- chan->ctrl_current.s.ch1_pulse_start = pulse;
- break;
- default:
- status = -EINVAL;
- break;
- }
- status = size;
- }
- return status;
-}
-
-int pwm_set_period_and_duty(struct sun4i_pwm_available_channel *chan) {
- int return_val = -EINVAL;
- unsigned int entire_cycles = get_entire_cycles(chan);
- unsigned int active_cycles = get_active_cycles(chan);
- chan->period_reg.initializer = 0;
- if(entire_cycles >= active_cycles && active_cycles) {
- chan->period_reg.s.pwm_entire_cycles = entire_cycles;
- chan->period_reg.s.pwm_active_cycles = active_cycles;
- } else {
- chan->period_reg.s.pwm_entire_cycles = MAX_CYCLES;
- chan->period_reg.s.pwm_active_cycles = MAX_CYCLES;
- }
- writel(chan->period_reg.initializer, chan->period_reg_addr);
- return return_val;
-}
-
-
-ssize_t pwm_set_mode(unsigned int enable, struct sun4i_pwm_available_channel *chan) {
- ssize_t status = 0;
- if(enable == NO_ENABLE_CHANGE) {
- switch (chan->channel) {
- case 0:
- enable = chan->ctrl_current.s.ch0_en;
- break;
- case 1:
- enable = chan->ctrl_current.s.ch1_en;
- break;
- default:
- status = -EINVAL;
- break;
- }
- }
- chan->ctrl_current.initializer = readl(chan->ctrl_addr);
- if(enable == 1) {
- switch (chan->channel) {
- case 0:
- chan->ctrl_current.s.ch0_prescaler = 0;
- chan->ctrl_current.s.ch0_act_state = 0;
- chan->ctrl_current.s.ch0_mode = 0;
- chan->ctrl_current.s.ch0_pulse_start = 0;
- chan->ctrl_current.s.ch0_en = 0;
- chan->ctrl_current.s.ch0_clk_gating = 0;
- break;
- case 1:
- chan->ctrl_current.s.ch1_prescaler = 0;
- chan->ctrl_current.s.ch1_act_state = 0;
- chan->ctrl_current.s.ch1_mode = 0;
- chan->ctrl_current.s.ch1_pulse_start = 0;
- chan->ctrl_current.s.ch1_en = 1;
- chan->ctrl_current.s.ch1_clk_gating = 0;
- break;
- default:
- status = -EINVAL;
- break;
- }
- if(status) {
- return status;
- }
- //writel(chan->ctrl_current.initializer,chan->ctrl_addr);
- chan->pin_current.initializer = readl(chan->pin_addr);
- if(chan->pin_mask.s0.pin0_select) {
- chan->pin_current.s0.pin0_select = SELECT_PWM;
- }
- if(chan->pin_mask.s0.pin1_select) {
- chan->pin_current.s0.pin1_select = SELECT_PWM;
- }
- if(chan->pin_mask.s0.pin2_select) {
- chan->pin_current.s0.pin2_select = SELECT_PWM;
- }
- if(chan->pin_mask.s0.pin3_select) {
- chan->pin_current.s0.pin3_select = SELECT_PWM;
- }
- if(chan->pin_mask.s0.pin4_select) {
- chan->pin_current.s0.pin4_select = SELECT_PWM;
- }
- if(chan->pin_mask.s0.pin5_select) {
- chan->pin_current.s0.pin5_select = SELECT_PWM;
- }
- if(chan->pin_mask.s0.pin6_select) {
- chan->pin_current.s0.pin6_select = SELECT_PWM;
- }
- if(chan->pin_mask.s0.pin7_select) {
- chan->pin_current.s0.pin7_select = SELECT_PWM;
- }
- if(chan->channel == 0) {
- chan->ctrl_current.s.ch0_prescaler = chan->prescale;
- } else {
- chan->ctrl_current.s.ch1_prescaler = chan->prescale;
- }
- pwm_set_period_and_duty(chan);
-
- writel(chan->pin_current.initializer,chan->pin_addr);
- //writel(chan->ctrl_current.initializer,chan->ctrl_addr);
- switch (chan->channel) {
- case 0:
- chan->ctrl_current.s.ch0_en = 1;
- chan->ctrl_current.s.ch0_clk_gating = 1;
- break;
- case 1:
- chan->ctrl_current.s.ch1_en = 1;
- chan->ctrl_current.s.ch1_clk_gating = 1;
- break;
- }
- writel(chan->ctrl_current.initializer,chan->ctrl_addr);
-
- } else if (enable == 0) {
- switch (chan->channel) {
- case 0:
- chan->ctrl_current.s.ch0_clk_gating = 0;
- chan->ctrl_current.s.ch0_en = enable;
- break;
- case 1:
- chan->ctrl_current.s.ch1_clk_gating = 0;
- chan->ctrl_current.s.ch1_en = enable;
- break;
- default:
- status = -EINVAL;
- break;
- }
- if(!status) {
- chan->pin_current.initializer &= ~chan->pin_mask.initializer;
- chan->pin_current.initializer |= readl(chan->pin_addr) & chan->pin_mask.initializer;
- writel(chan->pin_current.initializer,chan->pin_addr);
- writel(chan->ctrl_current.initializer,chan->ctrl_addr);
- }
- }
- return status;
-}
-
-
-
-void pwm_setup_available_channels( void ) {
- void * timer_base = ioremap(SW_PA_TIMERC_IO_BASE, 0x400); /* 0x01c20c00 */
- void * PWM_CTRL_REG_BASE = timer_base + 0x200; /* 0x01c20e00 */
- void * portc_io_base = ioremap(SW_PA_PORTC_IO_BASE,0x400); /* 0x01c20800 */
- void * PB_CFG0_REG = (portc_io_base + 0x24); /* 0x01C20824 */
- void * PI_CFG0_REG = (portc_io_base + 0x120); /* 0x01c20920 */
-
- /*void * PB_PULL0_REG = (portc_io_base + 0x040);*/ /* 0x01c20840 */
- /*void * PI_PULL0_REG = (portc_io_base + 0x13c);*/ /* 0x01c2091c */
- /*void * PH_CFG0_REG = (portc_io_base + 0xfc);*/ /* 0x01c208fc */
- /*void * PH_CFG1_REG = (portc_io_base + 0x100);*/ /* 0x01c20900 */
- /*void * PH_PULL0_REG = (portc_io_base + 0x118);*/ /* 0x01c20918 */
-
- pwm_available_chan[0].use_count = 0;
- pwm_available_chan[0].ctrl_addr = PWM_CTRL_REG_BASE;
- pwm_available_chan[0].pin_addr = PB_CFG0_REG;
- pwm_available_chan[0].period_reg_addr = pwm_available_chan[0].ctrl_addr + 0x04;
- pwm_available_chan[0].channel = 0;
- pwm_available_chan[0].ctrl_backup.initializer = readl(pwm_available_chan[0].ctrl_addr);
- pwm_available_chan[0].ctrl_mask.initializer = 0;
- pwm_available_chan[0].ctrl_mask.s.ch0_prescaler = 0x0f;
- pwm_available_chan[0].ctrl_mask.s.ch0_en = 0x01;
- pwm_available_chan[0].ctrl_mask.s.ch0_act_state = 0x01;
- pwm_available_chan[0].ctrl_mask.s.ch0_clk_gating = 0x00;
- pwm_available_chan[0].ctrl_mask.s.ch0_mode = 0x01;
- pwm_available_chan[0].ctrl_mask.s.ch0_pulse_start = 0x01;
- pwm_available_chan[0].ctrl_current.initializer = 0;
- pwm_available_chan[0].pin_backup.initializer = readl(pwm_available_chan[0].pin_addr);
-/* pwm_available_chan[0].pin_mask.initializer = 0xffffffff; */
- pwm_available_chan[0].pin_mask.s0.pin2_select = 0x07;
- pwm_available_chan[0].pin_current.s0.pin2_select = 0x02;
-
- pwm_available_chan[0].pin_name = "PB2";
- pwm_available_chan[0].period = 10000;
- pwm_available_chan[0].duty_percent = 100;
- *(unsigned int *)&pwm_available_chan[0].period_reg = 0;
- pwm_available_chan[0].prescale = 0;
-
-
- pwm_available_chan[1].use_count = 0;
- pwm_available_chan[1].ctrl_addr = PWM_CTRL_REG_BASE;
- pwm_available_chan[1].pin_addr = PI_CFG0_REG;
- pwm_available_chan[1].period_reg_addr = pwm_available_chan[1].ctrl_addr + 0x08;
- pwm_available_chan[1].channel = 1;
- pwm_available_chan[1].ctrl_backup.initializer = readl(pwm_available_chan[1].ctrl_addr);
- pwm_available_chan[1].ctrl_mask.initializer = 0;
- pwm_available_chan[1].ctrl_mask.s.ch1_prescaler = 0x0f;
- pwm_available_chan[1].ctrl_mask.s.ch1_en = 0x01;
- pwm_available_chan[1].ctrl_mask.s.ch1_act_state = 0x01;
- pwm_available_chan[1].ctrl_mask.s.ch1_clk_gating = 0x00;
- pwm_available_chan[1].ctrl_mask.s.ch1_mode = 0x01;
- pwm_available_chan[1].ctrl_mask.s.ch1_pulse_start = 0x01;
- pwm_available_chan[1].ctrl_current.initializer = 0;
- pwm_available_chan[1].pin_backup.initializer = readl(pwm_available_chan[1].pin_addr);
- pwm_available_chan[1].pin_mask.initializer = 0;
- pwm_available_chan[1].pin_mask.s0.pin3_select = 0x07;
- pwm_available_chan[1].pin_current.s0.pin3_select = 0x02;
- pwm_available_chan[1].pin_name = "PI3";
- pwm_available_chan[1].period = 10000;
- pwm_available_chan[1].duty_percent = 50;
- *(unsigned int *)&pwm_available_chan[1].period_reg = 0;
- pwm_available_chan[1].prescale = 0;
-
-
-}
-
-struct pwm_device {
- struct sun4i_pwm_available_channel *chan;
-};
-
-struct pwm_device pwm_devices[2] = {
- [0] = {.chan = &pwm_available_chan[0]},
- [1] = {.chan = &pwm_available_chan[1]}
-};
-
-struct pwm_device *pwm_request(int pwm_id, const char *label)
-{
- struct pwm_device *pwm;
- int found = 0;
-
- if(pwm_id < 2 && pwm_id >= 0) {
- pwm = &pwm_devices[pwm_id];
- found = 1;
- }
- if (found) {
- if (pwm->chan->use_count == 0) {
- pwm->chan->use_count++;
- pwm->chan->name = label;
- } else
- pwm = ERR_PTR(-EBUSY);
- } else
- pwm = ERR_PTR(-ENOENT);
-
- return pwm;
-}
-EXPORT_SYMBOL(pwm_request);
-
-
-int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
-{
- if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
- return -EINVAL;
-
- pwm->chan->period = period_ns / 1000;
- pwm->chan->prescale = pwm_get_best_prescale(pwm->chan->period);
- pwm->chan->duty = duty_ns / 1000;
- fixup_duty(pwm->chan);
- pwm_set_mode(NO_ENABLE_CHANGE,pwm->chan);
- return 0;
-}
-EXPORT_SYMBOL(pwm_config);
-
-
-int pwm_enable(struct pwm_device *pwm)
-{
- if (pwm == NULL) {
- return -EINVAL;
- }
- pwm_set_mode(PWM_CTRL_ENABLE,pwm->chan);
- return 0;
-}
-EXPORT_SYMBOL(pwm_enable);
-
-void pwm_disable(struct pwm_device *pwm)
-{
- if (pwm == NULL) {
- return;
- }
- pwm_set_mode(PWM_CTRL_DISABLE,pwm->chan);
-}
-EXPORT_SYMBOL(pwm_disable);
-
-void pwm_free(struct pwm_device *pwm)
-{
- if (pwm->chan->use_count) {
- pwm->chan->use_count--;
- } else
- pr_warning("PWM device already freed\n");
-}
-EXPORT_SYMBOL(pwm_free);
-
-
-module_init(sunxi_pwm_init);
-module_exit(sunxi_pwm_exit);
-
-
-
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("David H. Wilkins <dwil...@conecuh.com>");
+ return active_cycles;
+}
+
+/*
+ * When the duty is set, compute the number of microseconds
+ * based on the period.
+ */
+
+void fixup_duty(struct sun4i_pwm_available_channel *chan) {
+ if(chan->duty_percent >= 0) {
+ chan->duty = chan->period * chan->duty_percent / 100;
+ }
+}
+
+/*
+ * Stores the run (enable) bit.
+ */
+
+static ssize_t pwm_run_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) {
+ struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
+ ssize_t status = -EINVAL;
+ int enable = 0;
+
+ sscanf(buf,"%d",&enable);
+ if(enable < 2) {
+ status = pwm_set_mode(enable, chan);
+ }
+ return size;
+}
+
+static ssize_t pwm_duty_percent_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) {
+ unsigned int duty_percent = 0;
+ struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
+
+ sscanf(buf,"%u",&duty_percent);
+ if(duty_percent > 100) {
+ size = -EINVAL;
+ } else {
+ chan->duty_percent = duty_percent;
+ if(chan->period) {
+ fixup_duty(chan);
+ pwm_set_mode(NO_ENABLE_CHANGE,chan);
+ }
+ }
+
+ return size;
+}
+static ssize_t pwm_pulse_store(struct device *dev,struct device_attribute *attr, const char *buf, size_t size) {
+ struct sun4i_pwm_available_channel *chan = dev_get_drvdata(dev);
+ ssize_t status = -EINVAL;
+ int pulse = 0;
+ sscanf(buf,"%d",&pulse);
+ if(pulse < 2) {
+ switch (chan->channel) {
+ case 0:
+ chan->ctrl_current.s.ch0_pulse_start = pulse;
+ break;
+ case 1:
+ chan->ctrl_current.s.ch1_pulse_start = pulse;
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+ status = size;
+ }
+ return status;
+}
+
+int pwm_set_period_and_duty(struct sun4i_pwm_available_channel *chan) {
+ int return_val = -EINVAL;
+ unsigned int entire_cycles = get_entire_cycles(chan);
+ unsigned int active_cycles = get_active_cycles(chan);
+ chan->period_reg.initializer = 0;
+ if(entire_cycles >= active_cycles && active_cycles) {
+ chan->period_reg.s.pwm_entire_cycles = entire_cycles;
+ chan->period_reg.s.pwm_active_cycles = active_cycles;
+ } else {
+ chan->period_reg.s.pwm_entire_cycles = MAX_CYCLES;
+ chan->period_reg.s.pwm_active_cycles = MAX_CYCLES;
+ }
+ writel(chan->period_reg.initializer, chan->period_reg_addr);
+ return return_val;
+}
+
+
+ssize_t pwm_set_mode(unsigned int enable, struct sun4i_pwm_available_channel *chan) {
+ ssize_t status = 0;
+ if(enable == NO_ENABLE_CHANGE) {
+ switch (chan->channel) {
+ case 0:
+ enable = chan->ctrl_current.s.ch0_en;
+ break;
+ case 1:
+ enable = chan->ctrl_current.s.ch1_en;
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+ }
+ chan->ctrl_current.initializer = readl(chan->ctrl_addr);
+ if(enable == 1) {
+ switch (chan->channel) {
+ case 0:
+ chan->ctrl_current.s.ch0_prescaler = 0;
+ chan->ctrl_current.s.ch0_act_state = 0;
+ chan->ctrl_current.s.ch0_mode = 0;
+ chan->ctrl_current.s.ch0_pulse_start = 0;
+ chan->ctrl_current.s.ch0_en = 0;
+ chan->ctrl_current.s.ch0_clk_gating = 0;
+ break;
+ case 1:
+ chan->ctrl_current.s.ch1_prescaler = 0;
+ chan->ctrl_current.s.ch1_act_state = 0;
+ chan->ctrl_current.s.ch1_mode = 0;
+ chan->ctrl_current.s.ch1_pulse_start = 0;
+ chan->ctrl_current.s.ch1_en = 1;
+ chan->ctrl_current.s.ch1_clk_gating = 0;
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+ if(status) {
+ return status;
+ }
+ //writel(chan->ctrl_current.initializer,chan->ctrl_addr);
+ chan->pin_current.initializer = readl(chan->pin_addr);
+ if(chan->pin_mask.s0.pin0_select) {
+ chan->pin_current.s0.pin0_select = SELECT_PWM;
+ }
+ if(chan->pin_mask.s0.pin1_select) {
+ chan->pin_current.s0.pin1_select = SELECT_PWM;
+ }
+ if(chan->pin_mask.s0.pin2_select) {
+ chan->pin_current.s0.pin2_select = SELECT_PWM;
+ }
+ if(chan->pin_mask.s0.pin3_select) {
+ chan->pin_current.s0.pin3_select = SELECT_PWM;
+ }
+ if(chan->pin_mask.s0.pin4_select) {
+ chan->pin_current.s0.pin4_select = SELECT_PWM;
+ }
+ if(chan->pin_mask.s0.pin5_select) {
+ chan->pin_current.s0.pin5_select = SELECT_PWM;
+ }
+ if(chan->pin_mask.s0.pin6_select) {
+ chan->pin_current.s0.pin6_select = SELECT_PWM;
+ }
+ if(chan->pin_mask.s0.pin7_select) {
+ chan->pin_current.s0.pin7_select = SELECT_PWM;
+ }
+ if(chan->channel == 0) {
+ chan->ctrl_current.s.ch0_prescaler = chan->prescale;
+ } else {
+ chan->ctrl_current.s.ch1_prescaler = chan->prescale;
+ }
+ pwm_set_period_and_duty(chan);
+
+ writel(chan->pin_current.initializer,chan->pin_addr);
+ //writel(chan->ctrl_current.initializer,chan->ctrl_addr);
+ switch (chan->channel) {
+ case 0:
+ chan->ctrl_current.s.ch0_en = 1;
+ chan->ctrl_current.s.ch0_clk_gating = 1;
+ break;
+ case 1:
+ chan->ctrl_current.s.ch1_en = 1;
+ chan->ctrl_current.s.ch1_clk_gating = 1;
+ break;
+ }
+ writel(chan->ctrl_current.initializer,chan->ctrl_addr);
+
+ } else if (enable == 0) {
+ switch (chan->channel) {
+ case 0:
+ chan->ctrl_current.s.ch0_clk_gating = 0;
+ chan->ctrl_current.s.ch0_en = enable;
+ break;
+ case 1:
+ chan->ctrl_current.s.ch1_clk_gating = 0;
+ chan->ctrl_current.s.ch1_en = enable;
+ break;
+ default:
+ status = -EINVAL;
+ break;
+ }
+ if(!status) {
+ chan->pin_current.initializer &= ~chan->pin_mask.initializer;
+ chan->pin_current.initializer |= readl(chan->pin_addr) & chan->pin_mask.initializer;
+ writel(chan->pin_current.initializer,chan->pin_addr);
+ writel(chan->ctrl_current.initializer,chan->ctrl_addr);
+ }
+ }
+ return status;
+}
+
+
+
+void pwm_setup_available_channels( void ) {
+ void * timer_base = ioremap(SW_PA_TIMERC_IO_BASE, 0x400); /* 0x01c20c00 */
+ void * PWM_CTRL_REG_BASE = timer_base + 0x200; /* 0x01c20e00 */
+ void * portc_io_base = ioremap(SW_PA_PORTC_IO_BASE,0x400); /* 0x01c20800 */
+ void * PB_CFG0_REG = (portc_io_base + 0x24); /* 0x01C20824 */
+ void * PI_CFG0_REG = (portc_io_base + 0x120); /* 0x01c20920 */
+
+ /*void * PB_PULL0_REG = (portc_io_base + 0x040);*/ /* 0x01c20840 */
+ /*void * PI_PULL0_REG = (portc_io_base + 0x13c);*/ /* 0x01c2091c */
+ /*void * PH_CFG0_REG = (portc_io_base + 0xfc);*/ /* 0x01c208fc */
+ /*void * PH_CFG1_REG = (portc_io_base + 0x100);*/ /* 0x01c20900 */
+ /*void * PH_PULL0_REG = (portc_io_base + 0x118);*/ /* 0x01c20918 */
+
+ pwm_available_chan[0].use_count = 0;
+ pwm_available_chan[0].ctrl_addr = PWM_CTRL_REG_BASE;
+ pwm_available_chan[0].pin_addr = PB_CFG0_REG;
+ pwm_available_chan[0].period_reg_addr = pwm_available_chan[0].ctrl_addr + 0x04;
+ pwm_available_chan[0].channel = 0;
+ pwm_available_chan[0].ctrl_backup.initializer = readl(pwm_available_chan[0].ctrl_addr);
+ pwm_available_chan[0].ctrl_mask.initializer = 0;
+ pwm_available_chan[0].ctrl_mask.s.ch0_prescaler = 0x0f;
+ pwm_available_chan[0].ctrl_mask.s.ch0_en = 0x01;
+ pwm_available_chan[0].ctrl_mask.s.ch0_act_state = 0x01;
+ pwm_available_chan[0].ctrl_mask.s.ch0_clk_gating = 0x00;
+ pwm_available_chan[0].ctrl_mask.s.ch0_mode = 0x01;
+ pwm_available_chan[0].ctrl_mask.s.ch0_pulse_start = 0x01;
+ pwm_available_chan[0].ctrl_current.initializer = 0;
+ pwm_available_chan[0].pin_backup.initializer = readl(pwm_available_chan[0].pin_addr);
+/* pwm_available_chan[0].pin_mask.initializer = 0xffffffff; */
+ pwm_available_chan[0].pin_mask.s0.pin2_select = 0x07;
+ pwm_available_chan[0].pin_current.s0.pin2_select = 0x02;
+
+ pwm_available_chan[0].pin_name = "PB2";
+ pwm_available_chan[0].period = 10000;
+ pwm_available_chan[0].duty_percent = 100;
+ *(unsigned int *)&pwm_available_chan[0].period_reg = 0;
+ pwm_available_chan[0].prescale = 0;
+
+
+ pwm_available_chan[1].use_count = 0;
+ pwm_available_chan[1].ctrl_addr = PWM_CTRL_REG_BASE;
+ pwm_available_chan[1].pin_addr = PI_CFG0_REG;
+ pwm_available_chan[1].period_reg_addr = pwm_available_chan[1].ctrl_addr + 0x08;
+ pwm_available_chan[1].channel = 1;
+ pwm_available_chan[1].ctrl_backup.initializer = readl(pwm_available_chan[1].ctrl_addr);
+ pwm_available_chan[1].ctrl_mask.initializer = 0;
+ pwm_available_chan[1].ctrl_mask.s.ch1_prescaler = 0x0f;
+ pwm_available_chan[1].ctrl_mask.s.ch1_en = 0x01;
+ pwm_available_chan[1].ctrl_mask.s.ch1_act_state = 0x01;
+ pwm_available_chan[1].ctrl_mask.s.ch1_clk_gating = 0x00;
+ pwm_available_chan[1].ctrl_mask.s.ch1_mode = 0x01;
+ pwm_available_chan[1].ctrl_mask.s.ch1_pulse_start = 0x01;
+ pwm_available_chan[1].ctrl_current.initializer = 0;
+ pwm_available_chan[1].pin_backup.initializer = readl(pwm_available_chan[1].pin_addr);
+ pwm_available_chan[1].pin_mask.initializer = 0;
+ pwm_available_chan[1].pin_mask.s0.pin3_select = 0x07;
+ pwm_available_chan[1].pin_current.s0.pin3_select = 0x02;
+ pwm_available_chan[1].pin_name = "PI3";
+ pwm_available_chan[1].period = 10000;
+ pwm_available_chan[1].duty_percent = 50;
+ *(unsigned int *)&pwm_available_chan[1].period_reg = 0;
+ pwm_available_chan[1].prescale = 0;
+
+
+}
+
+struct pwm_device {
+ struct sun4i_pwm_available_channel *chan;
+};
+
+struct pwm_device pwm_devices[2] = {
+ [0] = {.chan = &pwm_available_chan[0]},
+ [1] = {.chan = &pwm_available_chan[1]}
+};
+
+struct pwm_device *pwm_request(int pwm_id, const char *label)
+{
+ struct pwm_device *pwm;
+ int found = 0;
+
+ if(pwm_id < 2 && pwm_id >= 0) {
+ pwm = &pwm_devices[pwm_id];
+ found = 1;
+ }
+ if (found) {
+ if (pwm->chan->use_count == 0) {
+ pwm->chan->use_count++;
+ pwm->chan->name = label;
+ } else
+ pwm = ERR_PTR(-EBUSY);
+ } else
+ pwm = ERR_PTR(-ENOENT);
+
+ return pwm;
+}
+EXPORT_SYMBOL(pwm_request);
+
+
+int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+ if (pwm == NULL || period_ns == 0 || duty_ns > period_ns)
+ return -EINVAL;
+
+ pwm->chan->period = period_ns / 1000;
+ pwm->chan->prescale = pwm_get_best_prescale(pwm->chan->period);
+ pwm->chan->duty = duty_ns / 1000;
+ fixup_duty(pwm->chan);
+ pwm_set_mode(NO_ENABLE_CHANGE,pwm->chan);
+ return 0;
+}
+EXPORT_SYMBOL(pwm_config);
+
+
+int pwm_enable(struct pwm_device *pwm)
+{
+ if (pwm == NULL) {
+ return -EINVAL;
+ }
+ pwm_set_mode(PWM_CTRL_ENABLE,pwm->chan);
+ return 0;
+}
+EXPORT_SYMBOL(pwm_enable);
+
+void pwm_disable(struct pwm_device *pwm)
+{
+ if (pwm == NULL) {
+ return;
+ }
+ pwm_set_mode(PWM_CTRL_DISABLE,pwm->chan);
+}
+EXPORT_SYMBOL(pwm_disable);
+
+void pwm_free(struct pwm_device *pwm)
+{
+ if (pwm->chan->use_count) {
+ pwm->chan->use_count--;
+ } else
+ pr_warning("PWM device already freed\n");
+}
+EXPORT_SYMBOL(pwm_free);
+
+
+module_init(sunxi_pwm_init);
+module_exit(sunxi_pwm_exit);
+
+
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David H. Wilkins <dwil...@conecuh.com>");
--
1.9.1