2014-03-20 18:04:23 +04:00
/*
* Intel Low Power Subsystem PWM controller driver
*
* Copyright ( C ) 2014 , Intel Corporation
* Author : Mika Westerberg < mika . westerberg @ linux . intel . com >
* Author : Chew Kean Ho < kean . ho . chew @ intel . com >
* Author : Chang Rebecca Swee Fun < rebecca . swee . fun . chang @ intel . com >
* Author : Chew Chiau Ee < chiau . ee . chew @ intel . com >
2014-04-18 15:17:40 +04:00
* Author : Alan Cox < alan @ linux . intel . com >
2014-03-20 18:04:23 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
2014-08-23 02:22:45 +04:00
# include <linux/io.h>
2014-03-20 18:04:23 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
2014-04-18 15:17:40 +04:00
2014-08-19 20:17:35 +04:00
# include "pwm-lpss.h"
2014-03-20 18:04:23 +04:00
# define PWM 0x00000000
# define PWM_ENABLE BIT(31)
# define PWM_SW_UPDATE BIT(30)
# define PWM_BASE_UNIT_SHIFT 8
# define PWM_BASE_UNIT_MASK 0x00ffff00
# define PWM_ON_TIME_DIV_MASK 0x000000ff
# define PWM_DIVISION_CORRECTION 0x2
# define PWM_LIMIT (0x8000 + PWM_DIVISION_CORRECTION)
# define NSECS_PER_SEC 1000000000UL
struct pwm_lpss_chip {
struct pwm_chip chip ;
void __iomem * regs ;
2014-04-18 15:17:40 +04:00
unsigned long clk_rate ;
} ;
/* BayTrail */
2014-08-19 20:17:35 +04:00
const struct pwm_lpss_boardinfo pwm_lpss_byt_info = {
2014-08-23 15:20:25 +04:00
. clk_rate = 25000000
2014-03-20 18:04:23 +04:00
} ;
2014-08-19 20:17:35 +04:00
EXPORT_SYMBOL_GPL ( pwm_lpss_byt_info ) ;
2014-03-20 18:04:23 +04:00
2014-08-19 18:18:29 +04:00
/* Braswell */
2014-08-19 20:17:35 +04:00
const struct pwm_lpss_boardinfo pwm_lpss_bsw_info = {
2014-08-23 15:20:25 +04:00
. clk_rate = 19200000
2014-08-19 18:18:29 +04:00
} ;
2014-08-19 20:17:35 +04:00
EXPORT_SYMBOL_GPL ( pwm_lpss_bsw_info ) ;
2014-08-19 18:18:29 +04:00
2014-03-20 18:04:23 +04:00
static inline struct pwm_lpss_chip * to_lpwm ( struct pwm_chip * chip )
{
return container_of ( chip , struct pwm_lpss_chip , chip ) ;
}
static int pwm_lpss_config ( struct pwm_chip * chip , struct pwm_device * pwm ,
int duty_ns , int period_ns )
{
struct pwm_lpss_chip * lpwm = to_lpwm ( chip ) ;
u8 on_time_div ;
unsigned long c ;
unsigned long long base_unit , freq = NSECS_PER_SEC ;
u32 ctrl ;
do_div ( freq , period_ns ) ;
/* The equation is: base_unit = ((freq / c) * 65536) + correction */
base_unit = freq * 65536 ;
2014-04-18 15:17:40 +04:00
c = lpwm - > clk_rate ;
2014-03-20 18:04:23 +04:00
if ( ! c )
return - EINVAL ;
do_div ( base_unit , c ) ;
base_unit + = PWM_DIVISION_CORRECTION ;
if ( base_unit > PWM_LIMIT )
return - EINVAL ;
if ( duty_ns < = 0 )
duty_ns = 1 ;
on_time_div = 255 - ( 255 * duty_ns / period_ns ) ;
ctrl = readl ( lpwm - > regs + PWM ) ;
ctrl & = ~ ( PWM_BASE_UNIT_MASK | PWM_ON_TIME_DIV_MASK ) ;
ctrl | = ( u16 ) base_unit < < PWM_BASE_UNIT_SHIFT ;
ctrl | = on_time_div ;
/* request PWM to update on next cycle */
ctrl | = PWM_SW_UPDATE ;
writel ( ctrl , lpwm - > regs + PWM ) ;
return 0 ;
}
static int pwm_lpss_enable ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct pwm_lpss_chip * lpwm = to_lpwm ( chip ) ;
u32 ctrl ;
ctrl = readl ( lpwm - > regs + PWM ) ;
writel ( ctrl | PWM_ENABLE , lpwm - > regs + PWM ) ;
return 0 ;
}
static void pwm_lpss_disable ( struct pwm_chip * chip , struct pwm_device * pwm )
{
struct pwm_lpss_chip * lpwm = to_lpwm ( chip ) ;
u32 ctrl ;
ctrl = readl ( lpwm - > regs + PWM ) ;
writel ( ctrl & ~ PWM_ENABLE , lpwm - > regs + PWM ) ;
}
static const struct pwm_ops pwm_lpss_ops = {
. config = pwm_lpss_config ,
. enable = pwm_lpss_enable ,
. disable = pwm_lpss_disable ,
. owner = THIS_MODULE ,
} ;
2014-08-19 20:17:35 +04:00
struct pwm_lpss_chip * pwm_lpss_probe ( struct device * dev , struct resource * r ,
const struct pwm_lpss_boardinfo * info )
2014-03-20 18:04:23 +04:00
{
struct pwm_lpss_chip * lpwm ;
int ret ;
2014-04-18 15:17:40 +04:00
lpwm = devm_kzalloc ( dev , sizeof ( * lpwm ) , GFP_KERNEL ) ;
2014-03-20 18:04:23 +04:00
if ( ! lpwm )
2014-04-18 15:17:40 +04:00
return ERR_PTR ( - ENOMEM ) ;
2014-03-20 18:04:23 +04:00
2014-04-18 15:17:40 +04:00
lpwm - > regs = devm_ioremap_resource ( dev , r ) ;
2014-03-20 18:04:23 +04:00
if ( IS_ERR ( lpwm - > regs ) )
2014-05-07 12:27:57 +04:00
return ERR_CAST ( lpwm - > regs ) ;
2014-04-18 15:17:40 +04:00
2014-05-09 12:35:21 +04:00
lpwm - > clk_rate = info - > clk_rate ;
2014-04-18 15:17:40 +04:00
lpwm - > chip . dev = dev ;
2014-03-20 18:04:23 +04:00
lpwm - > chip . ops = & pwm_lpss_ops ;
lpwm - > chip . base = - 1 ;
lpwm - > chip . npwm = 1 ;
ret = pwmchip_add ( & lpwm - > chip ) ;
if ( ret ) {
2014-04-18 15:17:40 +04:00
dev_err ( dev , " failed to add PWM chip: %d \n " , ret ) ;
return ERR_PTR ( ret ) ;
2014-03-20 18:04:23 +04:00
}
2014-04-18 15:17:40 +04:00
return lpwm ;
2014-03-20 18:04:23 +04:00
}
2014-08-19 20:17:35 +04:00
EXPORT_SYMBOL_GPL ( pwm_lpss_probe ) ;
2014-03-20 18:04:23 +04:00
2014-08-19 20:17:35 +04:00
int pwm_lpss_remove ( struct pwm_lpss_chip * lpwm )
2014-03-20 18:04:23 +04:00
{
u32 ctrl ;
ctrl = readl ( lpwm - > regs + PWM ) ;
writel ( ctrl & ~ PWM_ENABLE , lpwm - > regs + PWM ) ;
return pwmchip_remove ( & lpwm - > chip ) ;
}
2014-08-19 20:17:35 +04:00
EXPORT_SYMBOL_GPL ( pwm_lpss_remove ) ;
2014-03-20 18:04:23 +04:00
MODULE_DESCRIPTION ( " PWM driver for Intel LPSS " ) ;
MODULE_AUTHOR ( " Mika Westerberg <mika.westerberg@linux.intel.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;