1094 lines · 31.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * IBM Hot Plug Controller Driver * * Written By: Jyoti Shah, IBM Corporation * * Copyright (C) 2001-2003 IBM Corp. * * All rights reserved. * * Send feedback to <gregkh@us.ibm.com> *                  <jshah@us.ibm.com> * */ #include <linux/wait.h>#include <linux/time.h>#include <linux/completion.h>#include <linux/delay.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/init.h>#include <linux/mutex.h>#include <linux/sched.h>#include <linux/kthread.h>#include "ibmphp.h" static int to_debug = 0;#define debug_polling(fmt, arg...)	do { if (to_debug) debug(fmt, arg); } while (0) //----------------------------------------------------------------------------// timeout values//----------------------------------------------------------------------------#define CMD_COMPLETE_TOUT_SEC	60	// give HPC 60 sec to finish cmd#define HPC_CTLR_WORKING_TOUT	60	// give HPC 60 sec to finish cmd#define HPC_GETACCESS_TIMEOUT	60	// seconds#define POLL_INTERVAL_SEC	2	// poll HPC every 2 seconds#define POLL_LATCH_CNT		5	// poll latch 5 times, then poll slots //----------------------------------------------------------------------------// Winnipeg Architected Register Offsets//----------------------------------------------------------------------------#define WPG_I2CMBUFL_OFFSET	0x08	// I2C Message Buffer Low#define WPG_I2CMOSUP_OFFSET	0x10	// I2C Master Operation Setup Reg#define WPG_I2CMCNTL_OFFSET	0x20	// I2C Master Control Register#define WPG_I2CPARM_OFFSET	0x40	// I2C Parameter Register#define WPG_I2CSTAT_OFFSET	0x70	// I2C Status Register //----------------------------------------------------------------------------// Winnipeg Store Type commands (Add this commands to the register offset)//----------------------------------------------------------------------------#define WPG_I2C_AND		0x1000	// I2C AND operation#define WPG_I2C_OR		0x2000	// I2C OR operation //----------------------------------------------------------------------------// Command set for I2C Master Operation Setup Register//----------------------------------------------------------------------------#define WPG_READATADDR_MASK	0x00010000	// read,bytes,I2C shifted,index#define WPG_WRITEATADDR_MASK	0x40010000	// write,bytes,I2C shifted,index#define WPG_READDIRECT_MASK	0x10010000#define WPG_WRITEDIRECT_MASK	0x60010000  //----------------------------------------------------------------------------// bit masks for I2C Master Control Register//----------------------------------------------------------------------------#define WPG_I2CMCNTL_STARTOP_MASK	0x00000002	// Start the Operation //----------------------------------------------------------------------------////----------------------------------------------------------------------------#define WPG_I2C_IOREMAP_SIZE	0x2044	// size of linear address interval //----------------------------------------------------------------------------// command index//----------------------------------------------------------------------------#define WPG_1ST_SLOT_INDEX	0x01	// index - 1st slot for ctlr#define WPG_CTLR_INDEX		0x0F	// index - ctlr#define WPG_1ST_EXTSLOT_INDEX	0x10	// index - 1st ext slot for ctlr#define WPG_1ST_BUS_INDEX	0x1F	// index - 1st bus for ctlr //----------------------------------------------------------------------------// macro utilities//----------------------------------------------------------------------------// if bits 20,22,25,26,27,29,30 are OFF return 1#define HPC_I2CSTATUS_CHECK(s)	((u8)((s & 0x00000A76) ? 0 : 1)) //----------------------------------------------------------------------------// global variables//----------------------------------------------------------------------------static DEFINE_MUTEX(sem_hpcaccess);	// lock access to HPCstatic DEFINE_MUTEX(operations_mutex);	// lock all operations and					// access to data structuresstatic DECLARE_COMPLETION(exit_complete); // make sure polling thread goes awaystatic struct task_struct *ibmphp_poll_thread;//----------------------------------------------------------------------------// local function prototypes//----------------------------------------------------------------------------static u8 i2c_ctrl_read(struct controller *, void __iomem *, u8);static u8 i2c_ctrl_write(struct controller *, void __iomem *, u8, u8);static u8 hpc_writecmdtoindex(u8, u8);static u8 hpc_readcmdtoindex(u8, u8);static void get_hpc_access(void);static void free_hpc_access(void);static int poll_hpc(void *data);static int process_changeinstatus(struct slot *, struct slot *);static int process_changeinlatch(u8, u8, struct controller *);static int hpc_wait_ctlr_notworking(int, struct controller *, void __iomem *, u8 *);//----------------------------------------------------------------------------  /*----------------------------------------------------------------------* Name:    i2c_ctrl_read** Action:  read from HPC over I2C**---------------------------------------------------------------------*/static u8 i2c_ctrl_read(struct controller *ctlr_ptr, void __iomem *WPGBbar, u8 index){	u8 status;	int i;	void __iomem *wpg_addr;	// base addr + offset	unsigned long wpg_data;	// data to/from WPG LOHI format	unsigned long ultemp;	unsigned long data;	// actual data HILO format 	debug_polling("%s - Entry WPGBbar[%p] index[%x]\n", __func__, WPGBbar, index); 	//--------------------------------------------------------------------	// READ - step 1	// read at address, byte length, I2C address (shifted), index	// or read direct, byte length, index	if (ctlr_ptr->ctlr_type == 0x02) {		data = WPG_READATADDR_MASK;		// fill in I2C address		ultemp = (unsigned long)ctlr_ptr->u.wpeg_ctlr.i2c_addr;		ultemp = ultemp >> 1;		data |= (ultemp << 8); 		// fill in index		data |= (unsigned long)index;	} else if (ctlr_ptr->ctlr_type == 0x04) {		data = WPG_READDIRECT_MASK; 		// fill in index		ultemp = (unsigned long)index;		ultemp = ultemp << 8;		data |= ultemp;	} else {		err("this controller type is not supported\n");		return HPC_ERROR;	} 	wpg_data = swab32(data);	// swap data before writing	wpg_addr = WPGBbar + WPG_I2CMOSUP_OFFSET;	writel(wpg_data, wpg_addr); 	//--------------------------------------------------------------------	// READ - step 2 : clear the message buffer	data = 0x00000000;	wpg_data = swab32(data);	wpg_addr = WPGBbar + WPG_I2CMBUFL_OFFSET;	writel(wpg_data, wpg_addr); 	//--------------------------------------------------------------------	// READ - step 3 : issue start operation, I2C master control bit 30:ON	//                 2020 : [20] OR operation at [20] offset 0x20	data = WPG_I2CMCNTL_STARTOP_MASK;	wpg_data = swab32(data);	wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET + WPG_I2C_OR;	writel(wpg_data, wpg_addr); 	//--------------------------------------------------------------------	// READ - step 4 : wait until start operation bit clears	i = CMD_COMPLETE_TOUT_SEC;	while (i) {		msleep(10);		wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET;		wpg_data = readl(wpg_addr);		data = swab32(wpg_data);		if (!(data & WPG_I2CMCNTL_STARTOP_MASK))			break;		i--;	}	if (i == 0) {		debug("%s - Error : WPG timeout\n", __func__);		return HPC_ERROR;	}	//--------------------------------------------------------------------	// READ - step 5 : read I2C status register	i = CMD_COMPLETE_TOUT_SEC;	while (i) {		msleep(10);		wpg_addr = WPGBbar + WPG_I2CSTAT_OFFSET;		wpg_data = readl(wpg_addr);		data = swab32(wpg_data);		if (HPC_I2CSTATUS_CHECK(data))			break;		i--;	}	if (i == 0) {		debug("ctrl_read - Exit Error:I2C timeout\n");		return HPC_ERROR;	} 	//--------------------------------------------------------------------	// READ - step 6 : get DATA	wpg_addr = WPGBbar + WPG_I2CMBUFL_OFFSET;	wpg_data = readl(wpg_addr);	data = swab32(wpg_data); 	status = (u8) data; 	debug_polling("%s - Exit index[%x] status[%x]\n", __func__, index, status); 	return (status);} /*----------------------------------------------------------------------* Name:    i2c_ctrl_write** Action:  write to HPC over I2C** Return   0 or error codes*---------------------------------------------------------------------*/static u8 i2c_ctrl_write(struct controller *ctlr_ptr, void __iomem *WPGBbar, u8 index, u8 cmd){	u8 rc;	void __iomem *wpg_addr;	// base addr + offset	unsigned long wpg_data;	// data to/from WPG LOHI format	unsigned long ultemp;	unsigned long data;	// actual data HILO format	int i; 	debug_polling("%s - Entry WPGBbar[%p] index[%x] cmd[%x]\n", __func__, WPGBbar, index, cmd); 	rc = 0;	//--------------------------------------------------------------------	// WRITE - step 1	// write at address, byte length, I2C address (shifted), index	// or write direct, byte length, index	data = 0x00000000; 	if (ctlr_ptr->ctlr_type == 0x02) {		data = WPG_WRITEATADDR_MASK;		// fill in I2C address		ultemp = (unsigned long)ctlr_ptr->u.wpeg_ctlr.i2c_addr;		ultemp = ultemp >> 1;		data |= (ultemp << 8); 		// fill in index		data |= (unsigned long)index;	} else if (ctlr_ptr->ctlr_type == 0x04) {		data = WPG_WRITEDIRECT_MASK; 		// fill in index		ultemp = (unsigned long)index;		ultemp = ultemp << 8;		data |= ultemp;	} else {		err("this controller type is not supported\n");		return HPC_ERROR;	} 	wpg_data = swab32(data);	// swap data before writing	wpg_addr = WPGBbar + WPG_I2CMOSUP_OFFSET;	writel(wpg_data, wpg_addr); 	//--------------------------------------------------------------------	// WRITE - step 2 : clear the message buffer	data = 0x00000000 | (unsigned long)cmd;	wpg_data = swab32(data);	wpg_addr = WPGBbar + WPG_I2CMBUFL_OFFSET;	writel(wpg_data, wpg_addr); 	//--------------------------------------------------------------------	// WRITE - step 3 : issue start operation,I2C master control bit 30:ON	//                 2020 : [20] OR operation at [20] offset 0x20	data = WPG_I2CMCNTL_STARTOP_MASK;	wpg_data = swab32(data);	wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET + WPG_I2C_OR;	writel(wpg_data, wpg_addr); 	//--------------------------------------------------------------------	// WRITE - step 4 : wait until start operation bit clears	i = CMD_COMPLETE_TOUT_SEC;	while (i) {		msleep(10);		wpg_addr = WPGBbar + WPG_I2CMCNTL_OFFSET;		wpg_data = readl(wpg_addr);		data = swab32(wpg_data);		if (!(data & WPG_I2CMCNTL_STARTOP_MASK))			break;		i--;	}	if (i == 0) {		debug("%s - Exit Error:WPG timeout\n", __func__);		rc = HPC_ERROR;	} 	//--------------------------------------------------------------------	// WRITE - step 5 : read I2C status register	i = CMD_COMPLETE_TOUT_SEC;	while (i) {		msleep(10);		wpg_addr = WPGBbar + WPG_I2CSTAT_OFFSET;		wpg_data = readl(wpg_addr);		data = swab32(wpg_data);		if (HPC_I2CSTATUS_CHECK(data))			break;		i--;	}	if (i == 0) {		debug("ctrl_read - Error : I2C timeout\n");		rc = HPC_ERROR;	} 	debug_polling("%s Exit rc[%x]\n", __func__, rc);	return (rc);} //------------------------------------------------------------//  Read from ISA type HPC//------------------------------------------------------------static u8 isa_ctrl_read(struct controller *ctlr_ptr, u8 offset){	u16 start_address;	u8 data; 	start_address = ctlr_ptr->u.isa_ctlr.io_start;	data = inb(start_address + offset);	return data;} //--------------------------------------------------------------// Write to ISA type HPC//--------------------------------------------------------------static void isa_ctrl_write(struct controller *ctlr_ptr, u8 offset, u8 data){	u16 start_address;	u16 port_address; 	start_address = ctlr_ptr->u.isa_ctlr.io_start;	port_address = start_address + (u16) offset;	outb(data, port_address);} static u8 pci_ctrl_read(struct controller *ctrl, u8 offset){	u8 data = 0x00;	debug("inside pci_ctrl_read\n");	if (ctrl->ctrl_dev)		pci_read_config_byte(ctrl->ctrl_dev, HPC_PCI_OFFSET + offset, &data);	return data;} static u8 pci_ctrl_write(struct controller *ctrl, u8 offset, u8 data){	u8 rc = -ENODEV;	debug("inside pci_ctrl_write\n");	if (ctrl->ctrl_dev) {		pci_write_config_byte(ctrl->ctrl_dev, HPC_PCI_OFFSET + offset, data);		rc = 0;	}	return rc;} static u8 ctrl_read(struct controller *ctlr, void __iomem *base, u8 offset){	u8 rc;	switch (ctlr->ctlr_type) {	case 0:		rc = isa_ctrl_read(ctlr, offset);		break;	case 1:		rc = pci_ctrl_read(ctlr, offset);		break;	case 2:	case 4:		rc = i2c_ctrl_read(ctlr, base, offset);		break;	default:		return -ENODEV;	}	return rc;} static u8 ctrl_write(struct controller *ctlr, void __iomem *base, u8 offset, u8 data){	u8 rc = 0;	switch (ctlr->ctlr_type) {	case 0:		isa_ctrl_write(ctlr, offset, data);		break;	case 1:		rc = pci_ctrl_write(ctlr, offset, data);		break;	case 2:	case 4:		rc = i2c_ctrl_write(ctlr, base, offset, data);		break;	default:		return -ENODEV;	}	return rc;}/*----------------------------------------------------------------------* Name:    hpc_writecmdtoindex()** Action:  convert a write command to proper index within a controller** Return   index, HPC_ERROR*---------------------------------------------------------------------*/static u8 hpc_writecmdtoindex(u8 cmd, u8 index){	u8 rc; 	switch (cmd) {	case HPC_CTLR_ENABLEIRQ:	// 0x00.N.15	case HPC_CTLR_CLEARIRQ:	// 0x06.N.15	case HPC_CTLR_RESET:	// 0x07.N.15	case HPC_CTLR_IRQSTEER:	// 0x08.N.15	case HPC_CTLR_DISABLEIRQ:	// 0x01.N.15	case HPC_ALLSLOT_ON:	// 0x11.N.15	case HPC_ALLSLOT_OFF:	// 0x12.N.15		rc = 0x0F;		break; 	case HPC_SLOT_OFF:	// 0x02.Y.0-14	case HPC_SLOT_ON:	// 0x03.Y.0-14	case HPC_SLOT_ATTNOFF:	// 0x04.N.0-14	case HPC_SLOT_ATTNON:	// 0x05.N.0-14	case HPC_SLOT_BLINKLED:	// 0x13.N.0-14		rc = index;		break; 	case HPC_BUS_33CONVMODE:	case HPC_BUS_66CONVMODE:	case HPC_BUS_66PCIXMODE:	case HPC_BUS_100PCIXMODE:	case HPC_BUS_133PCIXMODE:		rc = index + WPG_1ST_BUS_INDEX - 1;		break; 	default:		err("hpc_writecmdtoindex - Error invalid cmd[%x]\n", cmd);		rc = HPC_ERROR;	} 	return rc;} /*----------------------------------------------------------------------* Name:    hpc_readcmdtoindex()** Action:  convert a read command to proper index within a controller** Return   index, HPC_ERROR*---------------------------------------------------------------------*/static u8 hpc_readcmdtoindex(u8 cmd, u8 index){	u8 rc; 	switch (cmd) {	case READ_CTLRSTATUS:		rc = 0x0F;		break;	case READ_SLOTSTATUS:	case READ_ALLSTAT:		rc = index;		break;	case READ_EXTSLOTSTATUS:		rc = index + WPG_1ST_EXTSLOT_INDEX;		break;	case READ_BUSSTATUS:		rc = index + WPG_1ST_BUS_INDEX - 1;		break;	case READ_SLOTLATCHLOWREG:		rc = 0x28;		break;	case READ_REVLEVEL:		rc = 0x25;		break;	case READ_HPCOPTIONS:		rc = 0x27;		break;	default:		rc = HPC_ERROR;	}	return rc;} /*----------------------------------------------------------------------* Name:    HPCreadslot()** Action:  issue a READ command to HPC** Input:   pslot   - cannot be NULL for READ_ALLSTAT*          pstatus - can be NULL for READ_ALLSTAT*

Lines 1–500 of 1094.