Commit 173442f2 authored by Tomoya MORINAGA's avatar Tomoya MORINAGA Committed by Ben Dooks

i2c-eg20t: support new device OKI SEMICONDUCTOR ML7213 IOH

Support new device OKI SEMICONDUCTOR ML7213 IOH.
The ML7213 which is for IVI(In-Vehicle Infotainment) is a companion
chip for the Atom E6xx series and compatible with the Intel EG20T
PCH.
Signed-off-by: default avatarTomoya MORINAGA <tomoya-linux@dsn.okisemi.com>
Signed-off-by: default avatarBen Dooks <ben-linux@fluff.org>
parent 493f3358
...@@ -639,13 +639,16 @@ config I2C_XILINX ...@@ -639,13 +639,16 @@ config I2C_XILINX
will be called xilinx_i2c. will be called xilinx_i2c.
config I2C_EG20T config I2C_EG20T
tristate "PCH I2C of Intel EG20T" tristate "Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH"
depends on PCI depends on PCI
help help
This driver is for PCH(Platform controller Hub) I2C of EG20T which This driver is for PCH(Platform controller Hub) I2C of EG20T which
is an IOH(Input/Output Hub) for x86 embedded processor. is an IOH(Input/Output Hub) for x86 embedded processor.
This driver can access PCH I2C bus device. This driver can access PCH I2C bus device.
This driver also supports the ML7213, a companion chip for the
Atom E6xx series and compatible with the Intel EG20T PCH.
comment "External I2C/SMBus adapter drivers" comment "External I2C/SMBus adapter drivers"
config I2C_PARPORT config I2C_PARPORT
......
...@@ -131,6 +131,13 @@ ...@@ -131,6 +131,13 @@
#define pch_pci_dbg(pdev, fmt, arg...) \ #define pch_pci_dbg(pdev, fmt, arg...) \
dev_dbg(&pdev->dev, "%s :" fmt, __func__, ##arg) dev_dbg(&pdev->dev, "%s :" fmt, __func__, ##arg)
/*
Set the number of I2C instance max
Intel EG20T PCH : 1ch
OKI SEMICONDUCTOR ML7213 IOH : 2ch
*/
#define PCH_I2C_MAX_DEV 2
/** /**
* struct i2c_algo_pch_data - for I2C driver functionalities * struct i2c_algo_pch_data - for I2C driver functionalities
* @pch_adapter: stores the reference to i2c_adapter structure * @pch_adapter: stores the reference to i2c_adapter structure
...@@ -155,12 +162,14 @@ struct i2c_algo_pch_data { ...@@ -155,12 +162,14 @@ struct i2c_algo_pch_data {
* @pch_data: stores a list of i2c_algo_pch_data * @pch_data: stores a list of i2c_algo_pch_data
* @pch_i2c_suspended: specifies whether the system is suspended or not * @pch_i2c_suspended: specifies whether the system is suspended or not
* perhaps with more lines and words. * perhaps with more lines and words.
* @ch_num: specifies the number of i2c instance
* *
* pch_data has as many elements as maximum I2C channels * pch_data has as many elements as maximum I2C channels
*/ */
struct adapter_info { struct adapter_info {
struct i2c_algo_pch_data pch_data; struct i2c_algo_pch_data pch_data[PCH_I2C_MAX_DEV];
bool pch_i2c_suspended; bool pch_i2c_suspended;
int ch_num;
}; };
...@@ -169,8 +178,13 @@ static int pch_clk = 50000; /* specifies I2C clock speed in KHz */ ...@@ -169,8 +178,13 @@ static int pch_clk = 50000; /* specifies I2C clock speed in KHz */
static wait_queue_head_t pch_event; static wait_queue_head_t pch_event;
static DEFINE_MUTEX(pch_mutex); static DEFINE_MUTEX(pch_mutex);
/* Definition for ML7213 by OKI SEMICONDUCTOR */
#define PCI_VENDOR_ID_ROHM 0x10DB
#define PCI_DEVICE_ID_ML7213_I2C 0x802D
static struct pci_device_id __devinitdata pch_pcidev_id[] = { static struct pci_device_id __devinitdata pch_pcidev_id[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH_I2C)}, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_PCH_I2C), 1, },
{ PCI_VDEVICE(ROHM, PCI_DEVICE_ID_ML7213_I2C), 2, },
{0,} {0,}
}; };
...@@ -211,8 +225,7 @@ static void pch_i2c_init(struct i2c_algo_pch_data *adap) ...@@ -211,8 +225,7 @@ static void pch_i2c_init(struct i2c_algo_pch_data *adap)
/* Initialize I2C registers */ /* Initialize I2C registers */
iowrite32(0x21, p + PCH_I2CNF); iowrite32(0x21, p + PCH_I2CNF);
pch_setbit(adap->pch_base_address, PCH_I2CCTL, pch_setbit(adap->pch_base_address, PCH_I2CCTL, PCH_I2CCTL_I2CMEN);
PCH_I2CCTL_I2CMEN);
if (pch_i2c_speed != 400) if (pch_i2c_speed != 400)
pch_i2c_speed = 100; pch_i2c_speed = 100;
...@@ -474,7 +487,7 @@ static void pch_i2c_sendnack(struct i2c_algo_pch_data *adap) ...@@ -474,7 +487,7 @@ static void pch_i2c_sendnack(struct i2c_algo_pch_data *adap)
* @last: specifies whether last message or not. * @last: specifies whether last message or not.
* @first: specifies whether first message or not. * @first: specifies whether first message or not.
*/ */
s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, static s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
u32 last, u32 first) u32 last, u32 first)
{ {
struct i2c_algo_pch_data *adap = i2c_adap->algo_data; struct i2c_algo_pch_data *adap = i2c_adap->algo_data;
...@@ -568,10 +581,10 @@ s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, ...@@ -568,10 +581,10 @@ s32 pch_i2c_readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
} }
/** /**
* pch_i2c_cb_ch0() - Interrupt handler Call back function * pch_i2c_cb() - Interrupt handler Call back function
* @adap: Pointer to struct i2c_algo_pch_data. * @adap: Pointer to struct i2c_algo_pch_data.
*/ */
static void pch_i2c_cb_ch0(struct i2c_algo_pch_data *adap) static void pch_i2c_cb(struct i2c_algo_pch_data *adap)
{ {
u32 sts; u32 sts;
void __iomem *p = adap->pch_base_address; void __iomem *p = adap->pch_base_address;
...@@ -599,24 +612,30 @@ static void pch_i2c_cb_ch0(struct i2c_algo_pch_data *adap) ...@@ -599,24 +612,30 @@ static void pch_i2c_cb_ch0(struct i2c_algo_pch_data *adap)
*/ */
static irqreturn_t pch_i2c_handler(int irq, void *pData) static irqreturn_t pch_i2c_handler(int irq, void *pData)
{ {
s32 reg_val; u32 reg_val;
int flag;
struct i2c_algo_pch_data *adap_data = (struct i2c_algo_pch_data *)pData; int i;
void __iomem *p = adap_data->pch_base_address; struct adapter_info *adap_info = pData;
u32 mode = ioread32(p + PCH_I2CMOD) & (BUFFER_MODE | EEPROM_SR_MODE); void __iomem *p;
u32 mode;
for (i = 0, flag = 0; i < adap_info->ch_num; i++) {
p = adap_info->pch_data[i].pch_base_address;
mode = ioread32(p + PCH_I2CMOD);
mode &= BUFFER_MODE | EEPROM_SR_MODE;
if (mode != NORMAL_MODE) { if (mode != NORMAL_MODE) {
pch_err(adap_data, "I2C mode is not supported\n"); pch_err(adap_info->pch_data,
return IRQ_NONE; "I2C-%d mode(%d) is not supported\n", mode, i);
continue;
} }
reg_val = ioread32(p + PCH_I2CSR); reg_val = ioread32(p + PCH_I2CSR);
if (reg_val & (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT)) if (reg_val & (I2CMAL_BIT | I2CMCF_BIT | I2CMIF_BIT)) {
pch_i2c_cb_ch0(adap_data); pch_i2c_cb(&adap_info->pch_data[i]);
else flag = 1;
return IRQ_NONE; }
}
return IRQ_HANDLED; return flag ? IRQ_HANDLED : IRQ_NONE;
} }
/** /**
...@@ -712,8 +731,10 @@ static int __devinit pch_i2c_probe(struct pci_dev *pdev, ...@@ -712,8 +731,10 @@ static int __devinit pch_i2c_probe(struct pci_dev *pdev,
const struct pci_device_id *id) const struct pci_device_id *id)
{ {
void __iomem *base_addr; void __iomem *base_addr;
s32 ret; int ret;
int i, j;
struct adapter_info *adap_info; struct adapter_info *adap_info;
struct i2c_adapter *pch_adap;
pch_pci_dbg(pdev, "Entered.\n"); pch_pci_dbg(pdev, "Entered.\n");
...@@ -743,44 +764,48 @@ static int __devinit pch_i2c_probe(struct pci_dev *pdev, ...@@ -743,44 +764,48 @@ static int __devinit pch_i2c_probe(struct pci_dev *pdev,
goto err_pci_iomap; goto err_pci_iomap;
} }
adap_info->pch_i2c_suspended = false; /* Set the number of I2C channel instance */
adap_info->ch_num = id->driver_data;
adap_info->pch_data.p_adapter_info = adap_info; for (i = 0; i < adap_info->ch_num; i++) {
pch_adap = &adap_info->pch_data[i].pch_adapter;
adap_info->pch_i2c_suspended = false;
adap_info->pch_data.pch_adapter.owner = THIS_MODULE; adap_info->pch_data[i].p_adapter_info = adap_info;
adap_info->pch_data.pch_adapter.class = I2C_CLASS_HWMON;
strcpy(adap_info->pch_data.pch_adapter.name, KBUILD_MODNAME);
adap_info->pch_data.pch_adapter.algo = &pch_algorithm;
adap_info->pch_data.pch_adapter.algo_data =
&adap_info->pch_data;
/* (i * 0x80) + base_addr; */ pch_adap->owner = THIS_MODULE;
adap_info->pch_data.pch_base_address = base_addr; pch_adap->class = I2C_CLASS_HWMON;
strcpy(pch_adap->name, KBUILD_MODNAME);
pch_adap->algo = &pch_algorithm;
pch_adap->algo_data = &adap_info->pch_data[i];
adap_info->pch_data.pch_adapter.dev.parent = &pdev->dev; /* base_addr + offset; */
adap_info->pch_data[i].pch_base_address = base_addr + 0x100 * i;
ret = i2c_add_adapter(&(adap_info->pch_data.pch_adapter)); pch_adap->dev.parent = &pdev->dev;
ret = i2c_add_adapter(pch_adap);
if (ret) { if (ret) {
pch_pci_err(pdev, "i2c_add_adapter FAILED\n"); pch_pci_err(pdev, "i2c_add_adapter[ch:%d] FAILED\n", i);
goto err_i2c_add_adapter; goto err_i2c_add_adapter;
} }
pch_i2c_init(&adap_info->pch_data); pch_i2c_init(&adap_info->pch_data[i]);
}
ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED, ret = request_irq(pdev->irq, pch_i2c_handler, IRQF_SHARED,
KBUILD_MODNAME, &adap_info->pch_data); KBUILD_MODNAME, adap_info);
if (ret) { if (ret) {
pch_pci_err(pdev, "request_irq FAILED\n"); pch_pci_err(pdev, "request_irq FAILED\n");
goto err_request_irq; goto err_i2c_add_adapter;
} }
pci_set_drvdata(pdev, adap_info); pci_set_drvdata(pdev, adap_info);
pch_pci_dbg(pdev, "returns %d.\n", ret); pch_pci_dbg(pdev, "returns %d.\n", ret);
return 0; return 0;
err_request_irq:
i2c_del_adapter(&(adap_info->pch_data.pch_adapter));
err_i2c_add_adapter: err_i2c_add_adapter:
for (j = 0; j < i; j++)
i2c_del_adapter(&adap_info->pch_data[j].pch_adapter);
pci_iounmap(pdev, base_addr); pci_iounmap(pdev, base_addr);
err_pci_iomap: err_pci_iomap:
pci_release_regions(pdev); pci_release_regions(pdev);
...@@ -793,17 +818,22 @@ static int __devinit pch_i2c_probe(struct pci_dev *pdev, ...@@ -793,17 +818,22 @@ static int __devinit pch_i2c_probe(struct pci_dev *pdev,
static void __devexit pch_i2c_remove(struct pci_dev *pdev) static void __devexit pch_i2c_remove(struct pci_dev *pdev)
{ {
int i;
struct adapter_info *adap_info = pci_get_drvdata(pdev); struct adapter_info *adap_info = pci_get_drvdata(pdev);
pch_i2c_disbl_int(&adap_info->pch_data); free_irq(pdev->irq, adap_info);
free_irq(pdev->irq, &adap_info->pch_data);
i2c_del_adapter(&(adap_info->pch_data.pch_adapter));
if (adap_info->pch_data.pch_base_address) { for (i = 0; i < adap_info->ch_num; i++) {
pci_iounmap(pdev, adap_info->pch_data.pch_base_address); pch_i2c_disbl_int(&adap_info->pch_data[i]);
adap_info->pch_data.pch_base_address = 0; i2c_del_adapter(&adap_info->pch_data[i].pch_adapter);
} }
if (adap_info->pch_data[0].pch_base_address)
pci_iounmap(pdev, adap_info->pch_data[0].pch_base_address);
for (i = 0; i < adap_info->ch_num; i++)
adap_info->pch_data[i].pch_base_address = 0;
pci_set_drvdata(pdev, NULL); pci_set_drvdata(pdev, NULL);
pci_release_regions(pdev); pci_release_regions(pdev);
...@@ -816,17 +846,22 @@ static void __devexit pch_i2c_remove(struct pci_dev *pdev) ...@@ -816,17 +846,22 @@ static void __devexit pch_i2c_remove(struct pci_dev *pdev)
static int pch_i2c_suspend(struct pci_dev *pdev, pm_message_t state) static int pch_i2c_suspend(struct pci_dev *pdev, pm_message_t state)
{ {
int ret; int ret;
int i;
struct adapter_info *adap_info = pci_get_drvdata(pdev); struct adapter_info *adap_info = pci_get_drvdata(pdev);
void __iomem *p = adap_info->pch_data.pch_base_address; void __iomem *p = adap_info->pch_data[0].pch_base_address;
adap_info->pch_i2c_suspended = true; adap_info->pch_i2c_suspended = true;
while ((adap_info->pch_data.pch_i2c_xfer_in_progress)) { for (i = 0; i < adap_info->ch_num; i++) {
while ((adap_info->pch_data[i].pch_i2c_xfer_in_progress)) {
/* Wait until all channel transfers are completed */ /* Wait until all channel transfers are completed */
msleep(20); msleep(20);
} }
}
/* Disable the i2c interrupts */ /* Disable the i2c interrupts */
pch_i2c_disbl_int(&adap_info->pch_data); for (i = 0; i < adap_info->ch_num; i++)
pch_i2c_disbl_int(&adap_info->pch_data[i]);
pch_pci_dbg(pdev, "I2CSR = %x I2CBUFSTA = %x I2CESRSTA = %x " pch_pci_dbg(pdev, "I2CSR = %x I2CBUFSTA = %x I2CESRSTA = %x "
"invoked function pch_i2c_disbl_int successfully\n", "invoked function pch_i2c_disbl_int successfully\n",
...@@ -849,6 +884,7 @@ static int pch_i2c_suspend(struct pci_dev *pdev, pm_message_t state) ...@@ -849,6 +884,7 @@ static int pch_i2c_suspend(struct pci_dev *pdev, pm_message_t state)
static int pch_i2c_resume(struct pci_dev *pdev) static int pch_i2c_resume(struct pci_dev *pdev)
{ {
int i;
struct adapter_info *adap_info = pci_get_drvdata(pdev); struct adapter_info *adap_info = pci_get_drvdata(pdev);
pci_set_power_state(pdev, PCI_D0); pci_set_power_state(pdev, PCI_D0);
...@@ -861,7 +897,8 @@ static int pch_i2c_resume(struct pci_dev *pdev) ...@@ -861,7 +897,8 @@ static int pch_i2c_resume(struct pci_dev *pdev)
pci_enable_wake(pdev, PCI_D3hot, 0); pci_enable_wake(pdev, PCI_D3hot, 0);
pch_i2c_init(&adap_info->pch_data); for (i = 0; i < adap_info->ch_num; i++)
pch_i2c_init(&adap_info->pch_data[i]);
adap_info->pch_i2c_suspended = false; adap_info->pch_i2c_suspended = false;
...@@ -893,7 +930,7 @@ static void __exit pch_pci_exit(void) ...@@ -893,7 +930,7 @@ static void __exit pch_pci_exit(void)
} }
module_exit(pch_pci_exit); module_exit(pch_pci_exit);
MODULE_DESCRIPTION("PCH I2C PCI Driver"); MODULE_DESCRIPTION("Intel EG20T PCH/OKI SEMICONDUCTOR ML7213 IOH I2C Driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tomoya MORINAGA. <tomoya-linux@dsn.okisemi.com>"); MODULE_AUTHOR("Tomoya MORINAGA. <tomoya-linux@dsn.okisemi.com>");
module_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR)); module_param(pch_i2c_speed, int, (S_IRUSR | S_IWUSR));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment