ODROIDC: Support hardware PWM(GPIOX_11, GPIOX_10)

Change-Id: Ie258b5c5da3999af743e7e8fdbe22d5d9ed562af
This commit is contained in:
ckkim
2014-12-30 16:39:06 +09:00
parent 35ff6937e3
commit 22d7f4eb0e
8 changed files with 827 additions and 4 deletions

34
arch/arm/boot/dts/meson8b_odroidc.dts Executable file → Normal file
View 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 / */

View File

@@ -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

View File

@@ -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

View File

@@ -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/

View 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.

View File

@@ -0,0 +1,2 @@
obj-$(CONFIG_MESON_PWM) += pwm-meson.o
obj-$(CONFIG_MESON_PWM_CTRL) += pwm-ctrl.o

View 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");
//[*]--------------------------------------------------------------------------------------------------[*]
//[*]--------------------------------------------------------------------------------------------------[*]

View 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");