2015-02-02 05:52:51 +03:00
/*
* Intel Quark MFD PCI driver for I2C & GPIO
*
* Copyright ( c ) 2014 Intel Corporation .
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
*
* This program is distributed in the hope 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 .
*
* Intel Quark PCI device for I2C and GPIO controller sharing the same
* PCI function . This PCI driver will split the 2 devices into their
* respective drivers .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/mfd/core.h>
# include <linux/clkdev.h>
# include <linux/clk-provider.h>
# include <linux/dmi.h>
# include <linux/platform_data/gpio-dwapb.h>
# include <linux/platform_data/i2c-designware.h>
/* PCI BAR for register base address */
# define MFD_I2C_BAR 0
# define MFD_GPIO_BAR 1
2015-10-23 12:16:43 +03:00
/* ACPI _ADR value to match the child node */
# define MFD_ACPI_MATCH_GPIO 0ULL
# define MFD_ACPI_MATCH_I2C 1ULL
2015-02-02 05:52:51 +03:00
/* The base GPIO number under GPIOLIB framework */
# define INTEL_QUARK_MFD_GPIO_BASE 8
/* The default number of South-Cluster GPIO on Quark. */
# define INTEL_QUARK_MFD_NGPIO 8
/* The DesignWare GPIO ports on Quark. */
# define INTEL_QUARK_GPIO_NPORTS 1
# define INTEL_QUARK_IORES_MEM 0
# define INTEL_QUARK_IORES_IRQ 1
# define INTEL_QUARK_I2C_CONTROLLER_CLK "i2c_designware.0"
/* The Quark I2C controller source clock */
# define INTEL_QUARK_I2C_CLK_HZ 33000000
struct intel_quark_mfd {
2016-02-19 11:42:11 +03:00
struct device * dev ;
2015-02-02 05:52:51 +03:00
struct clk * i2c_clk ;
struct clk_lookup * i2c_clk_lookup ;
} ;
2017-05-30 11:17:40 +03:00
static const struct dmi_system_id dmi_platform_info [ ] = {
2015-02-02 05:52:51 +03:00
{
2017-05-30 11:17:40 +03:00
. matches = {
DMI_EXACT_MATCH ( DMI_BOARD_NAME , " Galileo " ) ,
} ,
. driver_data = ( void * ) 100000 ,
2015-02-02 05:52:51 +03:00
} ,
{
2017-05-30 11:17:40 +03:00
. matches = {
DMI_EXACT_MATCH ( DMI_BOARD_NAME , " GalileoGen2 " ) ,
} ,
. driver_data = ( void * ) 400000 ,
2015-02-02 05:52:51 +03:00
} ,
2017-05-30 11:17:41 +03:00
{
. matches = {
DMI_EXACT_MATCH ( DMI_BOARD_NAME , " SIMATIC IOT2000 " ) ,
DMI_EXACT_MATCH ( DMI_BOARD_ASSET_TAG ,
" 6ES7647-0AA00-0YA2 " ) ,
} ,
. driver_data = ( void * ) 400000 ,
} ,
{
. matches = {
DMI_EXACT_MATCH ( DMI_BOARD_NAME , " SIMATIC IOT2000 " ) ,
DMI_EXACT_MATCH ( DMI_BOARD_ASSET_TAG ,
" 6ES7647-0AA00-1YA2 " ) ,
} ,
. driver_data = ( void * ) 400000 ,
} ,
2015-03-25 21:03:55 +03:00
{ }
2015-02-02 05:52:51 +03:00
} ;
static struct resource intel_quark_i2c_res [ ] = {
[ INTEL_QUARK_IORES_MEM ] = {
. flags = IORESOURCE_MEM ,
} ,
[ INTEL_QUARK_IORES_IRQ ] = {
. flags = IORESOURCE_IRQ ,
} ,
} ;
2015-10-23 12:16:43 +03:00
static struct mfd_cell_acpi_match intel_quark_acpi_match_i2c = {
. adr = MFD_ACPI_MATCH_I2C ,
} ;
2015-02-02 05:52:51 +03:00
static struct resource intel_quark_gpio_res [ ] = {
[ INTEL_QUARK_IORES_MEM ] = {
. flags = IORESOURCE_MEM ,
} ,
} ;
2015-10-23 12:16:43 +03:00
static struct mfd_cell_acpi_match intel_quark_acpi_match_gpio = {
. adr = MFD_ACPI_MATCH_GPIO ,
} ;
2015-02-02 05:52:51 +03:00
static struct mfd_cell intel_quark_mfd_cells [ ] = {
{
. id = MFD_GPIO_BAR ,
. name = " gpio-dwapb " ,
2015-10-23 12:16:43 +03:00
. acpi_match = & intel_quark_acpi_match_gpio ,
2015-02-02 05:52:51 +03:00
. num_resources = ARRAY_SIZE ( intel_quark_gpio_res ) ,
. resources = intel_quark_gpio_res ,
. ignore_resource_conflicts = true ,
} ,
2015-10-23 12:16:42 +03:00
{
. id = MFD_I2C_BAR ,
. name = " i2c_designware " ,
2015-10-23 12:16:43 +03:00
. acpi_match = & intel_quark_acpi_match_i2c ,
2015-10-23 12:16:42 +03:00
. num_resources = ARRAY_SIZE ( intel_quark_i2c_res ) ,
. resources = intel_quark_i2c_res ,
. ignore_resource_conflicts = true ,
} ,
2015-02-02 05:52:51 +03:00
} ;
static const struct pci_device_id intel_quark_mfd_ids [ ] = {
{ PCI_VDEVICE ( INTEL , 0x0934 ) , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( pci , intel_quark_mfd_ids ) ;
2016-02-19 11:42:11 +03:00
static int intel_quark_register_i2c_clk ( struct device * dev )
2015-02-02 05:52:51 +03:00
{
2016-02-19 11:42:11 +03:00
struct intel_quark_mfd * quark_mfd = dev_get_drvdata ( dev ) ;
2015-02-02 05:52:51 +03:00
struct clk * i2c_clk ;
2016-02-19 11:42:11 +03:00
i2c_clk = clk_register_fixed_rate ( dev ,
2015-02-02 05:52:51 +03:00
INTEL_QUARK_I2C_CONTROLLER_CLK , NULL ,
2016-04-20 04:19:20 +03:00
0 , INTEL_QUARK_I2C_CLK_HZ ) ;
2016-02-09 04:45:28 +03:00
if ( IS_ERR ( i2c_clk ) )
return PTR_ERR ( i2c_clk ) ;
2015-02-02 05:52:51 +03:00
quark_mfd - > i2c_clk = i2c_clk ;
2016-02-09 04:45:28 +03:00
quark_mfd - > i2c_clk_lookup = clkdev_create ( i2c_clk , NULL ,
INTEL_QUARK_I2C_CONTROLLER_CLK ) ;
2015-02-02 05:52:51 +03:00
2016-02-09 04:45:28 +03:00
if ( ! quark_mfd - > i2c_clk_lookup ) {
2016-02-19 11:42:10 +03:00
clk_unregister ( quark_mfd - > i2c_clk ) ;
2016-02-19 11:42:11 +03:00
dev_err ( dev , " Fixed clk register failed \n " ) ;
2016-02-09 04:45:28 +03:00
return - ENOMEM ;
}
2015-02-02 05:52:51 +03:00
2016-02-09 04:45:28 +03:00
return 0 ;
2015-02-02 05:52:51 +03:00
}
2016-02-19 11:42:11 +03:00
static void intel_quark_unregister_i2c_clk ( struct device * dev )
2015-02-02 05:52:51 +03:00
{
2016-02-19 11:42:11 +03:00
struct intel_quark_mfd * quark_mfd = dev_get_drvdata ( dev ) ;
2015-02-02 05:52:51 +03:00
2016-02-19 11:42:10 +03:00
if ( ! quark_mfd - > i2c_clk_lookup )
2015-02-02 05:52:51 +03:00
return ;
clkdev_drop ( quark_mfd - > i2c_clk_lookup ) ;
clk_unregister ( quark_mfd - > i2c_clk ) ;
}
static int intel_quark_i2c_setup ( struct pci_dev * pdev , struct mfd_cell * cell )
{
2017-05-30 11:17:40 +03:00
const struct dmi_system_id * dmi_id ;
2015-02-02 05:52:51 +03:00
struct dw_i2c_platform_data * pdata ;
struct resource * res = ( struct resource * ) cell - > resources ;
struct device * dev = & pdev - > dev ;
res [ INTEL_QUARK_IORES_MEM ] . start =
pci_resource_start ( pdev , MFD_I2C_BAR ) ;
res [ INTEL_QUARK_IORES_MEM ] . end =
pci_resource_end ( pdev , MFD_I2C_BAR ) ;
res [ INTEL_QUARK_IORES_IRQ ] . start = pdev - > irq ;
res [ INTEL_QUARK_IORES_IRQ ] . end = pdev - > irq ;
pdata = devm_kzalloc ( dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
2015-03-25 21:03:55 +03:00
/* Normal mode by default */
pdata - > i2c_scl_freq = 100000 ;
2017-05-30 11:17:40 +03:00
dmi_id = dmi_first_match ( dmi_platform_info ) ;
if ( dmi_id )
pdata - > i2c_scl_freq = ( uintptr_t ) dmi_id - > driver_data ;
2015-02-02 05:52:51 +03:00
cell - > platform_data = pdata ;
cell - > pdata_size = sizeof ( * pdata ) ;
return 0 ;
}
static int intel_quark_gpio_setup ( struct pci_dev * pdev , struct mfd_cell * cell )
{
struct dwapb_platform_data * pdata ;
struct resource * res = ( struct resource * ) cell - > resources ;
struct device * dev = & pdev - > dev ;
res [ INTEL_QUARK_IORES_MEM ] . start =
pci_resource_start ( pdev , MFD_GPIO_BAR ) ;
res [ INTEL_QUARK_IORES_MEM ] . end =
pci_resource_end ( pdev , MFD_GPIO_BAR ) ;
pdata = devm_kzalloc ( dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return - ENOMEM ;
/* For intel quark x1000, it has only one port: portA */
pdata - > nports = INTEL_QUARK_GPIO_NPORTS ;
pdata - > properties = devm_kcalloc ( dev , pdata - > nports ,
sizeof ( * pdata - > properties ) ,
GFP_KERNEL ) ;
if ( ! pdata - > properties )
return - ENOMEM ;
/* Set the properties for portA */
2016-04-28 12:32:02 +03:00
pdata - > properties - > fwnode = NULL ;
2015-02-02 05:52:51 +03:00
pdata - > properties - > idx = 0 ;
pdata - > properties - > ngpio = INTEL_QUARK_MFD_NGPIO ;
pdata - > properties - > gpio_base = INTEL_QUARK_MFD_GPIO_BASE ;
pdata - > properties - > irq = pdev - > irq ;
pdata - > properties - > irq_shared = true ;
cell - > platform_data = pdata ;
cell - > pdata_size = sizeof ( * pdata ) ;
return 0 ;
}
static int intel_quark_mfd_probe ( struct pci_dev * pdev ,
const struct pci_device_id * id )
{
struct intel_quark_mfd * quark_mfd ;
int ret ;
ret = pcim_enable_device ( pdev ) ;
if ( ret )
return ret ;
quark_mfd = devm_kzalloc ( & pdev - > dev , sizeof ( * quark_mfd ) , GFP_KERNEL ) ;
if ( ! quark_mfd )
return - ENOMEM ;
2016-02-19 11:42:11 +03:00
quark_mfd - > dev = & pdev - > dev ;
2016-02-19 11:42:10 +03:00
dev_set_drvdata ( & pdev - > dev , quark_mfd ) ;
2015-02-02 05:52:51 +03:00
2016-02-19 11:42:11 +03:00
ret = intel_quark_register_i2c_clk ( & pdev - > dev ) ;
2015-02-02 05:52:51 +03:00
if ( ret )
return ret ;
2015-10-23 12:16:42 +03:00
ret = intel_quark_i2c_setup ( pdev , & intel_quark_mfd_cells [ 1 ] ) ;
2015-02-02 05:52:51 +03:00
if ( ret )
2016-02-19 11:42:10 +03:00
goto err_unregister_i2c_clk ;
2015-02-02 05:52:51 +03:00
2015-10-23 12:16:42 +03:00
ret = intel_quark_gpio_setup ( pdev , & intel_quark_mfd_cells [ 0 ] ) ;
2015-02-02 05:52:51 +03:00
if ( ret )
2016-02-19 11:42:10 +03:00
goto err_unregister_i2c_clk ;
2015-02-02 05:52:51 +03:00
2016-02-19 11:42:10 +03:00
ret = mfd_add_devices ( & pdev - > dev , 0 , intel_quark_mfd_cells ,
ARRAY_SIZE ( intel_quark_mfd_cells ) , NULL , 0 ,
NULL ) ;
if ( ret )
goto err_unregister_i2c_clk ;
return 0 ;
2015-02-02 05:52:51 +03:00
2016-02-19 11:42:10 +03:00
err_unregister_i2c_clk :
2016-02-19 11:42:11 +03:00
intel_quark_unregister_i2c_clk ( & pdev - > dev ) ;
2016-02-19 11:42:10 +03:00
return ret ;
2015-02-02 05:52:51 +03:00
}
static void intel_quark_mfd_remove ( struct pci_dev * pdev )
{
2016-02-19 11:42:11 +03:00
intel_quark_unregister_i2c_clk ( & pdev - > dev ) ;
2015-02-02 05:52:51 +03:00
mfd_remove_devices ( & pdev - > dev ) ;
}
static struct pci_driver intel_quark_mfd_driver = {
. name = " intel_quark_mfd_i2c_gpio " ,
. id_table = intel_quark_mfd_ids ,
. probe = intel_quark_mfd_probe ,
. remove = intel_quark_mfd_remove ,
} ;
module_pci_driver ( intel_quark_mfd_driver ) ;
MODULE_AUTHOR ( " Raymond Tan <raymond.tan@intel.com> " ) ;
MODULE_DESCRIPTION ( " Intel Quark MFD PCI driver for I2C & GPIO " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;