2011-03-08 22:01:39 -08:00
/*
* Hardware monitoring driver for UCD90xxx Sequencer and System Health
* Controller series
*
* Copyright ( C ) 2011 Ericsson AB .
*
* 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 . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/kernel.h>
# include <linux/module.h>
2017-02-24 10:13:07 -03:00
# include <linux/of_device.h>
2011-03-08 22:01:39 -08:00
# include <linux/init.h>
# include <linux/err.h>
# include <linux/slab.h>
# include <linux/i2c.h>
2017-05-21 22:34:43 +02:00
# include <linux/pmbus.h>
2018-03-16 16:22:05 -05:00
# include <linux/gpio.h>
# include <linux/gpio/driver.h>
2011-03-08 22:01:39 -08:00
# include "pmbus.h"
2016-08-19 21:08:45 -05:00
enum chips { ucd9000 , ucd90120 , ucd90124 , ucd90160 , ucd9090 , ucd90910 } ;
2011-03-08 22:01:39 -08:00
# define UCD9000_MONITOR_CONFIG 0xd5
# define UCD9000_NUM_PAGES 0xd6
# define UCD9000_FAN_CONFIG_INDEX 0xe7
# define UCD9000_FAN_CONFIG 0xe8
2018-03-16 16:22:05 -05:00
# define UCD9000_GPIO_SELECT 0xfa
# define UCD9000_GPIO_CONFIG 0xfb
2011-03-08 22:01:39 -08:00
# define UCD9000_DEVICE_ID 0xfd
2018-03-16 16:22:05 -05:00
/* GPIO CONFIG bits */
# define UCD9000_GPIO_CONFIG_ENABLE BIT(0)
# define UCD9000_GPIO_CONFIG_OUT_ENABLE BIT(1)
# define UCD9000_GPIO_CONFIG_OUT_VALUE BIT(2)
# define UCD9000_GPIO_CONFIG_STATUS BIT(3)
# define UCD9000_GPIO_INPUT 0
# define UCD9000_GPIO_OUTPUT 1
2011-03-08 22:01:39 -08:00
# define UCD9000_MON_TYPE(x) (((x) >> 5) & 0x07)
# define UCD9000_MON_PAGE(x) ((x) & 0x0f)
# define UCD9000_MON_VOLTAGE 1
# define UCD9000_MON_TEMPERATURE 2
# define UCD9000_MON_CURRENT 3
# define UCD9000_MON_VOLTAGE_HW 4
# define UCD9000_NUM_FAN 4
2018-03-16 16:22:05 -05:00
# define UCD9000_GPIO_NAME_LEN 16
# define UCD9090_NUM_GPIOS 23
# define UCD901XX_NUM_GPIOS 26
# define UCD90910_NUM_GPIOS 26
2011-03-08 22:01:39 -08:00
struct ucd9000_data {
u8 fan_data [ UCD9000_NUM_FAN ] [ I2C_SMBUS_BLOCK_MAX ] ;
struct pmbus_driver_info info ;
2018-03-16 16:22:05 -05:00
# ifdef CONFIG_GPIOLIB
struct gpio_chip gpio ;
# endif
2011-03-08 22:01:39 -08:00
} ;
# define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info)
static int ucd9000_get_fan_config ( struct i2c_client * client , int fan )
{
int fan_config = 0 ;
struct ucd9000_data * data
= to_ucd9000_data ( pmbus_get_driver_info ( client ) ) ;
if ( data - > fan_data [ fan ] [ 3 ] & 1 )
fan_config | = PB_FAN_2_INSTALLED ; /* Use lower bit position */
/* Pulses/revolution */
fan_config | = ( data - > fan_data [ fan ] [ 3 ] & 0x06 ) > > 1 ;
return fan_config ;
}
static int ucd9000_read_byte_data ( struct i2c_client * client , int page , int reg )
{
int ret = 0 ;
int fan_config ;
switch ( reg ) {
case PMBUS_FAN_CONFIG_12 :
2011-07-29 22:19:39 -07:00
if ( page > 0 )
2011-09-01 08:34:31 -07:00
return - ENXIO ;
2011-03-08 22:01:39 -08:00
ret = ucd9000_get_fan_config ( client , 0 ) ;
if ( ret < 0 )
return ret ;
fan_config = ret < < 4 ;
ret = ucd9000_get_fan_config ( client , 1 ) ;
if ( ret < 0 )
return ret ;
fan_config | = ret ;
ret = fan_config ;
break ;
case PMBUS_FAN_CONFIG_34 :
2011-07-29 22:19:39 -07:00
if ( page > 0 )
2011-09-01 08:34:31 -07:00
return - ENXIO ;
2011-03-08 22:01:39 -08:00
ret = ucd9000_get_fan_config ( client , 2 ) ;
if ( ret < 0 )
return ret ;
fan_config = ret < < 4 ;
ret = ucd9000_get_fan_config ( client , 3 ) ;
if ( ret < 0 )
return ret ;
fan_config | = ret ;
ret = fan_config ;
break ;
default :
ret = - ENODATA ;
break ;
}
return ret ;
}
static const struct i2c_device_id ucd9000_id [ ] = {
{ " ucd9000 " , ucd9000 } ,
{ " ucd90120 " , ucd90120 } ,
{ " ucd90124 " , ucd90124 } ,
2016-08-19 21:08:45 -05:00
{ " ucd90160 " , ucd90160 } ,
2011-03-08 22:01:39 -08:00
{ " ucd9090 " , ucd9090 } ,
{ " ucd90910 " , ucd90910 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ucd9000_id ) ;
2017-02-24 10:13:07 -03:00
static const struct of_device_id ucd9000_of_match [ ] = {
{
. compatible = " ti,ucd9000 " ,
. data = ( void * ) ucd9000
} ,
{
. compatible = " ti,ucd90120 " ,
. data = ( void * ) ucd90120
} ,
{
. compatible = " ti,ucd90124 " ,
. data = ( void * ) ucd90124
} ,
{
. compatible = " ti,ucd90160 " ,
. data = ( void * ) ucd90160
} ,
{
. compatible = " ti,ucd9090 " ,
. data = ( void * ) ucd9090
} ,
{
. compatible = " ti,ucd90910 " ,
. data = ( void * ) ucd90910
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , ucd9000_of_match ) ;
2018-03-16 16:22:05 -05:00
# ifdef CONFIG_GPIOLIB
static int ucd9000_gpio_read_config ( struct i2c_client * client ,
unsigned int offset )
{
int ret ;
/* No page set required */
ret = i2c_smbus_write_byte_data ( client , UCD9000_GPIO_SELECT , offset ) ;
if ( ret < 0 )
return ret ;
return i2c_smbus_read_byte_data ( client , UCD9000_GPIO_CONFIG ) ;
}
static int ucd9000_gpio_get ( struct gpio_chip * gc , unsigned int offset )
{
struct i2c_client * client = gpiochip_get_data ( gc ) ;
int ret ;
ret = ucd9000_gpio_read_config ( client , offset ) ;
if ( ret < 0 )
return ret ;
return ! ! ( ret & UCD9000_GPIO_CONFIG_STATUS ) ;
}
static void ucd9000_gpio_set ( struct gpio_chip * gc , unsigned int offset ,
int value )
{
struct i2c_client * client = gpiochip_get_data ( gc ) ;
int ret ;
ret = ucd9000_gpio_read_config ( client , offset ) ;
if ( ret < 0 ) {
dev_dbg ( & client - > dev , " failed to read GPIO %d config: %d \n " ,
offset , ret ) ;
return ;
}
if ( value ) {
if ( ret & UCD9000_GPIO_CONFIG_STATUS )
return ;
ret | = UCD9000_GPIO_CONFIG_STATUS ;
} else {
if ( ! ( ret & UCD9000_GPIO_CONFIG_STATUS ) )
return ;
ret & = ~ UCD9000_GPIO_CONFIG_STATUS ;
}
ret | = UCD9000_GPIO_CONFIG_ENABLE ;
/* Page set not required */
ret = i2c_smbus_write_byte_data ( client , UCD9000_GPIO_CONFIG , ret ) ;
if ( ret < 0 ) {
dev_dbg ( & client - > dev , " Failed to write GPIO %d config: %d \n " ,
offset , ret ) ;
return ;
}
ret & = ~ UCD9000_GPIO_CONFIG_ENABLE ;
ret = i2c_smbus_write_byte_data ( client , UCD9000_GPIO_CONFIG , ret ) ;
if ( ret < 0 )
dev_dbg ( & client - > dev , " Failed to write GPIO %d config: %d \n " ,
offset , ret ) ;
}
static int ucd9000_gpio_get_direction ( struct gpio_chip * gc ,
unsigned int offset )
{
struct i2c_client * client = gpiochip_get_data ( gc ) ;
int ret ;
ret = ucd9000_gpio_read_config ( client , offset ) ;
if ( ret < 0 )
return ret ;
return ! ( ret & UCD9000_GPIO_CONFIG_OUT_ENABLE ) ;
}
static int ucd9000_gpio_set_direction ( struct gpio_chip * gc ,
unsigned int offset , bool direction_out ,
int requested_out )
{
struct i2c_client * client = gpiochip_get_data ( gc ) ;
int ret , config , out_val ;
ret = ucd9000_gpio_read_config ( client , offset ) ;
if ( ret < 0 )
return ret ;
if ( direction_out ) {
out_val = requested_out ? UCD9000_GPIO_CONFIG_OUT_VALUE : 0 ;
if ( ret & UCD9000_GPIO_CONFIG_OUT_ENABLE ) {
if ( ( ret & UCD9000_GPIO_CONFIG_OUT_VALUE ) = = out_val )
return 0 ;
} else {
ret | = UCD9000_GPIO_CONFIG_OUT_ENABLE ;
}
if ( out_val )
ret | = UCD9000_GPIO_CONFIG_OUT_VALUE ;
else
ret & = ~ UCD9000_GPIO_CONFIG_OUT_VALUE ;
} else {
if ( ! ( ret & UCD9000_GPIO_CONFIG_OUT_ENABLE ) )
return 0 ;
ret & = ~ UCD9000_GPIO_CONFIG_OUT_ENABLE ;
}
ret | = UCD9000_GPIO_CONFIG_ENABLE ;
config = ret ;
/* Page set not required */
ret = i2c_smbus_write_byte_data ( client , UCD9000_GPIO_CONFIG , config ) ;
if ( ret < 0 )
return ret ;
config & = ~ UCD9000_GPIO_CONFIG_ENABLE ;
return i2c_smbus_write_byte_data ( client , UCD9000_GPIO_CONFIG , config ) ;
}
static int ucd9000_gpio_direction_input ( struct gpio_chip * gc ,
unsigned int offset )
{
return ucd9000_gpio_set_direction ( gc , offset , UCD9000_GPIO_INPUT , 0 ) ;
}
static int ucd9000_gpio_direction_output ( struct gpio_chip * gc ,
unsigned int offset , int val )
{
return ucd9000_gpio_set_direction ( gc , offset , UCD9000_GPIO_OUTPUT ,
val ) ;
}
static void ucd9000_probe_gpio ( struct i2c_client * client ,
const struct i2c_device_id * mid ,
struct ucd9000_data * data )
{
int rc ;
switch ( mid - > driver_data ) {
case ucd9090 :
data - > gpio . ngpio = UCD9090_NUM_GPIOS ;
break ;
case ucd90120 :
case ucd90124 :
case ucd90160 :
data - > gpio . ngpio = UCD901XX_NUM_GPIOS ;
break ;
case ucd90910 :
data - > gpio . ngpio = UCD90910_NUM_GPIOS ;
break ;
default :
return ; /* GPIO support is optional. */
}
/*
* Pinmux support has not been added to the new gpio_chip .
* This support should be added when possible given the mux
* behavior of these IO devices .
*/
data - > gpio . label = client - > name ;
data - > gpio . get_direction = ucd9000_gpio_get_direction ;
data - > gpio . direction_input = ucd9000_gpio_direction_input ;
data - > gpio . direction_output = ucd9000_gpio_direction_output ;
data - > gpio . get = ucd9000_gpio_get ;
data - > gpio . set = ucd9000_gpio_set ;
data - > gpio . can_sleep = true ;
data - > gpio . base = - 1 ;
data - > gpio . parent = & client - > dev ;
rc = devm_gpiochip_add_data ( & client - > dev , & data - > gpio , client ) ;
if ( rc )
dev_warn ( & client - > dev , " Could not add gpiochip: %d \n " , rc ) ;
}
# else
static void ucd9000_probe_gpio ( struct i2c_client * client ,
const struct i2c_device_id * mid ,
struct ucd9000_data * data )
{
}
# endif /* CONFIG_GPIOLIB */
2011-03-08 22:01:39 -08:00
static int ucd9000_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
u8 block_buffer [ I2C_SMBUS_BLOCK_MAX + 1 ] ;
struct ucd9000_data * data ;
struct pmbus_driver_info * info ;
const struct i2c_device_id * mid ;
2017-02-24 10:13:07 -03:00
enum chips chip ;
2011-03-08 22:01:39 -08:00
int i , ret ;
if ( ! i2c_check_functionality ( client - > adapter ,
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA ) )
return - ENODEV ;
ret = i2c_smbus_read_block_data ( client , UCD9000_DEVICE_ID ,
block_buffer ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " Failed to read device ID \n " ) ;
return ret ;
}
block_buffer [ ret ] = ' \0 ' ;
dev_info ( & client - > dev , " Device ID %s \n " , block_buffer ) ;
2011-08-31 11:53:41 -04:00
for ( mid = ucd9000_id ; mid - > name [ 0 ] ; mid + + ) {
2011-03-08 22:01:39 -08:00
if ( ! strncasecmp ( mid - > name , block_buffer , strlen ( mid - > name ) ) )
break ;
}
2011-08-31 11:53:41 -04:00
if ( ! mid - > name [ 0 ] ) {
2011-03-08 22:01:39 -08:00
dev_err ( & client - > dev , " Unsupported device \n " ) ;
return - ENODEV ;
}
2017-02-24 10:13:07 -03:00
if ( client - > dev . of_node )
chip = ( enum chips ) of_device_get_match_data ( & client - > dev ) ;
else
chip = id - > driver_data ;
if ( chip ! = ucd9000 & & chip ! = mid - > driver_data )
2011-03-08 22:01:39 -08:00
dev_notice ( & client - > dev ,
" Device mismatch: Configured %s, detected %s \n " ,
id - > name , mid - > name ) ;
2012-02-22 08:56:43 -08:00
data = devm_kzalloc ( & client - > dev , sizeof ( struct ucd9000_data ) ,
GFP_KERNEL ) ;
2011-03-08 22:01:39 -08:00
if ( ! data )
return - ENOMEM ;
info = & data - > info ;
ret = i2c_smbus_read_byte_data ( client , UCD9000_NUM_PAGES ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev ,
" Failed to read number of active pages \n " ) ;
2012-02-22 08:56:43 -08:00
return ret ;
2011-03-08 22:01:39 -08:00
}
info - > pages = ret ;
if ( ! info - > pages ) {
dev_err ( & client - > dev , " No pages configured \n " ) ;
2012-02-22 08:56:43 -08:00
return - ENODEV ;
2011-03-08 22:01:39 -08:00
}
/* The internal temperature sensor is always active */
info - > func [ 0 ] = PMBUS_HAVE_TEMP ;
/* Everything else is configurable */
ret = i2c_smbus_read_block_data ( client , UCD9000_MONITOR_CONFIG ,
block_buffer ) ;
if ( ret < = 0 ) {
dev_err ( & client - > dev , " Failed to read configuration data \n " ) ;
2012-02-22 08:56:43 -08:00
return - ENODEV ;
2011-03-08 22:01:39 -08:00
}
for ( i = 0 ; i < ret ; i + + ) {
int page = UCD9000_MON_PAGE ( block_buffer [ i ] ) ;
if ( page > = info - > pages )
continue ;
switch ( UCD9000_MON_TYPE ( block_buffer [ i ] ) ) {
case UCD9000_MON_VOLTAGE :
case UCD9000_MON_VOLTAGE_HW :
info - > func [ page ] | = PMBUS_HAVE_VOUT
| PMBUS_HAVE_STATUS_VOUT ;
break ;
case UCD9000_MON_TEMPERATURE :
info - > func [ page ] | = PMBUS_HAVE_TEMP2
| PMBUS_HAVE_STATUS_TEMP ;
break ;
case UCD9000_MON_CURRENT :
info - > func [ page ] | = PMBUS_HAVE_IOUT
| PMBUS_HAVE_STATUS_IOUT ;
break ;
default :
break ;
}
}
/* Fan configuration */
if ( mid - > driver_data = = ucd90124 ) {
for ( i = 0 ; i < UCD9000_NUM_FAN ; i + + ) {
i2c_smbus_write_byte_data ( client ,
UCD9000_FAN_CONFIG_INDEX , i ) ;
ret = i2c_smbus_read_block_data ( client ,
UCD9000_FAN_CONFIG ,
data - > fan_data [ i ] ) ;
if ( ret < 0 )
2012-02-22 08:56:43 -08:00
return ret ;
2011-03-08 22:01:39 -08:00
}
i2c_smbus_write_byte_data ( client , UCD9000_FAN_CONFIG_INDEX , 0 ) ;
info - > read_byte_data = ucd9000_read_byte_data ;
info - > func [ 0 ] | = PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12
| PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34 ;
}
2018-03-16 16:22:05 -05:00
ucd9000_probe_gpio ( client , mid , data ) ;
2012-02-22 08:56:43 -08:00
return pmbus_do_probe ( client , mid , info ) ;
2011-03-08 22:01:39 -08:00
}
/* This is the driver that will be inserted */
static struct i2c_driver ucd9000_driver = {
. driver = {
. name = " ucd9000 " ,
2017-02-24 10:13:07 -03:00
. of_match_table = of_match_ptr ( ucd9000_of_match ) ,
2011-03-08 22:01:39 -08:00
} ,
. probe = ucd9000_probe ,
2012-02-22 08:56:44 -08:00
. remove = pmbus_do_remove ,
2011-03-08 22:01:39 -08:00
. id_table = ucd9000_id ,
} ;
2012-01-20 15:38:18 +08:00
module_i2c_driver ( ucd9000_driver ) ;
2011-03-08 22:01:39 -08:00
MODULE_AUTHOR ( " Guenter Roeck " ) ;
MODULE_DESCRIPTION ( " PMBus driver for TI UCD90xxx " ) ;
MODULE_LICENSE ( " GPL " ) ;