2018-05-16 11:50:40 +03:00
// SPDX-License-Identifier: GPL-2.0
2015-06-01 14:13:53 +03:00
/*
* Copyright ( c ) 2015 Endless Mobile , Inc .
* Author : Carlo Caione < carlo @ endlessm . com >
*
2018-02-19 14:21:39 +03:00
* Copyright ( c ) 2018 Baylibre , SAS .
* Author : Jerome Brunet < jbrunet @ baylibre . com >
2015-06-01 14:13:53 +03:00
*/
/*
* In the most basic form , a Meson PLL is composed as follows :
*
* PLL
2018-08-01 17:00:52 +03:00
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* | |
* | + - - + |
* in > > - - - - - [ / N ] - - - > | | + - - - - - + |
* | | | - - - - - - | DCO | - - - - > > out
* | + - - - - - - - - - > | | + - - v - - + |
* | | + - - + | |
* | | | |
* | + - - [ * ( M + ( F / Fmax ) ] < - - + |
* | |
* + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2015-06-01 14:13:53 +03:00
*
2018-08-01 17:00:52 +03:00
* out = in * ( m + frac / frac_max ) / n
2015-06-01 14:13:53 +03:00
*/
# include <linux/clk-provider.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/io.h>
2018-01-19 18:55:23 +03:00
# include <linux/math64.h>
2015-06-01 14:13:53 +03:00
# include <linux/module.h>
clk: meson: rework and clean drivers dependencies
Initially, the meson clock directory only hosted 2 controllers drivers,
for meson8 and gxbb. At the time, both used the same set of clock drivers
so managing the dependencies was not a big concern.
Since this ancient time, entropy did its job, controllers with different
requirement and specific clock drivers have been added. Unfortunately, we
did not do a great job at managing the dependencies between the
controllers and the different clock drivers. Some drivers, such as
clk-phase or vid-pll-div, are compiled even if they are useless on the
target (meson8). As we are adding new controllers, we need to be able to
pick a driver w/o pulling the whole thing.
The patch aims to clean things up by:
* providing a dedicated CONFIG_ for each clock drivers
* allowing clock drivers to be compiled as a modules, if possible
* stating explicitly which drivers are required by each controller.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Link: https://lkml.kernel.org/r/20190201125841.26785-5-jbrunet@baylibre.com
2019-02-01 15:58:41 +03:00
# include "clk-regmap.h"
# include "clk-pll.h"
2015-06-01 14:13:53 +03:00
2018-02-12 17:58:42 +03:00
static inline struct meson_clk_pll_data *
meson_clk_pll_data ( struct clk_regmap * clk )
{
return ( struct meson_clk_pll_data * ) clk - > data ;
}
2015-06-01 14:13:53 +03:00
2019-02-01 17:53:42 +03:00
static int __pll_round_closest_mult ( struct meson_clk_pll_data * pll )
{
if ( ( pll - > flags & CLK_MESON_PLL_ROUND_CLOSEST ) & &
! MESON_PARM_APPLICABLE ( & pll - > frac ) )
return 1 ;
return 0 ;
}
2018-02-19 14:21:39 +03:00
static unsigned long __pll_params_to_rate ( unsigned long parent_rate ,
2019-02-01 17:53:42 +03:00
unsigned int m , unsigned int n ,
unsigned int frac ,
2018-02-19 14:21:39 +03:00
struct meson_clk_pll_data * pll )
{
2019-02-01 17:53:42 +03:00
u64 rate = ( u64 ) parent_rate * m ;
2018-02-19 14:21:39 +03:00
if ( frac & & MESON_PARM_APPLICABLE ( & pll - > frac ) ) {
u64 frac_rate = ( u64 ) parent_rate * frac ;
rate + = DIV_ROUND_UP_ULL ( frac_rate ,
( 1 < < pll - > frac . width ) ) ;
}
2019-02-01 17:53:42 +03:00
return DIV_ROUND_UP_ULL ( rate , n ) ;
2018-02-19 14:21:39 +03:00
}
2015-06-01 14:13:53 +03:00
static unsigned long meson_clk_pll_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
2018-02-12 17:58:42 +03:00
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_pll_data * pll = meson_clk_pll_data ( clk ) ;
2019-02-01 17:53:42 +03:00
unsigned int m , n , frac ;
2015-06-01 14:13:53 +03:00
2019-02-01 17:53:42 +03:00
n = meson_parm_read ( clk - > map , & pll - > n ) ;
2019-12-15 14:47:05 +03:00
/*
* On some HW , N is set to zero on init . This value is invalid as
* it would result in a division by zero . The rate can ' t be
* calculated in this case
*/
if ( n = = 0 )
return 0 ;
2019-02-01 17:53:42 +03:00
m = meson_parm_read ( clk - > map , & pll - > m ) ;
2015-06-01 14:13:53 +03:00
2018-02-19 14:21:39 +03:00
frac = MESON_PARM_APPLICABLE ( & pll - > frac ) ?
meson_parm_read ( clk - > map , & pll - > frac ) :
0 ;
2018-01-19 18:55:23 +03:00
2019-02-01 17:53:42 +03:00
return __pll_params_to_rate ( parent_rate , m , n , frac , pll ) ;
2015-06-01 14:13:53 +03:00
}
2019-02-01 17:53:42 +03:00
static unsigned int __pll_params_with_frac ( unsigned long rate ,
unsigned long parent_rate ,
unsigned int m ,
unsigned int n ,
struct meson_clk_pll_data * pll )
2015-06-01 14:13:53 +03:00
{
2019-02-01 17:53:42 +03:00
unsigned int frac_max = ( 1 < < pll - > frac . width ) ;
u64 val = ( u64 ) rate * n ;
/* Bail out if we are already over the requested rate */
if ( rate < parent_rate * m / n )
return 0 ;
2018-01-19 18:55:21 +03:00
2018-02-19 14:21:41 +03:00
if ( pll - > flags & CLK_MESON_PLL_ROUND_CLOSEST )
val = DIV_ROUND_CLOSEST_ULL ( val * frac_max , parent_rate ) ;
else
val = div_u64 ( val * frac_max , parent_rate ) ;
2019-02-01 17:53:42 +03:00
val - = m * frac_max ;
2015-06-01 14:13:53 +03:00
2019-02-01 17:53:42 +03:00
return min ( ( unsigned int ) val , ( frac_max - 1 ) ) ;
2015-06-01 14:13:53 +03:00
}
2018-08-01 17:00:53 +03:00
static bool meson_clk_pll_is_better ( unsigned long rate ,
unsigned long best ,
unsigned long now ,
struct meson_clk_pll_data * pll )
{
2019-02-01 17:53:42 +03:00
if ( __pll_round_closest_mult ( pll ) ) {
2018-08-01 17:00:53 +03:00
/* Round Closest */
if ( abs ( now - rate ) < abs ( best - rate ) )
return true ;
2019-02-01 17:53:42 +03:00
} else {
/* Round down */
2019-03-24 19:43:27 +03:00
if ( now < = rate & & best < now )
2019-02-01 17:53:42 +03:00
return true ;
2018-08-01 17:00:53 +03:00
}
return false ;
}
2019-02-01 17:53:42 +03:00
static int meson_clk_get_pll_table_index ( unsigned int index ,
unsigned int * m ,
unsigned int * n ,
struct meson_clk_pll_data * pll )
2015-06-01 14:13:53 +03:00
{
2019-02-01 17:53:42 +03:00
if ( ! pll - > table [ index ] . n )
return - EINVAL ;
* m = pll - > table [ index ] . m ;
* n = pll - > table [ index ] . n ;
return 0 ;
}
static unsigned int meson_clk_get_pll_range_m ( unsigned long rate ,
unsigned long parent_rate ,
unsigned int n ,
struct meson_clk_pll_data * pll )
{
u64 val = ( u64 ) rate * n ;
if ( __pll_round_closest_mult ( pll ) )
return DIV_ROUND_CLOSEST_ULL ( val , parent_rate ) ;
2015-06-01 14:13:53 +03:00
2019-02-01 17:53:42 +03:00
return div_u64 ( val , parent_rate ) ;
}
static int meson_clk_get_pll_range_index ( unsigned long rate ,
unsigned long parent_rate ,
unsigned int index ,
unsigned int * m ,
unsigned int * n ,
struct meson_clk_pll_data * pll )
{
* n = index + 1 ;
/* Check the predivider range */
if ( * n > = ( 1 < < pll - > n . width ) )
return - EINVAL ;
if ( * n = = 1 ) {
/* Get the boundaries out the way */
if ( rate < = pll - > range - > min * parent_rate ) {
* m = pll - > range - > min ;
return - ENODATA ;
} else if ( rate > = pll - > range - > max * parent_rate ) {
* m = pll - > range - > max ;
return - ENODATA ;
}
}
* m = meson_clk_get_pll_range_m ( rate , parent_rate , * n , pll ) ;
/* the pre-divider gives a multiplier too big - stop */
if ( * m > = ( 1 < < pll - > m . width ) )
return - EINVAL ;
return 0 ;
}
static int meson_clk_get_pll_get_index ( unsigned long rate ,
unsigned long parent_rate ,
unsigned int index ,
unsigned int * m ,
unsigned int * n ,
struct meson_clk_pll_data * pll )
{
if ( pll - > range )
return meson_clk_get_pll_range_index ( rate , parent_rate ,
index , m , n , pll ) ;
else if ( pll - > table )
return meson_clk_get_pll_table_index ( index , m , n , pll ) ;
return - EINVAL ;
}
2018-01-19 18:55:21 +03:00
2019-02-01 17:53:42 +03:00
static int meson_clk_get_pll_settings ( unsigned long rate ,
unsigned long parent_rate ,
unsigned int * best_m ,
unsigned int * best_n ,
struct meson_clk_pll_data * pll )
{
unsigned long best = 0 , now = 0 ;
unsigned int i , m , n ;
int ret ;
for ( i = 0 , ret = 0 ; ! ret ; i + + ) {
ret = meson_clk_get_pll_get_index ( rate , parent_rate ,
i , & m , & n , pll ) ;
if ( ret = = - EINVAL )
break ;
2018-02-19 14:21:39 +03:00
2019-02-01 17:53:42 +03:00
now = __pll_params_to_rate ( parent_rate , m , n , 0 , pll ) ;
if ( meson_clk_pll_is_better ( rate , best , now , pll ) ) {
2018-08-01 17:00:53 +03:00
best = now ;
2019-02-01 17:53:42 +03:00
* best_m = m ;
* best_n = n ;
if ( now = = rate )
break ;
2018-08-01 17:00:53 +03:00
}
2018-02-19 14:21:41 +03:00
}
2018-02-19 14:21:39 +03:00
2019-02-01 17:53:42 +03:00
return best ? 0 : - EINVAL ;
2018-02-19 14:21:39 +03:00
}
2021-05-17 23:37:24 +03:00
static int meson_clk_pll_determine_rate ( struct clk_hw * hw ,
struct clk_rate_request * req )
2018-02-19 14:21:39 +03:00
{
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_pll_data * pll = meson_clk_pll_data ( clk ) ;
2019-02-01 17:53:42 +03:00
unsigned int m , n , frac ;
2018-08-01 17:00:53 +03:00
unsigned long round ;
2019-02-01 17:53:42 +03:00
int ret ;
2018-02-19 14:21:39 +03:00
2021-05-17 23:37:24 +03:00
ret = meson_clk_get_pll_settings ( req - > rate , req - > best_parent_rate ,
& m , & n , pll ) ;
2019-02-01 17:53:42 +03:00
if ( ret )
2021-05-17 23:37:24 +03:00
return ret ;
2018-02-19 14:21:39 +03:00
2021-05-17 23:37:24 +03:00
round = __pll_params_to_rate ( req - > best_parent_rate , m , n , 0 , pll ) ;
2018-08-01 17:00:53 +03:00
2021-05-17 23:37:24 +03:00
if ( ! MESON_PARM_APPLICABLE ( & pll - > frac ) | | req - > rate = = round ) {
req - > rate = round ;
return 0 ;
}
2018-02-12 17:58:42 +03:00
2018-02-19 14:21:39 +03:00
/*
* The rate provided by the setting is not an exact match , let ' s
* try to improve the result using the fractional parameter
*/
2021-05-17 23:37:24 +03:00
frac = __pll_params_with_frac ( req - > rate , req - > best_parent_rate , m , n , pll ) ;
req - > rate = __pll_params_to_rate ( req - > best_parent_rate , m , n , frac , pll ) ;
2018-02-19 14:21:39 +03:00
2021-05-17 23:37:24 +03:00
return 0 ;
2015-06-01 14:13:53 +03:00
}
2018-02-12 17:58:42 +03:00
static int meson_clk_pll_wait_lock ( struct clk_hw * hw )
2017-03-22 13:32:23 +03:00
{
2018-02-12 17:58:42 +03:00
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_pll_data * pll = meson_clk_pll_data ( clk ) ;
2022-08-29 21:52:59 +03:00
int delay = 5000 ;
2018-02-12 17:58:42 +03:00
do {
2022-08-29 21:52:59 +03:00
/* Is the clock locked now ? Time out after 100ms. */
2018-02-12 17:58:42 +03:00
if ( meson_parm_read ( clk - > map , & pll - > l ) )
2017-03-22 13:32:23 +03:00
return 0 ;
2022-08-29 21:52:59 +03:00
udelay ( 20 ) ;
} while ( - - delay ) ;
2018-02-12 17:58:42 +03:00
2015-06-01 14:13:53 +03:00
return - ETIMEDOUT ;
}
2019-09-24 15:39:53 +03:00
static int meson_clk_pll_init ( struct clk_hw * hw )
2017-03-22 13:32:23 +03:00
{
2018-02-12 17:58:42 +03:00
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_pll_data * pll = meson_clk_pll_data ( clk ) ;
if ( pll - > init_count ) {
meson_parm_write ( clk - > map , & pll - > rst , 1 ) ;
regmap_multi_reg_write ( clk - > map , pll - > init_regs ,
pll - > init_count ) ;
meson_parm_write ( clk - > map , & pll - > rst , 0 ) ;
}
2019-09-24 15:39:53 +03:00
return 0 ;
2017-03-22 13:32:23 +03:00
}
2018-11-16 01:40:43 +03:00
static int meson_clk_pll_is_enabled ( struct clk_hw * hw )
{
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_pll_data * pll = meson_clk_pll_data ( clk ) ;
if ( meson_parm_read ( clk - > map , & pll - > rst ) | |
! meson_parm_read ( clk - > map , & pll - > en ) | |
! meson_parm_read ( clk - > map , & pll - > l ) )
return 0 ;
return 1 ;
}
2019-03-07 17:14:53 +03:00
static int meson_clk_pcie_pll_enable ( struct clk_hw * hw )
{
2022-08-15 00:25:31 +03:00
int retries = 10 ;
2019-03-07 17:14:53 +03:00
2022-08-15 00:25:31 +03:00
do {
meson_clk_pll_init ( hw ) ;
if ( ! meson_clk_pll_wait_lock ( hw ) )
return 0 ;
pr_info ( " Retry enabling PCIe PLL clock \n " ) ;
} while ( - - retries ) ;
2019-03-07 17:14:53 +03:00
2022-08-15 00:25:31 +03:00
return - EIO ;
2019-03-07 17:14:53 +03:00
}
2018-08-01 17:00:50 +03:00
static int meson_clk_pll_enable ( struct clk_hw * hw )
{
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_pll_data * pll = meson_clk_pll_data ( clk ) ;
2018-11-16 01:40:43 +03:00
/* do nothing if the PLL is already enabled */
if ( clk_hw_is_enabled ( hw ) )
return 0 ;
2018-08-01 17:00:50 +03:00
/* Make sure the pll is in reset */
meson_parm_write ( clk - > map , & pll - > rst , 1 ) ;
/* Enable the pll */
meson_parm_write ( clk - > map , & pll - > en , 1 ) ;
/* Take the pll out reset */
meson_parm_write ( clk - > map , & pll - > rst , 0 ) ;
if ( meson_clk_pll_wait_lock ( hw ) )
return - EIO ;
return 0 ;
}
static void meson_clk_pll_disable ( struct clk_hw * hw )
{
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_pll_data * pll = meson_clk_pll_data ( clk ) ;
/* Put the pll is in reset */
meson_parm_write ( clk - > map , & pll - > rst , 1 ) ;
/* Disable the pll */
meson_parm_write ( clk - > map , & pll - > en , 0 ) ;
}
2015-06-01 14:13:53 +03:00
static int meson_clk_pll_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
2018-02-12 17:58:42 +03:00
struct clk_regmap * clk = to_clk_regmap ( hw ) ;
struct meson_clk_pll_data * pll = meson_clk_pll_data ( clk ) ;
2020-12-26 15:15:55 +03:00
unsigned int enabled , m , n , frac = 0 ;
2015-06-01 14:13:53 +03:00
unsigned long old_rate ;
2020-12-26 15:15:55 +03:00
int ret ;
2015-06-01 14:13:53 +03:00
if ( parent_rate = = 0 | | rate = = 0 )
return - EINVAL ;
2020-12-26 15:15:54 +03:00
old_rate = clk_hw_get_rate ( hw ) ;
2015-06-01 14:13:53 +03:00
2019-02-01 17:53:42 +03:00
ret = meson_clk_get_pll_settings ( rate , parent_rate , & m , & n , pll ) ;
if ( ret )
return ret ;
2015-06-01 14:13:53 +03:00
2018-08-01 17:00:50 +03:00
enabled = meson_parm_read ( clk - > map , & pll - > en ) ;
if ( enabled )
meson_clk_pll_disable ( hw ) ;
2016-06-07 04:08:15 +03:00
2019-02-01 17:53:42 +03:00
meson_parm_write ( clk - > map , & pll - > n , n ) ;
meson_parm_write ( clk - > map , & pll - > m , m ) ;
2018-02-12 17:58:42 +03:00
2018-02-19 14:21:39 +03:00
if ( MESON_PARM_APPLICABLE ( & pll - > frac ) ) {
2019-02-01 17:53:42 +03:00
frac = __pll_params_with_frac ( rate , parent_rate , m , n , pll ) ;
2018-02-19 14:21:39 +03:00
meson_parm_write ( clk - > map , & pll - > frac , frac ) ;
}
2018-02-12 17:58:42 +03:00
2018-08-01 17:00:50 +03:00
/* If the pll is stopped, bail out now */
if ( ! enabled )
return 0 ;
2017-03-22 13:32:23 +03:00
2020-12-26 15:15:56 +03:00
ret = meson_clk_pll_enable ( hw ) ;
if ( ret ) {
2015-06-01 14:13:53 +03:00
pr_warn ( " %s: pll did not lock, trying to restore old rate %lu \n " ,
__func__ , old_rate ) ;
2018-02-12 17:58:42 +03:00
/*
* FIXME : Do we really need / want this HACK ?
* It looks unsafe . what happens if the clock gets into a
* broken state and we can ' t lock back on the old_rate ? Looks
* like an infinite recursion is possible
*/
2015-06-01 14:13:53 +03:00
meson_clk_pll_set_rate ( hw , old_rate , parent_rate ) ;
}
2020-12-26 15:15:56 +03:00
return ret ;
2015-06-01 14:13:53 +03:00
}
2019-03-07 17:14:53 +03:00
/*
* The Meson G12A PCIE PLL is fined tuned to deliver a very precise
* 100 MHz reference clock for the PCIe Analog PHY , and thus requires
* a strict register sequence to enable the PLL .
* To simplify , re - use the _init ( ) op to enable the PLL and keep
* the other ops except set_rate since the rate is fixed .
*/
const struct clk_ops meson_clk_pcie_pll_ops = {
. recalc_rate = meson_clk_pll_recalc_rate ,
2021-05-17 23:37:24 +03:00
. determine_rate = meson_clk_pll_determine_rate ,
2019-03-07 17:14:53 +03:00
. is_enabled = meson_clk_pll_is_enabled ,
. enable = meson_clk_pcie_pll_enable ,
. disable = meson_clk_pll_disable
} ;
EXPORT_SYMBOL_GPL ( meson_clk_pcie_pll_ops ) ;
2016-04-28 22:01:42 +03:00
const struct clk_ops meson_clk_pll_ops = {
2018-02-12 17:58:42 +03:00
. init = meson_clk_pll_init ,
2015-06-01 14:13:53 +03:00
. recalc_rate = meson_clk_pll_recalc_rate ,
2021-05-17 23:37:24 +03:00
. determine_rate = meson_clk_pll_determine_rate ,
2015-06-01 14:13:53 +03:00
. set_rate = meson_clk_pll_set_rate ,
2018-11-16 01:40:43 +03:00
. is_enabled = meson_clk_pll_is_enabled ,
2018-08-01 17:00:50 +03:00
. enable = meson_clk_pll_enable ,
. disable = meson_clk_pll_disable
2015-06-01 14:13:53 +03:00
} ;
clk: meson: rework and clean drivers dependencies
Initially, the meson clock directory only hosted 2 controllers drivers,
for meson8 and gxbb. At the time, both used the same set of clock drivers
so managing the dependencies was not a big concern.
Since this ancient time, entropy did its job, controllers with different
requirement and specific clock drivers have been added. Unfortunately, we
did not do a great job at managing the dependencies between the
controllers and the different clock drivers. Some drivers, such as
clk-phase or vid-pll-div, are compiled even if they are useless on the
target (meson8). As we are adding new controllers, we need to be able to
pick a driver w/o pulling the whole thing.
The patch aims to clean things up by:
* providing a dedicated CONFIG_ for each clock drivers
* allowing clock drivers to be compiled as a modules, if possible
* stating explicitly which drivers are required by each controller.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Link: https://lkml.kernel.org/r/20190201125841.26785-5-jbrunet@baylibre.com
2019-02-01 15:58:41 +03:00
EXPORT_SYMBOL_GPL ( meson_clk_pll_ops ) ;
2015-06-01 14:13:53 +03:00
2016-04-28 22:01:42 +03:00
const struct clk_ops meson_clk_pll_ro_ops = {
2015-06-01 14:13:53 +03:00
. recalc_rate = meson_clk_pll_recalc_rate ,
2018-11-16 01:40:43 +03:00
. is_enabled = meson_clk_pll_is_enabled ,
2015-06-01 14:13:53 +03:00
} ;
clk: meson: rework and clean drivers dependencies
Initially, the meson clock directory only hosted 2 controllers drivers,
for meson8 and gxbb. At the time, both used the same set of clock drivers
so managing the dependencies was not a big concern.
Since this ancient time, entropy did its job, controllers with different
requirement and specific clock drivers have been added. Unfortunately, we
did not do a great job at managing the dependencies between the
controllers and the different clock drivers. Some drivers, such as
clk-phase or vid-pll-div, are compiled even if they are useless on the
target (meson8). As we are adding new controllers, we need to be able to
pick a driver w/o pulling the whole thing.
The patch aims to clean things up by:
* providing a dedicated CONFIG_ for each clock drivers
* allowing clock drivers to be compiled as a modules, if possible
* stating explicitly which drivers are required by each controller.
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Link: https://lkml.kernel.org/r/20190201125841.26785-5-jbrunet@baylibre.com
2019-02-01 15:58:41 +03:00
EXPORT_SYMBOL_GPL ( meson_clk_pll_ro_ops ) ;
MODULE_DESCRIPTION ( " Amlogic PLL driver " ) ;
MODULE_AUTHOR ( " Carlo Caione <carlo@endlessm.com> " ) ;
MODULE_AUTHOR ( " Jerome Brunet <jbrunet@baylibre.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;