mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-03 17:51:57 +09:00
ODROIDC: Support hardware PWM(GPIOX_11, GPIOX_10)
Change-Id: Ie258b5c5da3999af743e7e8fdbe22d5d9ed562af
This commit is contained in:
34
arch/arm/boot/dts/meson8b_odroidc.dts
Executable file → Normal file
34
arch/arm/boot/dts/meson8b_odroidc.dts
Executable file → Normal file
@@ -877,7 +877,23 @@
|
||||
9 0x00080000>;
|
||||
amlogic,pins = "GPIOX_8","GPIOX_9","GPIOX_10";
|
||||
};
|
||||
};
|
||||
|
||||
odroid_pwm0:odroid_pwm0{
|
||||
amlogic,setmask=<2 0x00000008>; /*GPIOX_11 PWM_B Reg2[3]*/
|
||||
amlogic,clrmask=<3 0x00100000
|
||||
7 0x40000000>;
|
||||
amlogic,pins="GPIOX_11";
|
||||
};
|
||||
odroid_pwm1:odroid_pwm1{
|
||||
amlogic,setmask=<2 0x00000008 /*GPIOX_11 PWM_B Reg2[3]*/
|
||||
9 0x00080000>; /*GPIOX_10 PWM_E Reg9[19]*/
|
||||
amlogic,clrmask=<3 0x00500100
|
||||
4 0x00800000
|
||||
6 0x00020000
|
||||
7 0xc0000000>;
|
||||
amlogic,pins="GPIOX_11","GPIOX_10";
|
||||
};
|
||||
};
|
||||
meson-eth{
|
||||
compatible = "amlogic,meson-eth";
|
||||
dev_name = "meson-eth";
|
||||
@@ -902,4 +918,20 @@
|
||||
linux,default-trigger = "heartbeat";
|
||||
};
|
||||
};
|
||||
|
||||
pwm{
|
||||
compatible = "amlogic, odroid-pwm";
|
||||
dev_name = "meson_pwm";
|
||||
status = "ok";
|
||||
pinctrl-names = "odroid_pwm0","odroid_pwm1";
|
||||
pinctrl-0 = <&odroid_pwm0>;
|
||||
pinctrl-1 = <&odroid_pwm1>;
|
||||
};
|
||||
|
||||
pwm-ctrl {
|
||||
compatible = "amlogic, pwm-ctrl";
|
||||
dev_name = "pwm-ctrl";
|
||||
status = "ok";
|
||||
};
|
||||
|
||||
}; /* end of / */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
# Automatically generated file; DO NOT EDIT.
|
||||
# Linux/arm 3.10.42 Kernel Configuration
|
||||
# Linux/arm 3.10.43 Kernel Configuration
|
||||
#
|
||||
CONFIG_ARM=y
|
||||
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
|
||||
@@ -1109,7 +1109,7 @@ CONFIG_PINCTRL_AMLOGIC=y
|
||||
# CONFIG_AMLOGIC_BOARD_HAS_PMU is not set
|
||||
# CONFIG_AML_PMU_ALGORITHM_SUPPORT is not set
|
||||
# CONFIG_AML_DVFS is not set
|
||||
CONFIG_MESON_CS_DCDC_REGULATOR=y
|
||||
# CONFIG_MESON_CS_DCDC_REGULATOR is not set
|
||||
|
||||
#
|
||||
# Security key Support
|
||||
@@ -1141,6 +1141,8 @@ CONFIG_AML_WDT=y
|
||||
#
|
||||
CONFIG_AMLOGIC_SPICC_MASTER=m
|
||||
# CONFIG_AMLOGIC_SPICC_MASTER_DEBUG is not set
|
||||
CONFIG_MESON_PWM=m
|
||||
CONFIG_MESON_PWM_CTRL=m
|
||||
|
||||
#
|
||||
# USB Support
|
||||
@@ -2003,6 +2005,7 @@ CONFIG_INPUT_MISC=y
|
||||
CONFIG_INPUT_UINPUT=m
|
||||
CONFIG_INPUT_GPIO=m
|
||||
# CONFIG_INPUT_PCF8574 is not set
|
||||
# CONFIG_INPUT_PWM_BEEPER is not set
|
||||
# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set
|
||||
# CONFIG_INPUT_ADXL34X is not set
|
||||
# CONFIG_INPUT_IMS_PCU is not set
|
||||
@@ -2335,6 +2338,7 @@ CONFIG_HWMON=y
|
||||
# CONFIG_SENSORS_MAX6697 is not set
|
||||
# CONFIG_SENSORS_MCP3021 is not set
|
||||
# CONFIG_SENSORS_NCT6775 is not set
|
||||
# CONFIG_SENSORS_NTC_THERMISTOR is not set
|
||||
# CONFIG_SENSORS_PC87360 is not set
|
||||
# CONFIG_SENSORS_PC87427 is not set
|
||||
# CONFIG_SENSORS_PCF8591 is not set
|
||||
@@ -2957,6 +2961,7 @@ CONFIG_LCD_CLASS_DEVICE=m
|
||||
# CONFIG_LCD_HX8357 is not set
|
||||
CONFIG_BACKLIGHT_CLASS_DEVICE=y
|
||||
CONFIG_BACKLIGHT_GENERIC=y
|
||||
# CONFIG_BACKLIGHT_PWM is not set
|
||||
# CONFIG_BACKLIGHT_ADP8860 is not set
|
||||
# CONFIG_BACKLIGHT_ADP8870 is not set
|
||||
# CONFIG_BACKLIGHT_LM3630 is not set
|
||||
@@ -3397,6 +3402,7 @@ CONFIG_LEDS_GPIO=y
|
||||
# CONFIG_LEDS_PCA955X is not set
|
||||
# CONFIG_LEDS_PCA9633 is not set
|
||||
# CONFIG_LEDS_DAC124S085 is not set
|
||||
# CONFIG_LEDS_PWM is not set
|
||||
# CONFIG_LEDS_REGULATOR is not set
|
||||
# CONFIG_LEDS_BD2802 is not set
|
||||
# CONFIG_LEDS_LT3593 is not set
|
||||
@@ -3595,7 +3601,7 @@ CONFIG_OF_IOMMU=y
|
||||
# CONFIG_EXTCON is not set
|
||||
# CONFIG_MEMORY is not set
|
||||
# CONFIG_IIO is not set
|
||||
# CONFIG_PWM is not set
|
||||
CONFIG_PWM=y
|
||||
CONFIG_IRQCHIP=y
|
||||
CONFIG_ARM_GIC=y
|
||||
# CONFIG_IPACK_BUS is not set
|
||||
|
||||
@@ -26,6 +26,7 @@ source "drivers/amlogic/smartcard/Kconfig"
|
||||
source "drivers/amlogic/thermal/Kconfig"
|
||||
source "drivers/amlogic/watchdog/Kconfig"
|
||||
source "drivers/amlogic/spi/Kconfig"
|
||||
source "drivers/amlogic/pwm/Kconfig"
|
||||
|
||||
#
|
||||
# Block devices
|
||||
|
||||
@@ -129,3 +129,5 @@ obj-y += crypto/
|
||||
#obj-$(CONFIG_MESON_TRUSTZONE) += trustzone/
|
||||
obj-$(CONFIG_MESON_TRUSTZONE) += secure_monitor/
|
||||
obj-y += spi/
|
||||
|
||||
obj-$(CONFIG_MESON_PWM) += pwm/
|
||||
|
||||
17
drivers/amlogic/pwm/Kconfig
Normal file
17
drivers/amlogic/pwm/Kconfig
Normal file
@@ -0,0 +1,17 @@
|
||||
config MESON_PWM
|
||||
tristate "Amlogic PWM support"
|
||||
depends on !MESON_CS_DCDC_REGULATOR
|
||||
help
|
||||
Generic PWM framework driver for AMLOGIC.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pwm-meson.
|
||||
|
||||
config MESON_PWM_CTRL
|
||||
tristate "Amlogic PWM Control driver"
|
||||
depends on MESON_PWM
|
||||
help
|
||||
Generic PWM framework driver for Hardkernel.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called pwm-meson.
|
||||
2
drivers/amlogic/pwm/Makefile
Normal file
2
drivers/amlogic/pwm/Makefile
Normal file
@@ -0,0 +1,2 @@
|
||||
obj-$(CONFIG_MESON_PWM) += pwm-meson.o
|
||||
obj-$(CONFIG_MESON_PWM_CTRL) += pwm-ctrl.o
|
||||
429
drivers/amlogic/pwm/pwm-ctrl.c
Normal file
429
drivers/amlogic/pwm/pwm-ctrl.c
Normal file
@@ -0,0 +1,429 @@
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
//
|
||||
// ODROID Board : PWM ctrl driver
|
||||
//
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/pwm.h>
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
struct pwm_ctrl {
|
||||
struct pwm_device *pwm0;
|
||||
struct pwm_device *pwm1;
|
||||
struct mutex mutex;
|
||||
int pwm0_status,pwm1_status;
|
||||
int freq0,freq1;
|
||||
int duty0,duty1;
|
||||
};
|
||||
|
||||
//[*]------------------------------------------------------------------------------------------------------------------
|
||||
//
|
||||
// driver sysfs attribute define
|
||||
//
|
||||
//[*]------------------------------------------------------------------------------------------------------------------
|
||||
static ssize_t set_enable0 (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct pwm_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
|
||||
if(!(sscanf(buf, "%u\n", &val))) return -EINVAL;
|
||||
dev_info(dev, "PWM_0 : %s [%d] \n",__FUNCTION__,val);
|
||||
|
||||
mutex_lock(&ctrl->mutex);
|
||||
if(val) {
|
||||
ctrl->pwm0_status = 1;
|
||||
pwm_disable(ctrl->pwm0);
|
||||
pwm_config(ctrl->pwm0, ctrl->duty0, ctrl->freq0);
|
||||
pwm_enable(ctrl->pwm0);
|
||||
}
|
||||
else {
|
||||
pwm_disable(ctrl->pwm0);
|
||||
pwm_config(ctrl->pwm0, 0, ctrl->freq0);
|
||||
ctrl->pwm0_status = 0;
|
||||
}
|
||||
mutex_unlock(&ctrl->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_status0 (struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pwm_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
|
||||
if(ctrl->pwm0_status) return sprintf(buf, "PWM_0 : %s\n", "on");
|
||||
else return sprintf(buf, "PWM_0 : %s\n", "off");
|
||||
}
|
||||
|
||||
//[*]------------------------------------------------------------------------------------------------------------------
|
||||
//[*]------------------------------------------------------------------------------------------------------------------
|
||||
static ssize_t set_duty0 (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct pwm_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
|
||||
if(!(sscanf(buf, "%u\n", &val))) return -EINVAL;
|
||||
|
||||
if((val > 100)||(val < 0)){
|
||||
dev_err(dev, "PWM_0 : Invalid param. Duty cycle range is 0 to 100 \n");
|
||||
return count;
|
||||
}
|
||||
|
||||
dev_info(dev, "PWM_0 : %s [%d] \n",__FUNCTION__,val);
|
||||
|
||||
mutex_lock(&ctrl->mutex);
|
||||
ctrl->duty0 = val;
|
||||
|
||||
if(ctrl->pwm0_status){
|
||||
pwm_disable(ctrl->pwm0);
|
||||
pwm_config(ctrl->pwm0, ctrl->duty0, ctrl->freq0);
|
||||
pwm_enable(ctrl->pwm0);
|
||||
}
|
||||
else {
|
||||
pwm_disable(ctrl->pwm0);
|
||||
pwm_config(ctrl->pwm0, 0, ctrl->freq0);
|
||||
}
|
||||
mutex_unlock(&ctrl->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_duty0 (struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pwm_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", ctrl->duty0);
|
||||
}
|
||||
|
||||
//[*]------------------------------------------------------------------------------------------------------------------
|
||||
//[*]------------------------------------------------------------------------------------------------------------------
|
||||
static ssize_t set_freq0 (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct pwm_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
|
||||
if(!(sscanf(buf, "%u\n", &val))) return -EINVAL;
|
||||
|
||||
if((val < 0)){
|
||||
dev_err(dev, "PWM_0 : Invalid param. Duty cycle range is 0 to 100 \n");
|
||||
return count;
|
||||
}
|
||||
|
||||
dev_info(dev, "PWM_0 : %s [%d] \n",__FUNCTION__,val);
|
||||
|
||||
mutex_lock(&ctrl->mutex);
|
||||
ctrl->freq0 = val;
|
||||
|
||||
if(ctrl->pwm0_status){
|
||||
pwm_disable(ctrl->pwm0);
|
||||
pwm_config(ctrl->pwm0, ctrl->duty0, ctrl->freq0);
|
||||
pwm_enable(ctrl->pwm0);
|
||||
}
|
||||
else {
|
||||
pwm_disable(ctrl->pwm0);
|
||||
pwm_config(ctrl->pwm0, 0, ctrl->freq0);
|
||||
}
|
||||
mutex_unlock(&ctrl->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_freq0 (struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pwm_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", ctrl->freq0);
|
||||
}
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
static DEVICE_ATTR(enable0, S_IRWXUGO, show_status0, set_enable0);
|
||||
static DEVICE_ATTR(freq0, S_IRWXUGO, show_freq0, set_freq0);
|
||||
static DEVICE_ATTR(duty0, S_IRWXUGO, show_duty0, set_duty0);
|
||||
|
||||
static struct attribute *pwm0_ctrl_sysfs_entries[] = {
|
||||
&dev_attr_enable0.attr,
|
||||
&dev_attr_freq0.attr,
|
||||
&dev_attr_duty0.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct attribute_group pwm0_ctrl_attr_group = {
|
||||
.name = NULL,
|
||||
.attrs = pwm0_ctrl_sysfs_entries,
|
||||
};
|
||||
|
||||
|
||||
//[*]------------------------------------------------------------------------------------------------------------------
|
||||
static ssize_t set_enable1 (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct pwm_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
|
||||
if(!(sscanf(buf, "%u\n", &val))) return -EINVAL;
|
||||
dev_info(dev, "PWM_1 : %s [%d] \n",__FUNCTION__,val);
|
||||
|
||||
mutex_lock(&ctrl->mutex);
|
||||
if(val) {
|
||||
ctrl->pwm1_status = 1;
|
||||
pwm_disable(ctrl->pwm1);
|
||||
pwm_config(ctrl->pwm1, ctrl->duty1, ctrl->freq1);
|
||||
pwm_enable(ctrl->pwm1);
|
||||
}
|
||||
else {
|
||||
pwm_disable(ctrl->pwm1);
|
||||
pwm_config(ctrl->pwm1, 0, ctrl->freq1);
|
||||
ctrl->pwm1_status = 0;
|
||||
}
|
||||
mutex_unlock(&ctrl->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_status1 (struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pwm_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
|
||||
if(ctrl->pwm1_status) return sprintf(buf, "PWM_1 : %s\n", "on");
|
||||
else return sprintf(buf, "PWM_1 : %s\n", "off");
|
||||
}
|
||||
|
||||
//[*]------------------------------------------------------------------------------------------------------------------
|
||||
//[*]------------------------------------------------------------------------------------------------------------------
|
||||
static ssize_t set_duty1 (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct pwm_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
|
||||
if(!(sscanf(buf, "%u\n", &val))) return -EINVAL;
|
||||
|
||||
if((val > 100)||(val < 0)){
|
||||
dev_err(dev, "PWM_1 : Invalid param. Duty cycle range is 0 to 100 \n");
|
||||
return count;
|
||||
}
|
||||
dev_info(dev, "PWM_1 : %s [%d] \n",__FUNCTION__,val);
|
||||
|
||||
mutex_lock(&ctrl->mutex);
|
||||
ctrl->duty1 = val;
|
||||
|
||||
if(ctrl->pwm1_status){
|
||||
pwm_disable(ctrl->pwm1);
|
||||
pwm_config(ctrl->pwm1, ctrl->duty1, ctrl->freq1);
|
||||
pwm_enable(ctrl->pwm1);
|
||||
}
|
||||
else {
|
||||
pwm_disable(ctrl->pwm1);
|
||||
pwm_config(ctrl->pwm1, 0, ctrl->freq1);
|
||||
}
|
||||
mutex_unlock(&ctrl->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_duty1 (struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pwm_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", ctrl->duty1);
|
||||
}
|
||||
|
||||
//[*]------------------------------------------------------------------------------------------------------------------
|
||||
//[*]------------------------------------------------------------------------------------------------------------------
|
||||
static ssize_t set_freq1 (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
|
||||
{
|
||||
struct pwm_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
unsigned int val;
|
||||
|
||||
if(!(sscanf(buf, "%u\n", &val))) return -EINVAL;
|
||||
|
||||
if((val < 10)||(val > 1000000)){
|
||||
dev_err(dev, "PWM_1 : Invalid param. Duty cycle range is 10 to 1MHz \n");
|
||||
return count;
|
||||
}
|
||||
dev_info(dev, "PWM_1 : %s [%d] \n",__FUNCTION__,val);
|
||||
mutex_lock(&ctrl->mutex);
|
||||
ctrl->freq1 = val;
|
||||
|
||||
if(ctrl->pwm1_status){
|
||||
pwm_disable(ctrl->pwm1);
|
||||
pwm_config(ctrl->pwm1, ctrl->duty1, ctrl->freq1);
|
||||
pwm_enable(ctrl->pwm1);
|
||||
}
|
||||
else {
|
||||
pwm_disable(ctrl->pwm1);
|
||||
pwm_config(ctrl->pwm1, 0, ctrl->freq1);
|
||||
}
|
||||
mutex_unlock(&ctrl->mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t show_freq1 (struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct pwm_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
|
||||
return sprintf(buf, "%d\n", ctrl->freq1);
|
||||
}
|
||||
//[*]------------------------------------------------------------------------------------------------------------------
|
||||
//[*]------------------------------------------------------------------------------------------------------------------
|
||||
static DEVICE_ATTR(enable1, S_IRWXUGO, show_status1, set_enable1);
|
||||
static DEVICE_ATTR(freq1, S_IRWXUGO, show_freq1, set_freq1);
|
||||
static DEVICE_ATTR(duty1, S_IRWXUGO, show_duty1, set_duty1);
|
||||
static struct attribute *pwm1_ctrl_sysfs_entries[] = {
|
||||
&dev_attr_enable1.attr,
|
||||
&dev_attr_freq1.attr,
|
||||
&dev_attr_duty1.attr,
|
||||
NULL
|
||||
};
|
||||
static struct attribute_group pwm1_ctrl_attr_group = {
|
||||
.name = NULL,
|
||||
.attrs = pwm1_ctrl_sysfs_entries,
|
||||
};
|
||||
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
static int pwm_ctrl_resume(struct platform_device *dev)
|
||||
{
|
||||
#if defined(DEBUG_PM_MSG)
|
||||
dev_info(dev,"%s\n", __FUNCTION__);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
static int pwm_ctrl_suspend(struct platform_device *dev, pm_message_t state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
static int pwm_ctrl_probe (struct platform_device *pdev)
|
||||
{
|
||||
struct pwm_ctrl *ctrl;
|
||||
struct device *dev = &pdev->dev;
|
||||
int ret=0;
|
||||
|
||||
ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
|
||||
if (!ctrl) {
|
||||
dev_err(&pdev->dev, "no memory for state\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
ctrl->pwm0 = pwm_request(0, "pwm-ctrl");
|
||||
if (IS_ERR(ctrl->pwm0)) {
|
||||
dev_err(&pdev->dev, "unable to request legacy PWM\n");
|
||||
ret = PTR_ERR(ctrl->pwm0);
|
||||
goto err_request;
|
||||
}
|
||||
|
||||
ctrl->pwm1 = pwm_request(1, "pwm-ctrl");
|
||||
if (IS_ERR(ctrl->pwm1)) {
|
||||
dev_err(&pdev->dev, "unable to request legacy PWM\n");
|
||||
ctrl->pwm1=NULL;
|
||||
}
|
||||
|
||||
mutex_init(&ctrl->mutex);
|
||||
dev_set_drvdata(dev, ctrl);
|
||||
|
||||
ret =sysfs_create_group(&dev->kobj, &pwm0_ctrl_attr_group);
|
||||
if(ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to create sysfs group !!\n");
|
||||
}
|
||||
|
||||
if(ctrl->pwm1 != NULL){
|
||||
ret =sysfs_create_group(&dev->kobj, &pwm1_ctrl_attr_group);
|
||||
if(ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to create sysfs group !!\n");
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_request:
|
||||
devm_kfree(&pdev->dev, ctrl);
|
||||
kfree(ctrl);
|
||||
err_alloc:
|
||||
return ret;
|
||||
}
|
||||
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
static int pwm_ctrl_remove (struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct pwm_ctrl *ctrl = dev_get_drvdata(dev);
|
||||
|
||||
|
||||
if(ctrl->pwm1 != NULL){
|
||||
sysfs_remove_group(&dev->kobj, &pwm1_ctrl_attr_group);
|
||||
}
|
||||
sysfs_remove_group(&dev->kobj, &pwm0_ctrl_attr_group);
|
||||
|
||||
if(ctrl->pwm1)
|
||||
pwm_free(ctrl->pwm1);
|
||||
|
||||
if(ctrl->pwm0)
|
||||
pwm_free(ctrl->pwm0);
|
||||
|
||||
devm_kfree(dev, ctrl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
#if defined(CONFIG_OF)
|
||||
static const struct of_device_id pwm_ctrl_dt[] = {
|
||||
{ .compatible = "amlogic, pwm-ctrl" },
|
||||
{ },
|
||||
};
|
||||
#endif
|
||||
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
static struct platform_driver pwm_ctrl_driver = {
|
||||
.driver = {
|
||||
.name = "pwm-ctrl",
|
||||
.owner = THIS_MODULE,
|
||||
#if defined(CONFIG_OF)
|
||||
.of_match_table = of_match_ptr(pwm_ctrl_dt),
|
||||
#endif
|
||||
},
|
||||
.probe = pwm_ctrl_probe,
|
||||
.remove = pwm_ctrl_remove,
|
||||
.suspend = pwm_ctrl_suspend,
|
||||
.resume = pwm_ctrl_resume,
|
||||
};
|
||||
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
static int __init pwm_ctrl_init(void)
|
||||
{
|
||||
return platform_driver_register(&pwm_ctrl_driver);
|
||||
}
|
||||
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
static void __exit pwm_ctrl_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&pwm_ctrl_driver);
|
||||
}
|
||||
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
module_init(pwm_ctrl_init);
|
||||
module_exit(pwm_ctrl_exit);
|
||||
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
MODULE_DESCRIPTION("PWM ctrl driver for odroid-Dev board");
|
||||
MODULE_AUTHOR("HardKernel");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
//[*]--------------------------------------------------------------------------------------------------[*]
|
||||
334
drivers/amlogic/pwm/pwm-meson.c
Normal file
334
drivers/amlogic/pwm/pwm-meson.c
Normal file
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* pwm-meson.c
|
||||
*
|
||||
* Support for Meson current source PWM
|
||||
*
|
||||
* Copyright (C) 2012 Elvis Yu <elvis.yu@amlogic.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation version 2.
|
||||
*
|
||||
* This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
|
||||
* whether express or implied; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/machine.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/slab.h>
|
||||
#include <mach/am_regs.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/pwm.h>
|
||||
|
||||
static int npwm=1;
|
||||
module_param(npwm,int,0644);
|
||||
MODULE_PARM_DESC(npwm,"\n odroid-c1 The number of available pwm (max 2-port)\n");
|
||||
|
||||
#define PWM_B 0
|
||||
#define PWM_E 1
|
||||
#define FIN_FREQ (24 * 1000)
|
||||
|
||||
struct meson_pwm_device {
|
||||
unsigned int freq;
|
||||
unsigned int duty;
|
||||
unsigned char pwm_id;
|
||||
struct pwm_device *pwm;
|
||||
};
|
||||
|
||||
struct meson_chip {
|
||||
struct platform_device *pdev;
|
||||
struct pwm_chip chip;
|
||||
struct meson_pwm_device *meson_pwm[2];
|
||||
struct pinctrl *pinctrl;
|
||||
};
|
||||
|
||||
#define to_meson_chip(chip) container_of(chip, struct meson_chip, chip)
|
||||
#define pwm_dbg(_pwm, msg...) dev_dbg(&(_pwm)->pdev->dev, msg)
|
||||
|
||||
static void meson_pwm_init(struct device *dev, int pwmn)
|
||||
{
|
||||
unsigned long flags;
|
||||
if(pwmn == 1) {
|
||||
local_irq_save(flags);
|
||||
aml_write_reg32(P_PWM_MISC_REG_AB, (aml_read_reg32(P_PWM_MISC_REG_AB) & ~(0x7f << 16)) | ((1 << 23)));
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
else {
|
||||
local_irq_save(flags);
|
||||
aml_write_reg32(P_PWM_MISC_REG_AB, (aml_read_reg32(P_PWM_MISC_REG_AB) & ~(0x7f << 16)) | ((1 << 23)));
|
||||
aml_write_reg32(P_PWM_MISC_REG_EF, (aml_read_reg32(P_PWM_MISC_REG_EF) & ~(0x7f << 8)) | ((1 << 15)));
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static int meson_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct meson_chip *meson = to_meson_chip(chip);
|
||||
struct meson_pwm_device *meson_pwm;
|
||||
unsigned int id = pwm->pwm;
|
||||
unsigned long flags;
|
||||
|
||||
meson_pwm = meson->meson_pwm[id];
|
||||
local_irq_save(flags);
|
||||
switch (id) {
|
||||
case PWM_B:
|
||||
aml_set_reg32_bits(P_PWM_MISC_REG_AB, 1, 1, 1); //enable pwm_b
|
||||
break;
|
||||
case PWM_E:
|
||||
aml_set_reg32_bits(P_PWM_MISC_REG_EF, 1, 0, 1); //enable pwm_e
|
||||
break;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm)
|
||||
{
|
||||
struct meson_chip *meson = to_meson_chip(chip);
|
||||
struct meson_pwm_device *meson_pwm;
|
||||
unsigned int id = pwm->pwm;
|
||||
unsigned long flags;
|
||||
|
||||
meson_pwm = meson->meson_pwm[id];
|
||||
local_irq_save(flags);
|
||||
switch (id) {
|
||||
case PWM_B:
|
||||
aml_set_reg32_bits(P_PWM_MISC_REG_AB, 0, 1, 1); //disable pwm_b
|
||||
break;
|
||||
case PWM_E:
|
||||
aml_set_reg32_bits(P_PWM_MISC_REG_EF, 0, 0, 1); //disable pwm_e
|
||||
break;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
return;
|
||||
}
|
||||
|
||||
static int meson_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm,
|
||||
int duty_percent, int pwm_freq)
|
||||
{
|
||||
struct meson_chip *meson = to_meson_chip(chip);
|
||||
struct meson_pwm_device *meson_pwm;
|
||||
unsigned int id = pwm->pwm;
|
||||
struct device *dev = chip->dev;
|
||||
unsigned pwm_hi = 0, pwm_lo = 0;
|
||||
unsigned fout_freq=0, pwm_cnt, pwm_pre_div;
|
||||
unsigned long flags=0;
|
||||
int i=0;
|
||||
|
||||
if((duty_percent<0)||(duty_percent>100)){
|
||||
dev_err(dev, "Not available duty_percent... error!!!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
meson_pwm = meson->meson_pwm[id];
|
||||
|
||||
fout_freq = ((pwm_freq >= (FIN_FREQ * 500)) ? (FIN_FREQ * 500) : pwm_freq);
|
||||
for (i=0; i<0x7f; i++) {
|
||||
pwm_pre_div = i;
|
||||
pwm_cnt = FIN_FREQ * 1000 / (pwm_freq * (pwm_pre_div + 1)) - 2;
|
||||
if (pwm_cnt <= 0xffff)
|
||||
break;
|
||||
}
|
||||
if(duty_percent==0) {
|
||||
pwm_hi=0;
|
||||
pwm_lo=pwm_cnt;
|
||||
goto div_set;
|
||||
}
|
||||
if(duty_percent==100) {
|
||||
pwm_hi=pwm_cnt;
|
||||
pwm_lo=0;
|
||||
goto div_set;
|
||||
}
|
||||
pwm_hi = (pwm_cnt*duty_percent)/100;
|
||||
pwm_lo = (pwm_cnt*(100-duty_percent))/100;
|
||||
|
||||
div_set :
|
||||
local_irq_save(flags);
|
||||
switch(id){
|
||||
case PWM_B:
|
||||
aml_set_reg32_bits(P_PWM_MISC_REG_AB, pwm_pre_div, 16, 7); //pwm_b_clk_div
|
||||
aml_write_reg32(P_PWM_PWM_B, (pwm_hi << 16) | (pwm_lo));
|
||||
break;
|
||||
case PWM_E:
|
||||
aml_set_reg32_bits(P_PWM_MISC_REG_EF, pwm_pre_div, 8, 7); //pwm_e_clk_div
|
||||
aml_write_reg32(P_PWM_PWM_E, (pwm_hi << 16) | (pwm_lo));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_pwm_request(struct pwm_chip *chip,
|
||||
struct pwm_device *pwm)
|
||||
{
|
||||
struct meson_chip *meson = to_meson_chip(chip);
|
||||
unsigned int id = pwm->pwm;
|
||||
|
||||
meson->meson_pwm[id] = devm_kzalloc(chip->dev, sizeof(struct meson_pwm_device), GFP_KERNEL);
|
||||
if (!meson->meson_pwm[id])
|
||||
return -ENOMEM;
|
||||
|
||||
meson->meson_pwm[id]->pwm_id = id;
|
||||
|
||||
meson->meson_pwm[id]->pwm = pwm;
|
||||
pwm_set_chip_data(pwm, meson->meson_pwm[id]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void meson_pwm_free(struct pwm_chip *chip,
|
||||
struct pwm_device *pwm)
|
||||
{
|
||||
struct meson_chip *meson = to_meson_chip(chip);
|
||||
unsigned int id = pwm->pwm;
|
||||
|
||||
if(meson->pinctrl){
|
||||
devm_pinctrl_put(meson->pinctrl);
|
||||
meson->pinctrl=NULL;
|
||||
}
|
||||
devm_kfree(chip->dev, meson->meson_pwm[id]);
|
||||
}
|
||||
|
||||
static struct pwm_ops meson_pwm_ops = {
|
||||
.request = meson_pwm_request,
|
||||
.free = meson_pwm_free,
|
||||
.enable = meson_pwm_enable,
|
||||
.disable = meson_pwm_disable,
|
||||
.config = meson_pwm_config,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int meson_pwm_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct meson_chip *meson;
|
||||
char prop_name[20];
|
||||
int ret=0;
|
||||
|
||||
if (!np) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if((npwm<=0)||(npwm>2)){
|
||||
dev_err(dev, "Available pwm_device number error.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
meson = devm_kzalloc(&pdev->dev, sizeof(*meson), GFP_KERNEL);
|
||||
if (meson == NULL) {
|
||||
dev_err(dev, "failed to allocate pwm_device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
meson->pdev = pdev;
|
||||
meson->chip.dev = &pdev->dev;
|
||||
meson->chip.ops = &meson_pwm_ops;
|
||||
meson->chip.base = -1;
|
||||
meson->chip.npwm = npwm;
|
||||
|
||||
ret = pwmchip_add(&meson->chip);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "failed to register pwm\n");
|
||||
return ret;
|
||||
}
|
||||
platform_set_drvdata(pdev, meson);
|
||||
|
||||
if(npwm == 2) strcpy(prop_name,"odroid_pwm1");
|
||||
else strcpy(prop_name,"odroid_pwm0");
|
||||
|
||||
meson->pinctrl = devm_pinctrl_get_select(&pdev->dev, prop_name);
|
||||
if(IS_ERR(meson->pinctrl)) {
|
||||
dev_err(&pdev->dev, "pinmux error\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dev_info(&pdev->dev, "pinctrl_name = %s\n", prop_name);
|
||||
|
||||
meson_pwm_init(dev, npwm);
|
||||
|
||||
dev_info(dev, "register pwm device.. %s\n",__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_pwm_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct meson_chip *meson = platform_get_drvdata(pdev);
|
||||
int err;
|
||||
|
||||
|
||||
err = pwmchip_remove(&meson->chip);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
devm_kfree(meson->chip.dev,meson);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int meson_pwm_suspend(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int meson_pwm_resume(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(meson_pwm_pm_ops, meson_pwm_suspend,
|
||||
meson_pwm_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id meson_pwm_of_match[]={
|
||||
{ .compatible = "amlogic, odroid-pwm", },
|
||||
{},
|
||||
};
|
||||
#else
|
||||
#define meson_pwm_of_match NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver meson_pwm_driver = {
|
||||
.driver = {
|
||||
.name = "meson_pwm",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = meson_pwm_of_match,
|
||||
.pm = &meson_pwm_pm_ops,
|
||||
},
|
||||
.probe = meson_pwm_probe,
|
||||
.remove = meson_pwm_remove,
|
||||
};
|
||||
|
||||
static int __init module_pwm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&meson_pwm_driver);
|
||||
if (ret)
|
||||
pr_err("failed to add pwm driver\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
static void __exit module_pwm_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&meson_pwm_driver);
|
||||
}
|
||||
module_init(module_pwm_init);
|
||||
module_exit(module_pwm_exit);
|
||||
|
||||
MODULE_DESCRIPTION("AMLogic meson8b PWM driver");
|
||||
MODULE_AUTHOR("HardKernel");
|
||||
MODULE_LICENSE("GPL");
|
||||
Reference in New Issue
Block a user